<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발자의 오르막</title>
    <link>https://catchdream.tistory.com/</link>
    <description>개발 공부할 때 정리해두는 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Fri, 14 Aug 2026 18:29:45 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>계단</managingEditor>
    <image>
      <title>개발자의 오르막</title>
      <url>https://tistory1.daumcdn.net/tistory/3179456/attach/28cf9a51790a472398bef17656596eba</url>
      <link>https://catchdream.tistory.com</link>
    </image>
    <item>
      <title>[Udemy] Apache Kafka 시리즈 - 초보자를 위한 아파치 카프카 강의 v3</title>
      <link>https://catchdream.tistory.com/263</link>
      <description>&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;1.png&quot; data-origin-width=&quot;775&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3SWoP/btsHgn758A7/k0IxRmbRNBMJWCPVkJqop1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3SWoP/btsHgn758A7/k0IxRmbRNBMJWCPVkJqop1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3SWoP/btsHgn758A7/k0IxRmbRNBMJWCPVkJqop1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3SWoP%2FbtsHgn758A7%2Fk0IxRmbRNBMJWCPVkJqop1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;775&quot; height=&quot;340&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;775&quot; data-origin-height=&quot;340&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;저번 글에서는 Udmey 의 Docker &amp;amp; Kubernetes 실전 가이드 강의를 후기작성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 블로그 챌린지에서 Udemy 인강 2개를 제공해주셨기 때문에 그 중 하나인 Apache Kafka 시리즈 - 초보자를 위한 아파치 카프카 강의를 소개하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;왜 Kafka 강의를 선택했는가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcSJEC/btsHgYfWBOl/j4IU5FdPHeMWgTUxzZNFy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcSJEC/btsHgYfWBOl/j4IU5FdPHeMWgTUxzZNFy0/img.png&quot; data-alt=&quot;https://www.osckorea.com/post/blog-02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcSJEC/btsHgYfWBOl/j4IU5FdPHeMWgTUxzZNFy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcSJEC%2FbtsHgYfWBOl%2Fj4IU5FdPHeMWgTUxzZNFy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;250&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.osckorea.com/post/blog-02&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;Apache Kafka 는 오픈 소스 분산형 스트리밍 플랫폼입니다. 단어 뜻 그대로 오프된 소스코드를 가진 소프트웨어이며, 데이터를 실시간으로 처리하고, 처리된 결과를 다른 시스템으로 전송하는 플랫폼의 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&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;3.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/befR98/btsHi3HciWS/0OpIKIikIBKROuebDVIwtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/befR98/btsHi3HciWS/0OpIKIikIBKROuebDVIwtK/img.png&quot; data-alt=&quot;https://always-kimkim.tistory.com/entry/kafka101-broker&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/befR98/btsHi3HciWS/0OpIKIikIBKROuebDVIwtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbefR98%2FbtsHi3HciWS%2F0OpIKIikIBKROuebDVIwtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;738&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://always-kimkim.tistory.com/entry/kafka101-broker&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;위의 그림처럼 이벤트나 정보를 수집한 Producer 애플리케이션이 요청을 하게 되고, 이를 Consumer 애플리케이션이 수행하게 되는 플랫폼입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 애플리케이션에서 모든 요청을 수행하는 모놀리식 아키텍처 (Monolithic Architecture) 에서 점차 MSA (Microsoft Architecture) 로 수요가 생기게 되면서 각 서버 간의 데이터 흐름이 중요해졌습니다. 같은 역할을 하는 서버를 여러대 배포하는 것이 아닌, 서비스에서 해야하는 일을 분리, 분배하고 각 서버 간의 데이터 교류를 통해 역할을 수행하는 형태로 변화하였기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령, 쇼핑몰의 예를 들면 쇼핑몰의 공지사항에 대한 기능 요청에 대한 비중보다 주문 및 장바구니에 대한 기능 요청 비중이 훨씬 높기 때문입니다. 주문에 대한 기능을 다른 서버로 분리해놓고 보다 높은 사양의 서버를 운영하면 효율적으로 자원과 역할을 수행할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 위와 같이 서버가 분리되기 때문에 각 서버간에 이벤트를 전달하는 것이 중요해졌습니다. 상품을 주문하면 주문에 대한 알림 카톡이 오는 것과 같이 다른 서버에 필요한 이벤트를 요청하는 형태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 서버 간 데이터를 실시간으로 전달할 수 있는 도구로 Kafka가 가장 주목을 받고 있었기에 이 강의를 선택하였습니다.&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;4.png&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bijlZu/btsHhs2dxUI/Xd6UTDIsyNbXHTgcYh0k90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bijlZu/btsHhs2dxUI/Xd6UTDIsyNbXHTgcYh0k90/img.png&quot; data-alt=&quot;https://www.udemy.com/course/apache-kafka-korean/?couponCode=ST20MT50724&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bijlZu/btsHhs2dxUI/Xd6UTDIsyNbXHTgcYh0k90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbijlZu%2FbtsHhs2dxUI%2FXd6UTDIsyNbXHTgcYh0k90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;760&quot; height=&quot;346&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.udemy.com/course/apache-kafka-korean/?couponCode=ST20MT50724&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;Udemy 의 가장 커다란 차별점은 바로 자막에 있다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타 온라인 강의 사이트에서는 한국인들이 만든 강의들만 있다보니 여러 인터넷 강의를 접하기가 어렵습니다. 특히 우리나라의 IT 소프트웨어에서는 Java 의 비중이 워낙 높기 때문에 Java, SpringBoot 관련 주제가 많고, 다른 주제에 대해서는 선택의 비중이 높지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Udemy 는 글로벌 단위로 인터넷 강의를 제작하기 때문에 자막만 잘 되어 있으면 다양한 선택의 폭으로 필요한 주제를 선택할 수 있습니다.&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;5.png&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVT9j1/btsHgB6cUwl/WkZe5kJil8SveWsmhgm8i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVT9j1/btsHgB6cUwl/WkZe5kJil8SveWsmhgm8i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVT9j1/btsHgB6cUwl/WkZe5kJil8SveWsmhgm8i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVT9j1%2FbtsHgB6cUwl%2FWkZe5kJil8SveWsmhgm8i0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;730&quot; height=&quot;792&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;730&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 121 개의 강의, 8시간 30분의 강의시간으로 19,000 원에 구매할 수 있는 것은 가격 점에서도 매우 매력적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka 의 공식문서를 따라가면서 강의를 구성하는 것이 아니라 Kafka 내용을 인지하는 것에 필요한 이미지들을 직접 강사님이 만들어 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 Kafka 를 접하기 위해 다른 강의를 들었을 때에는 직관적으로 이해되지 않았지만, 이번 강의에서는 강사님이 직관적인 이미지 자료를 제공해주어 이해하기 손쉬웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히나 Kafka의 설치부터 CLI, Java 기반으로 Producer, Consumer 애플리케이션을 구축할 수 있는 실습까지 세세하게 안내를 하고 있습니다. 강의의 초반 부에서 기본 이론을 챙긴 후 실습을 쭉 따라가다보면 Kafka 에 대한 기초지식을 잘 쌓을 수 있을 것이라 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;인강을 듣고 난 후의 도전해볼 것?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 관심있는 사이드 프로젝트로 예정되어 있는 것은 바로 Group Chatting Server 입니다. 네이버나 트위터 등에서 사용되는 실시간 그룹채팅을 Redis 와 Kafka 이 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;Line Live 의 Chatting-Server 아키텍처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Line Live 의 Chatting-Server 아키텍처에서 중점적으로 다뤄보고 싶은 내용은 바로 Redis 의 Pub/Sub 구조입니다.&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;6.png&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4Q7qJ/btsHjeIBCqn/YRDIGqFHhqQpyilEAyFGMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4Q7qJ/btsHjeIBCqn/YRDIGqFHhqQpyilEAyFGMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4Q7qJ/btsHjeIBCqn/YRDIGqFHhqQpyilEAyFGMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4Q7qJ%2FbtsHjeIBCqn%2FYRDIGqFHhqQpyilEAyFGMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;409&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 A가 채팅 메시지를 보내면, 채팅 메시지를 받는 사용자들에게 메시지가 즉시 전달되는 것이 아니라 Redis 서버의 내부 처리 큐에 등록하여 순차적으로 처리되는 구조입니다. 우리는 채팅 메시지가 수신자 모두에게 전달되기까지 기다릴 필요 없이 Redis 내부 큐에 등록되기만 하면 작업이 완료되는 것입니다.&lt;/p&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 의 Pub/Sub 구조를 통해 Chatting-Server 간 메시지 전송도 비동기적으로 처리하여 대량의 메시지를 처리하는 높은 성능을 확보하게 되었습니다.&lt;/p&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 의 Pub/Sub 구조의 장단점은 다음과 같습니다.&lt;/p&gt;
&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis Pub/Sub 은 대량의 메시지를 처리할 수 있는 높은 성능을 가지고 있습니다.&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;Redis 의 Pub/Sub 은 여러 개의 서버로 확장하여 클러스터링을 구성할 수 있습니다.&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;Redis Pub/Sub 은 메시지를 즉시 처리하여 실시간성을 보장할 수 있습니다.&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;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;Redis Pub/Sub 은 메시지 전송 후 삭제되기 때문에 메시지의 지속성을 보장하지 않습니다.&lt;/li&gt;
&lt;li&gt;최대 1회 전송 (at-most-once) 패턴, 메시지가 유실될 수 있으나 최대 1회만 전송한다.&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;Redis Pub/Sub 은 Push 방식으로 Subscribe 에게 메시지를 밀어주기 때문에 Subscribe 가 많을 수록 성능이 저하될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Line Live Chatting-Server 는 Redis Pub/Sub 구조로 인해 메시지 유실 우려에 대해서는 MySQL 에 주기적으로 마이그레이션 하는 방법을 통해 극복을 하였습니다. 현재 네이버 Line Live Chatting Server 는 100대 이상의 인스턴스에서 동작하며 성능과 안정성을 증명해내고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;Kafka 기반의 Chatting Server&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 채팅 데이터 처리라고 하면 빼놓을 수 없는 키워드 중 하나가 바로 Kafka 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단, Kafka 에 대해 간략하게 설명한다면 Kafka 는 Source Application 과 Target Application 의 결합도를 낮추기 위한 메시지큐라고 볼 수 있습니다.&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;7.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9eB3K/btsHgCKYdgo/NMwzWH5GenLFJyEzAy4dh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9eB3K/btsHgCKYdgo/NMwzWH5GenLFJyEzAy4dh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9eB3K/btsHgCKYdgo/NMwzWH5GenLFJyEzAy4dh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9eB3K%2FbtsHgCKYdgo%2FNMwzWH5GenLFJyEzAy4dh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;858&quot; height=&quot;276&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;276&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;p data-ke-size=&quot;size16&quot;&gt;Kafka 의 구성요소는 다음과 같습니다.&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 : Kafka Broker 는 카프카가 설치되어 있는 서버 단위를 말합니다.&lt;/li&gt;
&lt;li&gt;Topic : 카프카에서 데이터가 들어갈 수 있는 공간으로 큐의 역할을 합니다.&lt;/li&gt;
&lt;li&gt;Partition : Partition 은 Topic 하나 당 Partition 을 여러개 보유할 수 있으며, 데이터 대기열을 물리적으로 분산된 것을 말합니다.&lt;/li&gt;
&lt;li&gt;Producer : 메시지를 발행하는 주체입니다.&lt;/li&gt;
&lt;li&gt;Consumer : 메시지를 구독하는 주체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Kafka 를 사용한다면 chatting-server architecture 가 어떤식으로 구성되는지 아래 예시를 통해 들어보겠습니다.&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;8.png&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PfhBs/btsHi2O4WVg/syYhybpW1BVMxzHgLkrMJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PfhBs/btsHi2O4WVg/syYhybpW1BVMxzHgLkrMJK/img.png&quot; data-alt=&quot;https://www.joinc.co.kr/w/man/12/Kafka/chatting&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PfhBs/btsHi2O4WVg/syYhybpW1BVMxzHgLkrMJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPfhBs%2FbtsHi2O4WVg%2FsyYhybpW1BVMxzHgLkrMJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;358&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.joinc.co.kr/w/man/12/Kafka/chatting&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;REST API 로 채팅방이 개설 요청을 하게 되면 Chatting-Server 는 Kafka 에 채팅방에 대한 Topic 을 생성합니다.&lt;/li&gt;
&lt;li&gt;Client 는 Chatting-Server 와 WebSocket 방식으로 커넥션을 가져갑니다.&lt;/li&gt;
&lt;li&gt;Client 는 WebSocket 으로 접속할 때 Request 로 던지는 채팅방_아이디를 기준으로 해당 Topic 을 Subscribe 합니다.&lt;/li&gt;
&lt;li&gt;메시지가 들어올 때마다 채팅방 Topic 을 Subsribe 하는 서버는 Client 에게 메시지를 전달합니다.&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;  우선 동작하는 건 알겠는데, Kafka 가 채팅 메시지 처리를 하는 것에 있어 장점이 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&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;Kafka 는 아래 3가지 장점을 가지고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;High throughput message capacity
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka 는 고 가용성으로, Producer (Websocket을 받는 Chatting-Server) 가 메시지를 발행하는 순간 Partition 에 메시지를 보관합니다.&lt;/li&gt;
&lt;li&gt;분산처리가 가능하기 때문에 짧은 시간 내에 엄청나게 많은 데이터를 Consumer 에게 전달할 수 있습니다.&lt;/li&gt;
&lt;li&gt;짧은 시간 내에 많은 데이터를 Consumer 에게 전달할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Scalability 와 Fault tolerant
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카프카는 확장성이 뛰어나므로, 브로커 중 몇대가 죽더라도 Replica 로 복제된 데이터로 안전하게 보관할 수 있습니다.&lt;/li&gt;
&lt;li&gt;가령, Websocket 으로 연결된 채팅 서버 중 1대가 죽거나 Restart 가 된다 하더라도, 해당 채팅메시지는 Kafka 의 Topic 에 보관되어 있기 때문에 재시작 후 다시 채팅 메시지를 받을 수 있습니다.&lt;/li&gt;
&lt;li&gt;기존, 연결이 끊어졌을 때 채팅메시지를 못 받았던 부분을 kafka 를 이용한다면 client 에서 다시 Reconnect 하는 것만으로 이전 채팅 메시지들을 다시 받을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Undeleted log
&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;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 Kafka를 접하시고, 사용하려고 하시는 분들에게 이 강의를 매우 추천합니다. 이론 부분을 소개해주시는 부분도 필요한 키워들에 대해서 설명해주셔서 배경지식을 갖기도 좋고, 개발하면서 궁금한 부분을 찾아보면서 진행할 수 있게 도와줍니다. 가격적인 측면에서도 저렴한 편이라 Kafka 를 잠깐 맛만 보기위해 알아보는 것에도 매우 좋을 듯 싶네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 사이드프로젝트를 도전해보면서 Udemy 에서 배운 내용과 연관도가 높은 주제는 따로 포스팅해서 다뤄볼 예정입니다.&lt;/p&gt;
&lt;p data-ke-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>Architecture</category>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/263</guid>
      <comments>https://catchdream.tistory.com/263#entry263comment</comments>
      <pubDate>Wed, 8 May 2024 19:14:03 +0900</pubDate>
    </item>
    <item>
      <title>요격 시스템 (ver. golang)</title>
      <link>https://catchdream.tistory.com/262</link>
      <description>&lt;h1&gt;문제링크&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/181188&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/181188&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1712618296851&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;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/181188&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4xtre/hyVMMRI4Pu/Ku6cc2dq2aQzzMS2vgkKk1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bRgLoD/hyVMY5GU2K/RLRE4Y3wMPAwwK3iHQBjM0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/181188&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/181188&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4xtre/hyVMMRI4Pu/Ku6cc2dq2aQzzMS2vgkKk1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bRgLoD/hyVMY5GU2K/RLRE4Y3wMPAwwK3iHQBjM0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;문제풀이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 최소한의 요격으로 모든 미사일을 격추시키는 것이 요구사항임을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-08 오전 8.08.23.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czsRZA/btsGuqDw8DU/hhCDVaF0MnrLsGDoT5dcm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czsRZA/btsGuqDw8DU/hhCDVaF0MnrLsGDoT5dcm1/img.png&quot; data-alt=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/181188&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czsRZA/btsGuqDw8DU/hhCDVaF0MnrLsGDoT5dcm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczsRZA%2FbtsGuqDw8DU%2FhhCDVaF0MnrLsGDoT5dcm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;598&quot; height=&quot;286&quot; data-filename=&quot;스크린샷 2024-04-08 오전 8.08.23.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://school.programmers.co.kr/learn/courses/30/lessons/181188&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;A나라의 미사일이 X축과 평행하게 발사되면, B나라에서는 Y축으로 발사하여 모든 미사일을 요격합니다. 따라서 X 구간의 범위가 겹치지 않는 선에서 최소한의 미사일을 발사하는 것이 중요합니다.&lt;/p&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 나라의 미사일 좌표를 정렬하고, start 와 end 값을 확인하고 끊겨지는 구간이 발생하는 경우 미사일을 한발 더 쏘게 하면 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌표의 끝점과 동일한 곳에서 미사일을 발사하면 안되는 제한조건이 있기 때문에 끝점과 일치하지 않는 경우만 미사일이 격추할 수 있도록 조건문을 작성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;go&quot; data-ke-language=&quot;go&quot;&gt;&lt;code&gt;func main() {
	fmt.Println(solution([][]int{{4, 5}, {4, 8}, {10, 14}, {11, 13}, {5, 12}, {3, 7}, {1, 4}}))
}

func solution(targets [][]int) int {
	cnt := 0

	fmt.Println(targets)
	// 한 발 맞출 때 최대한 많은 미사일을 맞추면 된다.
	// start 와 end 값을 확인하고 끊겨지는 구간을 확인하면 된다.
	// 값을 비교하기 위해서는 정렬이 필요하다.
	sort.Slice(targets[:], func(i, j int) bool {
		return targets[i][1] &amp;lt; targets[j][1]
	})

	fmt.Println(targets)

	// 끝점을 저장
	endPoint := 0
	for _, target := range targets {
		// 시작점이 endpoint 보다 작다면 이전 미사일로 격추할 수 있음
		if target[0] &amp;lt; endPoint {
			continue
		} else {
			cnt++
			endPoint = target[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;문제는 위와 같이 풀어보았고, 문제를 통해 정리할 수 있는 내용에 대해 정리하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Golang 에서 2차원 배열에 대한 정렬&lt;/h1&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ASIS : [[4 5] [4 8] [10 14] [11 13] [5 12] [3 7] [1 4]]

sort.Slice(targets[:], func(i, j int) bool {
		return targets[i][1] &amp;lt; targets[j][1]
})

TOBE : [[1 4] [4 5] [3 7] [4 8] [5 12] [11 13] [10 14]]
&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;sort.Slice 함수는 첫번째 인수로는 대상 배열, 두번째 인수로는 비교 함수를 받고 있습니다.&lt;/p&gt;
&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;그리고 비교함수의 i 와 j 는 인덱스를 의미하며 이는 순차적으로 증가됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 위에서 끝점(end point) 의 값에 따라 요격할 수 있는 범위가 달라지다보니, targets 의 배열의 첫 번째 요소의 끝점과 두 번째 요소의 끝점을 비교하여 오름차순(&amp;lt;) 정렬을 진행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Greedy Algorithm&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐욕알고리즘(Greedy Algorithm) 은 최적해를 구하는 알고리즘입니다. 각 단계에서 최적이라고 생각되는 것을 선택해나가 최종적인 해답에 도달하는 알고리즘입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의해야할 점은 탐욕알고리즘이 항상 최적의 값을 보장하는 것이 아니라 최적의 값의 근사한 값을 목표로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;탐욕 알고리즘의 필요 조건&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐욕 선택 속성 (Greedy Choice Property)&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;최적 부분 구조 (Optimal Substructure)&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;h3 data-ke-size=&quot;size23&quot;&gt;탐욕 알고리즘의 단계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택 절차(Selection Procedure) : 이 단계에서는 현재 상태에서 최적인 선택을 합니다. 이 선택은 이후에는 바뀌지 않습니다.&lt;/li&gt;
&lt;li&gt;적절성 검사(Feasibility Check) : 이 단계에서는 선택한 항목이 문제의 조건을 만족시키는지 확인합니다. 조건을 만족시키지 않으면 해당 항목은 제외됩니다.&lt;/li&gt;
&lt;li&gt;해답 검사(Solution Check) : 이 단계에서는 모든 선택이 완료되면 최종 선택이 문제의 조건을 만족시키는지 확인합니다. 조건을 만족시키면 해답으로 인정됩니다.&lt;/li&gt;
&lt;/ul&gt;
&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;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;문제의 구조에 맞게 선택 절차를 정의합니다. : 선택 절차 (Selection Procedure)&lt;/li&gt;
&lt;li&gt;선택 절차에 따라 선택을 수행합니다.&lt;/li&gt;
&lt;li&gt;선택된 해가 문제의 조건을 만족하는지 검사합니다. : 적절성 검사 (Feasibility Check)&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;h1&gt;요격시스템을 탐욕 알고리즘에 적용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 탐욕 알고리즘의 개념을 다져봤습니다. 우리가 문제를 풀 때 정렬을 적용한 배열을 순환을 통해서 어떤 조건을 판별하거나, 이전 단계에서의 결과가 다음 단계에서의 결과와 무관할 때 우리는 탐욕 알고리즘을 적용시킬 수 있는 점을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 탐욕 알고리즘으로 문제를 풀고자 할 때 우리는 선택절차, 적절성 검사, 해답 검사를 로직으로 구현하면 문제를 풀어낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3가지 단계를 쉽게 풀어내면, 하나의 단계를 어떤 단계부터 풀어낼 것인지 정하기 위해 배열을 정렬하여 단계를 설정합니다. 그리고 그 단계가 주어진 조건에 맞는지 적절성 검사를 진행합니다. 모든 검사가 완료되면 전체 해가 조건을 만족하는지 해답을 검사합니다.&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;pre class=&quot;go&quot;&gt;&lt;code&gt;func solution(targets [][]int) int {
	cnt := 0

	fmt.Println(targets)
	// 한 발 맞출 때 최대한 많은 미사일을 맞추면 된다.
	// start 와 end 값을 확인하고 끊겨지는 구간을 확인하면 된다.
	// 값을 비교하기 위해서는 정렬이 필요하다.
	sort.Slice(targets[:], func(i, j int) bool {
		return targets[i][1] &amp;lt; targets[j][1]
	})

	fmt.Println(targets)

	// 끝점을 저장
	endPoint := 0
	for _, target := range targets {
		// 시작점이 endpoint 보다 작다면 이전 미사일로 격추할 수 있음
		if target[0] &amp;lt; endPoint {
			continue
		} else {
			cnt++
			endPoint = target[1]
		}
	}

	return cnt
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선택절차&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요격시스템에서는 A나라가 발사한 미사일의 좌표 배열 targets 배열을 좌표의 끝점을 기준으로 정렬을 하였습니다. 우리는 targets 를 순회하면서 조건을 검사할 수 있는 단계들을 설정하였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;적절성 검사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요격시스템의 끝점을 endPoint 에 저장합니다. 그리고 이전 단계에서 발사했던 미사일의 타격범위에 포함되는지 적절성을 검사합니다. 미사일의 타격범위에 포함되지 않는 경우에는 새로운 미사일을 발사하고, 새로운 미사일의 endPoint 를 다시 저장하여 미사일 발사횟수를 count 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해답 검사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미사일 발사횟수 cnt 가 A 나라가 발사한 미사일들을 모두 격추할 수 있는지 검사하고, 답을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/212&quot;&gt;https://adjh54.tistory.com/212&lt;/a&gt;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/262</guid>
      <comments>https://catchdream.tistory.com/262#entry262comment</comments>
      <pubDate>Tue, 9 Apr 2024 08:21:09 +0900</pubDate>
    </item>
    <item>
      <title>[Udemy] - Docker &amp;amp; Kubernetes 실전 가이드 후기</title>
      <link>https://catchdream.tistory.com/261</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;775&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boZtXP/btsGjFhCW2J/gdl9fIzQJs5uPBsjHPVrRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boZtXP/btsGjFhCW2J/gdl9fIzQJs5uPBsjHPVrRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boZtXP/btsGjFhCW2J/gdl9fIzQJs5uPBsjHPVrRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboZtXP%2FbtsGjFhCW2J%2Fgdl9fIzQJs5uPBsjHPVrRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;775&quot; height=&quot;340&quot; data-origin-width=&quot;775&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/figure&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;개요&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;글또를 진행하던 도중 Udemy에서 강의 쿠폰을 받게 되어 후기를 작성하게 되었다. 원래 알고 즐겨쓰던 강의사이트였지만 글또를 통해 다시 한번 강의를 듣게되니 반가운 마음이 들었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;udemy 는 여타 다른 강의사이트와 확연하게 차별화되는 부분은 바로 외국인의 강의가 엄청 많다는 점이다. 만일 java 나 Spring 의 경우라면 인프런이나 패스트캠퍼스 등 국내 인터넷 강의사이트에도 컨텐츠가 넘칠만큼 많지만, Docker, Kubernetes, Golang, Mongodb 등 카테고리가 조금만 벗어나게 되면 인터넷 강의의 숫자가 확연하게 줄어든다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 있더라도 처음 도입 부분에 대한 내용을 다루거나, 중복되는 내용이 많다. 컨텐츠의 절대적인 숫자가 적어 어쩔수가 없는 현실이다. 심지어 Golang 같은 경우는 카테고리 자체가 존재하지 않는 경우가 더러 있다… ㅠㅠ&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&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;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dox2Xt/btsGl2oCxIT/fQ6zMt6D2kaPS3h5CvoTGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dox2Xt/btsGl2oCxIT/fQ6zMt6D2kaPS3h5CvoTGk/img.png&quot; data-alt=&quot;https://www.udemy.com/courses/search/?src=ukw&amp;amp;amp;amp;q=golang&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dox2Xt/btsGl2oCxIT/fQ6zMt6D2kaPS3h5CvoTGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdox2Xt%2FbtsGl2oCxIT%2FfQ6zMt6D2kaPS3h5CvoTGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1356&quot; height=&quot;714&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.udemy.com/courses/search/?src=ukw&amp;amp;amp;q=golang&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;그러나 udemy 의 경우에는 이처럼 강의의 절대적인 숫자가 많다보니, 품질이 높은 강의 또한 존재한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K0ttQ/btsGj3vuAXN/UinZhNnkQpv0ckHOH2qK21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K0ttQ/btsGj3vuAXN/UinZhNnkQpv0ckHOH2qK21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K0ttQ/btsGj3vuAXN/UinZhNnkQpv0ckHOH2qK21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK0ttQ%2FbtsGj3vuAXN%2FUinZhNnkQpv0ckHOH2qK21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;126&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이처럼 번역된 언어같은 경우 기재를 해놓는데, 번역 자막 자체가 있다는 것은 그만큼 자막의 품질이 보증되어있다는 것을 의미한다. 저같은 경우 한국어 자막을 볼 때 강의를 시청하는데 있어서 불편함이 없을 정도였다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이번에 선택한 강의는 아래와 같다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1165&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHNnWP/btsGjmWHYs9/xMqiUfKgyEs9U9PnZ6H010/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHNnWP/btsGjmWHYs9/xMqiUfKgyEs9U9PnZ6H010/img.png&quot; data-alt=&quot;https://www.udemy.com/course/docker-kubernetes-2022/?couponCode=ST13MT40224&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHNnWP/btsGjmWHYs9/xMqiUfKgyEs9U9PnZ6H010/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHNnWP%2FbtsGjmWHYs9%2FxMqiUfKgyEs9U9PnZ6H010%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1165&quot; height=&quot;386&quot; data-origin-width=&quot;1165&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.udemy.com/course/docker-kubernetes-2022/?couponCode=ST13MT40224&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nNbo5/btsGlYtdo0c/mh3T0P9nOhvVf0pGEb0N9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nNbo5/btsGlYtdo0c/mh3T0P9nOhvVf0pGEb0N9K/img.png&quot; data-alt=&quot;https://www.udemy.com/course/docker-kubernetes-2022/?couponCode=ST13MT40224&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nNbo5/btsGlYtdo0c/mh3T0P9nOhvVf0pGEb0N9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnNbo5%2FbtsGlYtdo0c%2Fmh3T0P9nOhvVf0pGEb0N9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;844&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.udemy.com/course/docker-kubernetes-2022/?couponCode=ST13MT40224&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;강의의 커리큘럼만 봐도 총 길이 23 시간 24분, 261개의 강의만 봐도 정말 내용이 많은 것을 알 수 있다. 그러나 인기가 많은 강의이다보니 자세하고 친절하게 설명되어 있고, 실습을 병행할 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;실습은 Node.js + MongoDB 결합의 형태로 이루어지는데, 저같은 경우는 Golang 기반으로 간단하게 짜봐서 같이 실습을 진행했다. Docker 의 기능을 확인하는 것이 주된 강의 목적이다 보니 Node.js 에 대한 배경지식은 없어도 괜찮았다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;&lt;h1&gt;데이터관리 및 볼륨으로 작업하기&lt;/h1&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;강의에서 강사님은 먼저 Image 를 통해 Docker Container 의 구조를 설명해준다. (이미지는 저작권 문제로 Skip 한다.)&lt;br&gt;이번 주제에서도 우리가 사용하는 데이터를 먼저 개념적으로 분류한다.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터의 종류&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;도커 이미지 (코드) 
  &lt;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;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;영구 데이터 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;ex) 사용자 회원 정보 데이터&lt;/li&gt; 
   &lt;li&gt;영구 데이터는 파일이나 데이터베이스에 저장된다.&lt;/li&gt; 
   &lt;li&gt;container 의 stop 이나 restart 로 인해 데이터가 소실되면 안된다.&lt;/li&gt; 
   &lt;li&gt;conatiner 나 volume 에 저장된다.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;도커의 데이터 쓰는 방식&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;이미지가 아닌 컨테이너에 저장된다는 것은 부가(extra) 레이어에 대해 이야기하는 것&lt;/li&gt;&lt;li&gt;이 레이어는 실제 컨테이너를 구성합니다.&lt;/li&gt;&lt;li&gt;이미지의 파일 시스템을 인식하며, 복사하지 않고 파일을 미러링하는 로직입니다.&lt;/li&gt;&lt;li&gt;도커는 read-write 권한을 가지며, 파일의 시스템을 조작할 수 있습니다.&lt;/li&gt;&lt;li&gt;이미지 자체는 변경하지 않은 채 부가레이어에서 변경합니다.&lt;/li&gt;&lt;li&gt;도커는 컨테이너의 변경 사항을 추적하고, 이미지와 파일 시스템을 가져와 최종 파일 시스템을 파생시켜 read-write 레이어에 저장된 변경 사항과 결합시킵니다.&lt;/li&gt;&lt;li&gt;컨테이너 내부에 쓰여진 데이터는 컨테이너 자체가 삭제되지 않는 이상 컨테이너에 유지된다.&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;볼륨이란?&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;COPY 
  &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;VOLUME 
  &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;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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;Dockerfile 에 VOLUME 명령어를 추가하는 방법 
  &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;익명 : docker run -v /app/data busybox&lt;/li&gt; 
     &lt;li&gt;명명 : docker volume create my-named-volume , docker run -v my-named-volume:/app/data busybox&lt;/li&gt; 
     &lt;li&gt;차이 
      &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
       &lt;li&gt;생성방법 : 익명 볼륨은 컨테이너 실행 시 자동으로 생성, 명명 볼륨은 명령어를 통해서만 생성&lt;/li&gt; 
       &lt;li&gt;이름 지정 : 익명 볼륨은 컨테이너 종속적이며, 이름이 없음, 명명 볼륨은 다른 컨테이너에서도 동시에 사용 가능&lt;/li&gt; 
       &lt;li&gt;수명 : 익명 볼륨은 컨테이너의 수명과 일치하며, 컨테이너 삭제 시 삭제됨. 명명 볼륨은 명시적으로 삭제하지 않는 한 계속해서 존재 
        &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
         &lt;li&gt;그러나 익명 볼륨은 --rm 옵션이 없다면 자동으로 제거 되지 않음&lt;/li&gt; 
         &lt;li&gt;docker volume rm {volume_name}&lt;/li&gt; 
         &lt;li&gt;docker volume prune&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;/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;/ul&gt;&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# docker volume 삭제
docker volume rm {volume_name}

# 사용하지 않는 볼륨 제거
docker volume prune
&lt;/code&gt;&lt;/pre&gt;&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;우리는 도커에 의해 관리되는 볼륨의 위치는 실제 알지 못한다.&lt;/li&gt;&lt;li&gt;바인드 마운트의 경우에는 알 수 있다. 왜냐하면 호스트 머신 상 매핑될 컨테이너의 경로를 설정할 수 있기 때문이다.&lt;/li&gt;&lt;li&gt;바인드마운트는 그대로 폴더를 연결하기 때문에 영구적이고 편집 가능한 데이터에 적합하다.&lt;/li&gt;&lt;li&gt;명명된 볼륨은 영구 데이터에 도움이 되지만 편집은 실제로 불가능하기 때문이다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;-v 로 선언할 때 : 앞에 로컬 머신 경로가 있으면 바인드 마운트, 볼륨 이름이 있으면 명명 볼륨으로 설정된다.&lt;/b&gt;&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;도커 볼륨 명령어&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;docker run -v /app/data ...&lt;/li&gt; 
   &lt;li&gt;컨테이너가 유지될 때까지만 유지됨&lt;/li&gt; 
   &lt;li&gt;컨테이너 간 데이터 공유할 수 없음&lt;/li&gt; 
   &lt;li&gt;시간 절약 가능&lt;/li&gt; 
   &lt;li&gt;호스트머신 안 파일을 생성하지만, 컨테이너가 중지될 때 지움&lt;/li&gt; 
  &lt;/ul&gt; &lt;/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;docker run -v data:/app/data ...&lt;/li&gt; 
   &lt;li&gt;특정 컨테이너에 연결되지 않은 채 생성 가능&lt;/li&gt; 
   &lt;li&gt;컨테이너 간 데이터를 공유할 수 있음&lt;/li&gt; 
   &lt;li&gt;다양한 컨테이너에서 동일한 볼륨에 저장 및 접근 가능&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;바인드 마운트 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;docker run -v /path/to/code:/app/code ...&lt;/li&gt; 
   &lt;li&gt;다수 컨테이너에 연결될 수 있음&lt;/li&gt; 
   &lt;li&gt;호스트머신의 파일 경로와 연동&lt;/li&gt; 
   &lt;li&gt;컨테이너 재시작 되어도 재사용 가능&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;디폴트로 볼륨은 read-write 이며, 이는 컨테이너가 볼륨에서 데이터를 읽고, 쓸 수 있음을 의미합니다.&lt;/li&gt;&lt;li&gt;컨테이너는 내부 경로 뒤에 콜론을 추가한 다음 readonly 를 의미하는 ro 를 추가하면 readonly 가 적용됩니다. (컨테이너만 적용)&lt;/li&gt;&lt;li&gt;호스트 머신에서는 파일을 읽고 쓸 수 있습니다.&lt;/li&gt;&lt;li&gt;도커파일에서 Volume 을 지정해도 docker run 명령어에서 명시된 Volumn 이 우선권을 가진다.&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;도커 볼륨 명령어&lt;/h3&gt;&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# docker volume 관련 명령어
docker volume --help

# volume 목록
docker volume ls

# volume 생성
docker volume create

# volume inspect
docker volume inspect {volume_id}
&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;볼륨은 호스트머신의 /var/lib/docker/volumes/… 에 저장된다.&lt;/li&gt;&lt;li&gt;읽기전용 같은 경우 volume inspect 의 Options 에서 해당 옵션이 기재된다.&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;바운드 마운트와 COPY 의 차이점&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;docker run 명령어는 보통 개발용으로 사용한다.&lt;/li&gt;&lt;li&gt;COPY 는 스냅샷을 가질 수 있어 캐시가 가능하다.&lt;/li&gt;&lt;/ul&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;.dockerignore&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;docker ignore 를 사용하면 COPY 명령어로 복사하지 못하게 할 수 있다.&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;인수와 환경변수&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;docker build 를 실행할 때 --build-arg 옵션을 사용해서 함께 제공되는 인수를 활용할 수 있다.&lt;/li&gt; 
 &lt;li&gt;환경변수와 arg 은 Dockerfile 내부에서 사용 가능하다. 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;ARG DEFAULT_PORT=80&lt;/li&gt; 
   &lt;li&gt;ENV PORT $DEFAULT_PORT&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;도커 파일 안에서의 변수는 변경 될 때마다 캐시된 레이어를 모두 바꾸기 때문에 변경되지 않는 레이어 아래에 선언하는 것이 좋다.&lt;/li&gt; 
 &lt;li&gt;환경변수는 docker 명령어 옵션에서도 사용 가능하다. 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;--build-arg DEFAULT_PORT=8000&lt;/li&gt; 
   &lt;li&gt;--e PORT=8080&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;인수는 실행 중인 애플리케이션의 전체 애플리케이션 코드에서 사용할 수 있다.&lt;/li&gt; 
 &lt;li&gt;포트의 디폴트 값의 경우도 인수로 다룰 수 있음. (ARG)&lt;/li&gt; 
&lt;/ul&gt;&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;&lt;h1&gt;결론&lt;/h1&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;이번 강의에서 다른 주제들도 기초부터 모든 내용에 대한 설명을 차근차근 해주고 있다. 저같은 경우 회사에서 Docker 와 Kubernetes 를 운영하고 있어 아는 부분이나 실습 부분은 빠른 배속으로 보고, 개념적인 부분은 노션에 정리하면서 강의를 진행했다. 내용 자체는 방대하나 이 강의를 1번이라도 훑게되면 기본적인 지식들로 바닥을 다질 수 있을 것이란 생각이 든다.&lt;/p&gt;</description>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/261</guid>
      <comments>https://catchdream.tistory.com/261#entry261comment</comments>
      <pubDate>Wed, 3 Apr 2024 16:04:43 +0900</pubDate>
    </item>
    <item>
      <title>[코드 트리 챌린지] 단계별 학습을 통한 문제 향상</title>
      <link>https://catchdream.tistory.com/260</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D3mOQ/btsGgdwRq0W/Lu7M8OMlJTp8g7CS3WQHb0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D3mOQ/btsGgdwRq0W/Lu7M8OMlJTp8g7CS3WQHb0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D3mOQ/btsGgdwRq0W/Lu7M8OMlJTp8g7CS3WQHb0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD3mOQ%2FbtsGgdwRq0W%2FLu7M8OMlJTp8g7CS3WQHb0%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;900&quot; height=&quot;900&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;900&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;h1&gt;개요&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벌써 2번째 코드트리 블로그 챌린지를 작성하게 되었다. &lt;a href=&quot;https://catchdream.tistory.com/259&quot; target=&quot;_blank&quot; rel=&quot;noopener&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a2.png&quot; data-origin-width=&quot;1469&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSVGBP/btsGcM9cX03/ewAajjFRvdzVWWSxdCFWc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSVGBP/btsGcM9cX03/ewAajjFRvdzVWWSxdCFWc0/img.png&quot; data-alt=&quot;https://www.codetree.ai/curriculums&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSVGBP/btsGcM9cX03/ewAajjFRvdzVWWSxdCFWc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSVGBP%2FbtsGcM9cX03%2FewAajjFRvdzVWWSxdCFWc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1469&quot; height=&quot;422&quot; data-filename=&quot;a2.png&quot; data-origin-width=&quot;1469&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.codetree.ai/curriculums&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;코드트리의 경우 프로그래밍 커리큘럼을 위와 같이 구분하였다. 먼저 프로그래밍 기초 (C, C++ ,Python, Java) 를 지원하여 코딩 테스트 문제를 푸는데 필요한 언어의 용법적 지식을 습득할 수 있는 과정이다. 프로그래밍 연습 같은 경우는 NOVICE LOW, MID, HIGH 로 구분되어진다. LOW 는 맨 처음 문제를 푸는 습관을 들이기 시작할 때에는 이만한 과정이 없는 것 같다. 난이도 자체는 쉬운 편이고, 내가 주력으로 사용하는 언어라면 문제를 풀어가면서 워밍업 해볼 수 있는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&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;NOVICE MID&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a3.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beIdSu/btsGeLujZzU/tAxOLUv6b3GZFvnOiA9Qik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beIdSu/btsGeLujZzU/tAxOLUv6b3GZFvnOiA9Qik/img.png&quot; data-alt=&quot;https://www.codetree.ai/curriculums&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beIdSu/btsGeLujZzU/tAxOLUv6b3GZFvnOiA9Qik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeIdSu%2FbtsGeLujZzU%2FtAxOLUv6b3GZFvnOiA9Qik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1078&quot; data-filename=&quot;a3.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.codetree.ai/curriculums&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 해당커리큘럼은 각 단계별로 문제 단원이 주어진다. 각 단원마다 문제의 난이도와 갯수는 제공하고 있으며, 내가 좋았던 부분은 문제를 풀기 전 기본문제를 통해 어떤 식으로 푸는 건지 간단한 개념과 풀이과정을 소개해주는 것이다. 학창 시절 수학에 대한 개념과 관련 연습문제를 풀고, 시험을 풀었던 것처럼 코딩테스트 문제도 개념과 연습문제 등 문제를 풀어가는 순서가 중요하고 효율적인 것을 이번 코드트리 덕분에 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a4.png&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ekVzI4/btsGeLgLQ1T/osuHlRybe83ZycMKKnRYQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ekVzI4/btsGeLgLQ1T/osuHlRybe83ZycMKKnRYQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ekVzI4/btsGeLgLQ1T/osuHlRybe83ZycMKKnRYQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FekVzI4%2FbtsGeLgLQ1T%2FosuHlRybe83ZycMKKnRYQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;92&quot; data-filename=&quot;a4.png&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;92&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;자료구조도 각 난이도별로 단원이 구분되어있기 때문에 내가 부족한 부분을 집중적으로 공부할 수 있게 되었다. 문제만 제공해주는 타사사이트에서 망망대해를 헤엄치던 중 코드트리를 접하게 되었을 때에는 이정표를 받은 것 같아 기분이 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a5.png&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o5aHp/btsGcK4Mpo1/aHkhPtHgvILfxYjdoLNDA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o5aHp/btsGcK4Mpo1/aHkhPtHgvILfxYjdoLNDA1/img.png&quot; data-alt=&quot;https://www.codetree.ai/training-field/frequent-problems/company/samsung/problems&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o5aHp/btsGcK4Mpo1/aHkhPtHgvILfxYjdoLNDA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo5aHp%2FbtsGcK4Mpo1%2FaHkhPtHgvILfxYjdoLNDA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;992&quot; height=&quot;802&quot; data-filename=&quot;a5.png&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.codetree.ai/training-field/frequent-problems/company/samsung/problems&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a6.png&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;751&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OTBBx/btsGdDKND6t/FNa9kTdkSOyw9utjpvwVqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OTBBx/btsGdDKND6t/FNa9kTdkSOyw9utjpvwVqK/img.png&quot; data-alt=&quot;https://www.codetree.ai/training-field/frequent-problems/company/samsung/channel&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OTBBx/btsGdDKND6t/FNa9kTdkSOyw9utjpvwVqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOTBBx%2FbtsGdDKND6t%2FFNa9kTdkSOyw9utjpvwVqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;943&quot; height=&quot;751&quot; data-filename=&quot;a6.png&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;751&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.codetree.ai/training-field/frequent-problems/company/samsung/channel&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;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a7.png&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bU504w/btsGeanX8kR/uz84VjKbimH62ZZrsVqYJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bU504w/btsGeanX8kR/uz84VjKbimH62ZZrsVqYJk/img.png&quot; data-alt=&quot;https://www.codetree.ai/cote/stores?planSetId=18&amp;amp;amp;missionIds=20&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bU504w/btsGeanX8kR/uz84VjKbimH62ZZrsVqYJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbU504w%2FbtsGeanX8kR%2Fuz84VjKbimH62ZZrsVqYJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1081&quot; data-filename=&quot;a7.png&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.codetree.ai/cote/stores?planSetId=18&amp;amp;missionIds=20&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a8.png&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEgz7D/btsGdQQPfZn/Ul7wbF2CQqIoJ9lZj2AKZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEgz7D/btsGdQQPfZn/Ul7wbF2CQqIoJ9lZj2AKZ1/img.png&quot; data-alt=&quot;내가 목표하는 기업에 관련된 문제들을 문제집의 형태로 제공한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEgz7D/btsGdQQPfZn/Ul7wbF2CQqIoJ9lZj2AKZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEgz7D%2FbtsGdQQPfZn%2FUl7wbF2CQqIoJ9lZj2AKZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;235&quot; data-filename=&quot;a8.png&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;235&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;위처럼 1개월권에 7만원정도에 달하는 금액이며, 직장을 다닌다는 기준 하에 1권이면 정말 빠듯하게 1개월을 보낼 것이라고 생각한다. 그러니 내가 집중할 수 있겠다라고 생각하는 기간에 결제를 해서 문제를 푸는 것이 좋을 듯 싶다.&lt;/p&gt;
&lt;p data-ke-size=&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;코드트리를 체험해보면서 정말 정성이 많이 들어간 사이트란 걸 느끼게 되었다. 매일 학습 내용이 GitHub 계정에 자동으로 커밋을 남기는 부분부터, 문제를 푸는 경험치를 EXP 로 알려주니, RPG 캐릭터를 키우는 것마냥 재미가 있었다. 정말 코딩테스트 하나만으로 다양한 시각화 자료를 통해 재미있게 문제에 접근하게 해주는 부분에서 정말 완성도 있는 사이트인 점을 느꼈다.&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;아쉬웠던 점은 나의 주력언어가 Golang 이다보니, Java 로 문제를 풀어가는 것이 좀 아쉽게 느껴졌다. 현재 Golang 에 애착을 더해가는 과정이기 때문에 문제를 풀더라도 애착가는 언어로 문제를 풀면 기분이 좋은 것은 어쩔 수 없나싶다. 그래서 좀 막히는 문제가 있다 싶으면 Golang 으로 먼저 풀어보고, Java 로 풀고 제출하는 형태로 많이 진행했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오로지 문제집만 있기 때문에 한 달에 69,000원은 좀 비싸게 느껴지기도 했다. 그러나 이번에 풀면서 느꼈던 것은 1권에 포함되는 양질의 문제양에 이정도 비용은 아깝지 않다는 것이었다. 다만 현재는 사이드프로젝트와 인터넷강의, 블로그와 같이 동시에 진행되는 것이 많아 비용만큼의 시간을 쏟지 못해 아쉽기는 하다. 한 2개월 정도 뒤에는 1개월에 1권씩 문제에 집중하면서 내 실력을 키워가보고 싶다.&lt;/p&gt;
&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>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/260</guid>
      <comments>https://catchdream.tistory.com/260#entry260comment</comments>
      <pubDate>Sat, 30 Mar 2024 22:41:29 +0900</pubDate>
    </item>
    <item>
      <title>[코드 트리 챌린지] 유클리드 호제법</title>
      <link>https://catchdream.tistory.com/259</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uXvNg/btsFioGhI3C/CsEVWW3rfTwLduehogqLb1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uXvNg/btsFioGhI3C/CsEVWW3rfTwLduehogqLb1/img.jpg&quot; data-alt=&quot;https://www.youtube.com/c/codetreeai/about&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uXvNg/btsFioGhI3C/CsEVWW3rfTwLduehogqLb1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuXvNg%2FbtsFioGhI3C%2FCsEVWW3rfTwLduehogqLb1%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;900&quot; height=&quot;900&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.youtube.com/c/codetreeai/about&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;개요&lt;/h1&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;2.png&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cK1Y9D/btsFfugRlER/ZM8VuGWQmEO3Ec0WbK7YCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cK1Y9D/btsFfugRlER/ZM8VuGWQmEO3Ec0WbK7YCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cK1Y9D/btsFfugRlER/ZM8VuGWQmEO3Ec0WbK7YCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcK1Y9D%2FbtsFfugRlER%2FZM8VuGWQmEO3Ec0WbK7YCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1122&quot; height=&quot;645&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;645&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;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;3.png&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEKQac/btsFinABsF6/x3HYuT3nHTGeT4UyHCrsdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEKQac/btsFinABsF6/x3HYuT3nHTGeT4UyHCrsdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEKQac/btsFinABsF6/x3HYuT3nHTGeT4UyHCrsdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEKQac%2FbtsFinABsF6%2Fx3HYuT3nHTGeT4UyHCrsdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;357&quot; height=&quot;217&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;217&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2024-02-28 at 11.22.48 PM.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQnSZ7/btsFoFIkDnB/eaAXoNuJV6csfad01sGNfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQnSZ7/btsFoFIkDnB/eaAXoNuJV6csfad01sGNfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQnSZ7/btsFoFIkDnB/eaAXoNuJV6csfad01sGNfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQnSZ7%2FbtsFoFIkDnB%2FeaAXoNuJV6csfad01sGNfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;862&quot; height=&quot;385&quot; data-filename=&quot;Screen Shot 2024-02-28 at 11.22.48 PM.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;385&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;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;p data-ke-size=&quot;size16&quot;&gt;반복적인 학습에서 가장 중요한 부분은 반복된 성취감이라고 생각한다. 아무리 바쁘더라도 Low Level 의 코딩 테스트 문제 3개는 무조건 풀 수 있다. (5분정도 걸립니다) 오늘도 3개는 풀고 잔다는 성취감은 코딩테스트에 대한 부담감을 분명하게 덜어준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 내가 준비하고자 하는 코딩 테스트 유형별로 분류가 되어있기 때문에 내가 약한 부분을 집중적으로 풀수도 있고, 준비하고자 하는 테스트를 집중적으로도 풀 수 있다. 회사원도 회사원이지만, 취직을 준비하는 취준생의 경우 코딩테스트에 대한 자신감을 분명하게 얻어갈 수 있는 기회라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 무언가를 부담스러워하고 두려워하는 이유는, 그 무언가가 측정되지 않은 두려움이기 때문이다. 코드트리는 그 두려움을 객관적인 지표를 통해 극복하게 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;아래는 유클리드 호제법에 대한 내용 정리와 이를 Golang, Java 로 문제를 푼 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;유클리드 호제법&lt;/h1&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;유클리드 호제법은 두 정수의 최대 공약수(GCD) 를 구하는 방법 중 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 두 수를 나눠가며 나머지가 0이 될 때까지 반복하여 최대 공약수를 찾는 방법이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;a 를 b 로 나누어 떨어지면, b 가 최대 공약수이다.&lt;/li&gt;
&lt;li&gt;a 를 b 로 나누어도 나머지가 있으면 a 를 b 로 나눈 나머지를 새로운 a 로 하고, b 를 이전의 a 로 대체한다.&lt;/li&gt;
&lt;li&gt;위의 과정을 나머지가 0이 될 때까지 반복한다. 나머지가 0이면 그때의 b 가 최대공약 수이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;유클리드 호제법 최대공약수 구하는 법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a, b 두 수가 있다면 a, b 를 나누어서 나머지를 r이라고 하면, r 이 0이면 b가 최대 공약수이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;GCD(48, 18)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;48 % 18 = 12&lt;/li&gt;
&lt;li&gt;18 % 12 = 6&lt;/li&gt;
&lt;li&gt;12 % 6 = 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GCD(54, 12)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;54 % 12 = 6&lt;/li&gt;
&lt;li&gt;12 % 6 = 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GCD(150, 36)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;150 % 36 = 6&lt;/li&gt;
&lt;li&gt;36 % 6 = 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;215&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b190j6/btsFipyruT8/XYAk3KzKIr3XCi9CW6lHLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b190j6/btsFipyruT8/XYAk3KzKIr3XCi9CW6lHLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b190j6/btsFipyruT8/XYAk3KzKIr3XCi9CW6lHLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb190j6%2FbtsFipyruT8%2FXYAk3KzKIr3XCi9CW6lHLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;215&quot; height=&quot;67&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;215&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;LCM(150, 36)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최대 공약 수 = GCD(150,36) = 6&lt;/li&gt;
&lt;li&gt;최소 공배 수 = 150 * 36 / 6 = 900&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;문제풀이 (Golang .ver)&lt;/h1&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;앞서 최대공약수를 구할 때 두 수의 나머지가 0이 될 때까지 계속해서 나누는 것을 알 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 조건이 이뤄지기 전까지 로직을 반복할 때에는 재귀함수를 적용 가능하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;재귀함수의 필요조건&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일한 함수를 계속 호출하면서 하나의 함수가 자기 자신을 재귀적으로 호출하게 하는 것&lt;/li&gt;
&lt;li&gt;재귀가 멈추는 시점&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;재귀함수&lt;/h3&gt;
&lt;pre class=&quot;go&quot; data-ke-language=&quot;go&quot;&gt;&lt;code&gt;// 유클리드 호제법으로 최대공약수 계산
func gcd(a, b int) int {
	if b == 0 {
		return a
	}

	return gcd(b, a%b)
}&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;위의 로직은 a%b 의 나머지가 0이 될 때까지 계속해서 gcd 함수를 반복하는 로직이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체 코드&lt;/h3&gt;
&lt;pre class=&quot;go&quot; data-ke-language=&quot;go&quot;&gt;&lt;code&gt;package main

import (
	&quot;fmt&quot;
	&quot;log&quot;
)

// 유클리드 호제법으로 최대공약수 계산
func gcd(a, b int) int {
	if b == 0 {
		return a
	}

	return gcd(b, a%b)
}

// 최소공배수 계산
func lcm(a, b int) int {
	return a * b / gcd(a, b)
}

func main() {
	var num1, num2 int

	// 사용자로부터 두 수 입력 받기
	fmt.Print(&quot;첫 번째 수를 입력하세요: &quot;)
	if _, err := fmt.Scan(&amp;amp;num1); err != nil {
		log.Fatalf(&quot;정수를 입력해주세요.&quot;)
	}

	fmt.Print(&quot;두 번째 수를 입력하세요: &quot;)
	if _, err := fmt.Scan(&amp;amp;num2); err != nil {
		log.Fatalf(&quot;정수를 입력해주세요.&quot;)
	}

	// 최대공약수 계산 및 출력
	gcdResult := gcd(num1, num2)
	fmt.Printf(&quot;두 수의 최대공약수(GCD): %d\\n&quot;, gcdResult)

	// 최소공배수 계산 및 출력
	lcmResult := lcm(num1, num2)
	fmt.Printf(&quot;두 수의 최소공배수(LCM): %d\\n&quot;, lcmResult)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;문제풀이 (Java .ver)&lt;/h1&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;앞서 최대공약수를 구할 때 두 수의 나머지가 0이 될 때까지 계속해서 나누는 것을 알 수 있었다.&lt;/li&gt;
&lt;li&gt;특정 조건이 이뤄지기 전까지 로직을 반복할 때에는 재귀함수를 적용 가능하다.&lt;/li&gt;
&lt;li&gt;재귀함수의 필요조건
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일한 함수를 계속 호출하면서 하나의 함수가 자기 자신을 재귀적으로 호출하게 하는 것&lt;/li&gt;
&lt;li&gt;재귀가 멈추는 시점&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;두 수를 입력 받아 최대고약수, 최소공배수를 구하는 로직을 구성하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;재귀함수&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static int gcd(int a, int b) {
	if (b == 0) {
		return a;
	}
	return gcd(b, a%b);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체 코드&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int a = sc.nextInt();
        int b = sc.nextInt();

        int gcdResult = gcd(a, b);
        int lcmResult = (a * b) / gcdResult;

        System.out.println(lcmResult);
    }

    public static int gcd(int a, int b) {
        if (b == 0) {
            return a;
        }

        return gcd(b, a%b);
    }
}&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;java 에서도 마찬가지로 gcd 함수를 재귀함수로 구현하였다.&lt;/li&gt;
&lt;li&gt;a 와 b 의 나머지가 0이 안니 경우 b 와 a%b 를 인자로 계속해서 값을 구하였다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/259</guid>
      <comments>https://catchdream.tistory.com/259#entry259comment</comments>
      <pubDate>Sat, 24 Feb 2024 13:00:39 +0900</pubDate>
    </item>
    <item>
      <title>코드스멜을 피하는 방법 Sonarqube</title>
      <link>https://catchdream.tistory.com/256</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Overview&lt;/h1&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;1.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNNPj7/btso8dtA2KD/RQqNgVvgc8d9MSwq3jEGv1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNNPj7/btso8dtA2KD/RQqNgVvgc8d9MSwq3jEGv1/img.jpg&quot; data-alt=&quot;https://www.cambiaresearch.com/articles/995518/six-reasons-to-ditch-the-term-code-smell&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNNPj7/btso8dtA2KD/RQqNgVvgc8d9MSwq3jEGv1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNNPj7%2Fbtso8dtA2KD%2FRQqNgVvgc8d9MSwq3jEGv1%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;1280&quot; height=&quot;720&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.cambiaresearch.com/articles/995518/six-reasons-to-ditch-the-term-code-smell&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드스멜, 소프트웨어 개발 시에 사용되는 용어로, 잠재적인 문제를 야기할 수 있는 부분들에서 냄새가 난다는 관용적 표현으로 많이 쓰입니다. 코드의 가독성, 유지보수성, 확장성, 성능 등 부정적인 영향을 끼칠 수 있는 부분들을 기준에서 해당이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;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;우리는 그런 이유로, 이런 문제들은 사람이 아닌 컴퓨터가 알려줬으면 합니다. 우리가 PR 을 날리기전에 이런 코드스멜을 풍기는 부분들을 지적해준다면!!. 다른 리뷰어가 봤을 때는 보다 주요 로직에 대해서 피드백을 받을 수 있겠죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 우리가 활용할 수 있는 오픈소스 SonarQube 소개드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;SonarQube 란?&lt;/h1&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;2.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;627&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbD4H1/btso6Na8uV9/Ryv53OGgKCuPmgynesk7x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbD4H1/btso6Na8uV9/Ryv53OGgKCuPmgynesk7x0/img.png&quot; data-alt=&quot;https://techblog.tabling.co.kr/%EA%B8%B0%EC%88%A0%EA%B3%B5%EC%9C%A0-%EC%A0%95%EC%A0%81-%EC%BD%94%EB%93%9C-%EB%B6%84%EC%84%9D-sonarqube-6b59fa9b6b85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbD4H1/btso6Na8uV9/Ryv53OGgKCuPmgynesk7x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbD4H1%2Fbtso6Na8uV9%2FRyv53OGgKCuPmgynesk7x0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1220&quot; height=&quot;627&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;627&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://techblog.tabling.co.kr/%EA%B8%B0%EC%88%A0%EA%B3%B5%EC%9C%A0-%EC%A0%95%EC%A0%81-%EC%BD%94%EB%93%9C-%EB%B6%84%EC%84%9D-sonarqube-6b59fa9b6b85&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;SonarQube 는 버그, 코드 악취와 보안 취약점을 감지하기 위한 정적 분석 도구입니다. 20개 이상의 프로그래밍 언어를 지원하며, Github Action 과 결합하여 자동 리뷰를 수행할 수 있는 오픈 소스입니다. 소나큐브는 중복코드, 코딩 표준, 유닛테스트, 코드 커버리지, 코드 복잡도, 주석, 버그, 보안 취약점의 보고서를 제공해줍니다.&lt;/p&gt;
&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;Linux, Window, Mac 등 다양한 환경에서 모두 구동이 가능합니다.&lt;/li&gt;
&lt;li&gt;20개가 넘는 프로그램 언어에 대한 코드 분석을 지원합니다.&lt;/li&gt;
&lt;li&gt;개발된 코드의 품질을 어드민을 통해 확인이 가능하며 지속적 관리가 가능합니다.&lt;/li&gt;
&lt;li&gt;품질 게이트를 통해 표준화된 코드 품질 요구사항을 설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SonarQube 동작 원리&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmP7NW/btso1tZdO9G/ovlaocsjPqt9Yi7qDBf7g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmP7NW/btso1tZdO9G/ovlaocsjPqt9Yi7qDBf7g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmP7NW/btso1tZdO9G/ovlaocsjPqt9Yi7qDBf7g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmP7NW%2Fbtso1tZdO9G%2FovlaocsjPqt9Yi7qDBf7g1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;841&quot; height=&quot;416&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소나큐브는 크게 Scanner 와 SonarQube Server 로 구분해서 생각하면 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;homebrew , wget 등 sonarqube scanner 오픈소스를 다운로드 받습니다. 그리고 이 Scanner 를 통해서 우리의 프로젝트를 분석하고 결과파일을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 결과파일을 HTTP 프로토콜을 통해 SonarQube Server 로 전송합니다. SonarQube Server 는 대시보드 형태로 분석 결과를 도표화하여 시각적으로 우리에게 보여줍니다.&lt;/p&gt;
&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;(저는 소나큐브 버전 6.7 기준으로 테스트를 진행하였습니다.)&lt;/p&gt;
&lt;p data-ke-size=&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;SonarQube Project Dashboard&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;4.png&quot; data-origin-width=&quot;1004&quot; data-origin-height=&quot;861&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdVnI1/btso6kAe0qb/dwVODI8ASKdR5OU1UErePk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdVnI1/btso6kAe0qb/dwVODI8ASKdR5OU1UErePk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdVnI1/btso6kAe0qb/dwVODI8ASKdR5OU1UErePk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdVnI1%2Fbtso6kAe0qb%2FdwVODI8ASKdR5OU1UErePk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1004&quot; height=&quot;861&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1004&quot; data-origin-height=&quot;861&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;h3 data-ke-size=&quot;size23&quot;&gt;Bugs (버그)&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 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;5.png&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFGqZF/btso6RR03Uo/KzMWC6sHttW4buqygRJ85k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFGqZF/btso6RR03Uo/KzMWC6sHttW4buqygRJ85k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFGqZF/btso6RR03Uo/KzMWC6sHttW4buqygRJ85k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFGqZF%2Fbtso6RR03Uo%2FKzMWC6sHttW4buqygRJ85k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;945&quot; height=&quot;595&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;595&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;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public Optional&amp;lt;File&amp;gt; convertMultipartFileToFile(MultipartFile file) throws Exception {
    File convertFile = new File(file.getOriginalFilename());
    if (convertFile.createNewFile()) {
        try {
            FileOutputStream fos = new FileOutputStream(convertFile);
            fos.write(file.getBytes());
        } catch (IOException ioe) {
            throw new Exception(ioe);
        }
        return Optional.of(convertFile);
    }
    return Optional.empty();
}
&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;위가 해당하는 소스 부분인데, 제가 FileOutputStream 을 사용하면서 fos.close(); 를 누락한 것이 보이네요 그리고 file.getOriginalFilename() 에 대해서는 null 처리를 하지 않았군요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 누수에 대한 위험도 야기될 수 있는 뿐만 아니라 null 정보가 들어왔을 때의 RunTimeException 도 야기될 수 있겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code Smells (코드스멜)&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;6.png&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvm8zH/btso4yZ7IgZ/i2C5qFwlxH1T88pVDKraAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvm8zH/btso4yZ7IgZ/i2C5qFwlxH1T88pVDKraAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvm8zH/btso4yZ7IgZ/i2C5qFwlxH1T88pVDKraAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdvm8zH%2Fbtso4yZ7IgZ%2Fi2C5qFwlxH1T88pVDKraAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1012&quot; height=&quot;522&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;522&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;Rename this package name to match the regular expression '^[a-z]+(\.[a-z][a-z0-9])$'.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Querydsl 을 사용하다보니, QClass file 이 생겼네요. 패키지 이름에 대해서도 네이밍을 변경하길 권장하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Rename this method name to match the regular expression '^[a-z][a-zA-Z0-9]*$'&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;**public** **static** Attachment Of(UsageType usageType, FileType fileType, 이런 메서드와 같이 대문자 OF 같은 부분도 지적해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Add a private constructor to hide the implicit public one.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래 코드 예시처럼 Dto 를 사용하기 위해 생성자를 명시하지 않은 경우도 CodeSmell 로 알려주고 있습니다. 하나의 클래스 안에는 private 생성자를 만들기를 권장하네요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;public class AttachmentCommand {

    @Getter
    @Builder
    public static class Upload {
        private MultipartFile multipartFile;
        private UsageType usageType;
        private String uuid;
        private FileType fileType;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 동작하는데는 지장이 없을수도 있으나 개발 관행상 좋지 않은 부분, 지적하기 애매한 부분을 소나큐브가 알려줍니다. PR 을 올리기 전에 이러한 부분들을 미리 볼 수 있고, 이부분들을 왜 권장하는지 알아보면서 향기로운 코드를 짜볼 수 있는 경험을 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;Coverage (코드커버리지)&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;높은 코드 커버리지는 테스트를 통해 코드의 신뢰성과 안정성을 높일 수 있습니다.&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;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Duplications (중복코드)&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;7.png&quot; data-origin-width=&quot;1013&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G6NPw/btso6pIeroy/wAyVymN3RB3HCrODEh2Wg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G6NPw/btso6pIeroy/wAyVymN3RB3HCrODEh2Wg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G6NPw/btso6pIeroy/wAyVymN3RB3HCrODEh2Wg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG6NPw%2Fbtso6pIeroy%2FwAyVymN3RB3HCrODEh2Wg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1013&quot; height=&quot;618&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1013&quot; data-origin-height=&quot;618&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;pre class=&quot;groovy&quot;&gt;&lt;code&gt;public class ParentDto {
    @Getter
    @NoArgsConstructor
    public static class ChangeHighSchoolRequest {
        private String highSchool;
    }
    @Getter
    @NoArgsConstructor
    public static class ChangeYearRequest {
        private Integer year;
    }
    @Getter
    @Builder
    public static class Response {
        private final Boolean isOk;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 프로젝트 같은 경우 Dto 를 도메인별로 만들어둔 경우가 있는데, 이러한 부분들이 중복된 코드로서 많이 발견이 되네요&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;h1&gt;소나큐브 Server 설치&lt;/h1&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;본 글에서는 Sonarqube server 에 대한 설치를 중점으로 다루지 않고, 간략하게만 안내해드리겠습니다.&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;SonarQube Server 는 기본으로 요청하는 서버 기본사양이 있습니다.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최소 2GB / 1 vcpu 용량의 서버&lt;/li&gt;
&lt;li&gt;PostgreSQL 버전 9.3 이상&lt;/li&gt;
&lt;li&gt;OpenJDK 11 또는 JRE 11&lt;/li&gt;
&lt;li&gt;모든 SonarQube프로세스는 sonar 사용자로 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Linux 설정 (최소 사양)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fm.max_map_count : 524288&lt;/li&gt;
&lt;li&gt;fs.file-max : 131072&lt;/li&gt;
&lt;li&gt;파일 디스크립터 개수 : 131072&lt;/li&gt;
&lt;li&gt;스레드 개수 : 8192&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;서버의 최소 사양 확인 명령어&lt;/h3&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# vm max 값 확인
sysctl vm.max_map_count

# 리눅스 최대 열 수 있는 파일 갯수 확인
sysctl fs.file-max

# 리눅스 파일 열기 제한 찾기
ulimit -n

# 리눅스 
ulimit -u
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서버 사양 변경 방법&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sysctl -w vm.max_map_count=524288
sysctl -w fs.file-max=131072
ulimit -n 131072
ulimit -u 8192
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EC2 도커 설치&lt;/h2&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# EC2 console, docker 설치 및 권한설정
sudo apt-get update &amp;amp;&amp;amp; \\
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common &amp;amp;&amp;amp; \\
curl -fsSL &amp;lt;https://download.docker.com/linux/ubuntu/gpg&amp;gt; | sudo apt-key add - &amp;amp;&amp;amp; \\
sudo apt-key fingerprint 0EBFCD88 &amp;amp;&amp;amp; \\
sudo add-apt-repository &quot;deb [arch=amd64] &amp;lt;https://download.docker.com/linux/ubuntu&amp;gt; $(lsb_release -cs) stable&quot; &amp;amp;&amp;amp; \\
sudo apt-get update &amp;amp;&amp;amp; \\
sudo apt-get install -y docker-ce &amp;amp;&amp;amp; \\
sudo usermod -aG docker ubuntu &amp;amp;&amp;amp; \\
sudo curl -L &quot;&amp;lt;https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$&amp;gt;(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose &amp;amp;&amp;amp; \\
sudo chmod +x /usr/local/bin/docker-compose &amp;amp;&amp;amp; \\
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;도커 컴포즈문 생성&lt;/h2&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# docker-compose.yml
version: &quot;3&quot;

services:
  sonarqube:
    image: sonarqube:community
    depends_on:
      - db
    environment: # 소나큐브 DB 연결 설정
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
    ports:
    	# 소나큐브의 defaulut 포트는 9000입니다. http 기본 포트로 접속하기위해 들어가면 화면을 보이게 하기 위해 호스트 포트는 80으로 설정
      - &quot;80:9000&quot; 
  db: #postgres DB 설치
    image: postgres:12
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - postgresql:/var/lib/postgresql
      - postgresql_data:/var/lib/postgresql/data

volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql:
  postgresql_data:
&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;h1&gt;Sonar Scanner 사용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소나큐브 설치는 공식문서(&lt;a href=&quot;https://docs.sonarsource.com/sonarqube/latest/&quot;&gt;https://docs.sonarsource.com/sonarqube/latest/&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;공식 웹사이트에서 설치파일 다운로드하기 (&lt;a href=&quot;https://docs.sonarsource.com/sonarqube/latest/analyzing-source-code/scanners/sonarscanner/&quot;&gt;https://docs.sonarsource.com/sonarqube/latest/analyzing-source-code/scanners/sonarscanner/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Mac 인 경우 homebrew 로 sonar scanner 설치
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;brew install sonar-scanner&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;docker 로 sonar-scanner 를 띄워 활용하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker run sonarsource/sonar-scanner-cli&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sonarqube 대시보드에서 sonar-token 발행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소나큐브 설치가 완료되면, 최초 로그인 계정으로 admin/admin 으로 계정이 제공됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 소나큐브에서 사용할 sonar-token 을 발급받아야 하는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sonarqube 대시보드의 맨 오른쪽 상단에 계정을 클릭하고, MyAccoutn 라는 메뉴를 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjCp6R/btso6L5rC2Z/DN9n0FJ81vHdDViCAYae90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjCp6R/btso6L5rC2Z/DN9n0FJ81vHdDViCAYae90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjCp6R/btso6L5rC2Z/DN9n0FJ81vHdDViCAYae90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjCp6R%2Fbtso6L5rC2Z%2FDN9n0FJ81vHdDViCAYae90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;452&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 Security 탭에서 우리가 사용하고자 하는 Sonar-token 을 발행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;PHP, Golang 등 Language 로 Sonnar scanner 적용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 Sonar Scanner 라이브러리를 설치완료하였다면 이제 우리가 분석하고자 하는 프로젝트를 스캔해볼 차례입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b08udm/btsoZ0J0tHY/RgeGftnKzqfUHnNR1Dkh01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b08udm/btsoZ0J0tHY/RgeGftnKzqfUHnNR1Dkh01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b08udm/btsoZ0J0tHY/RgeGftnKzqfUHnNR1Dkh01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb08udm%2FbtsoZ0J0tHY%2FRgeGftnKzqfUHnNR1Dkh01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;252&quot; height=&quot;312&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sonar scanner 를 테스트하기 위해 간략한 golang 프로젝트를 만들어봤는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sonar-scanner 는 프로젝트의 루트 위치에서 &lt;a href=&quot;http://sonar-project.properties&quot;&gt;sonar-project.properties&lt;/a&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;sonar-project.properties&lt;/h3&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;sonar.projectKey=sonar-golang
sonar.projectName=golang-002
sonar.sources=.
sonar.exclusions=**/*_test.go,**/vendor/**,**/testdata/*
sonar.tests=.
sonar.test.inclusions=**/*_test.go
sonar.test.exclusions=**/vendor/**
sonar.go.coverage.reportPaths=./coverage.out
&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;sonar.projectKey : 프로젝트를 식별하기 위한 고유 키&lt;/li&gt;
&lt;li&gt;sonar.projectName : 프로젝트 대시보드에서 명시되는 프로젝트 이름&lt;/li&gt;
&lt;li&gt;sonar.sources : sonnar scanner 가 분석하고자 하는 소스 위치&lt;/li&gt;
&lt;li&gt;sonar.exclusions : 분석에서 제외할 파일&lt;/li&gt;
&lt;li&gt;sonar.tests : 테스트 소스 코드의 경로&lt;/li&gt;
&lt;li&gt;sonar.test.inclusions : 분석에 포함할 테스트 파일 패턴&lt;/li&gt;
&lt;li&gt;sonar.test.exclusions : 분석에서 제외할 테스트 파일 패턴&lt;/li&gt;
&lt;li&gt;sonar.go.coverage.reportPaths : 코드 커버리지 보고서의 경로&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 properties 에는 우리에게 중요한 sonar server 의 호스트 주소와 토큰에 대한 정보가 빠져있습니다. 이는 소스트리에 올라가지 않게 환경변수로 지정하여 사용할 수 있게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 Docker 명령어를 통해 sonnar-scanner 를 활용한 CMD 입니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;docker run --rm \\
    --network host \\
    -e SONAR_HOST_URL=&quot;&amp;lt;http://sona-dev.kollus.com&amp;gt;&quot; \\
    -e SONAR_LOGIN=&quot;ddddddd&quot; \\
    -v &quot;${PWD}:/usr/src&quot; \\
    sonarsource/sonar-scanner-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sonnar scanner 가 성공적으로 적용되면 아래와 같은 로그가 찍히면서 sonar-server 로 요청을 전송합니다.&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;10.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ezfQ8V/btso6qAnyUs/C6nxFarUQHyS7kKOoYrBkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ezfQ8V/btso6qAnyUs/C6nxFarUQHyS7kKOoYrBkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ezfQ8V/btso6qAnyUs/C6nxFarUQHyS7kKOoYrBkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FezfQ8V%2Fbtso6qAnyUs%2FC6nxFarUQHyS7kKOoYrBkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1016&quot; height=&quot;312&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;997&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAxbNw/btso1uqgpPE/9VeO9OQoVYahil0mPqJE30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAxbNw/btso1uqgpPE/9VeO9OQoVYahil0mPqJE30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAxbNw/btso1uqgpPE/9VeO9OQoVYahil0mPqJE30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAxbNw%2Fbtso1uqgpPE%2F9VeO9OQoVYahil0mPqJE30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;997&quot; height=&quot;157&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;997&quot; data-origin-height=&quot;157&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;Gradle&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 다음으로 Gradle 프로젝트를 scan 하는 실습을 진행해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 제가 테스트하는 sonarqube-server 는 java 8 을 지원하기 때문에 java 8 version 으로 구성하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OHlEI/btso0uqCeUd/FnisUMZDUXKUq18UXu0Iw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OHlEI/btso0uqCeUd/FnisUMZDUXKUq18UXu0Iw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OHlEI/btso0uqCeUd/FnisUMZDUXKUq18UXu0Iw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOHlEI%2Fbtso0uqCeUd%2FFnisUMZDUXKUq18UXu0Iw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;564&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;515&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;build.gradle&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;plugins {
    id 'org.springframework.boot' version '2.7.13'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    id &quot;org.sonarqube&quot; version &quot;3.3&quot;
    id 'java'
    id 'jacoco'
}

group 'net.catenoid'
version '1.0-SNAPSHOT'
sourceCompatibility = '8'

repositories {
    mavenCentral()
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

dependencies {

    /** spring **/
    implementation 'org.springframework.boot:spring-boot-starter-web'
    annotationProcessor &quot;org.springframework.boot:spring-boot-configuration-processor&quot;
    developmentOnly 'org.springframework.boot:spring-boot-devtools'

    /** lombok **/
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    /** DB **/
    runtimeOnly 'com.h2database:h2'

    /** TEST **/
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'

    /** sonar qube **/
    implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
}

test {
    useJUnitPlatform()
    finalizedBy 'jacocoTestReport'
}

jacoco {
    toolVersion = &quot;0.8.7&quot;
}

jacocoTestReport {
    reports {
        xml.required = true
        csv.required = false
        html.required = false
    }
}

sonarqube {
    properties {
        property &quot;sonar.sources&quot;, &quot;src&quot;
        property &quot;sonar.language&quot;, &quot;java&quot;
        property &quot;sonar.sourceEncoding&quot;, &quot;UTF-8&quot;
        property &quot;sonar.java.binaries&quot;, &quot;${buildDir}/classes&quot;
        property &quot;sonar.test.inclusions&quot;, &quot;**/*Test.java&quot;
        property &quot;sonar.exclusions&quot;, &quot;**/resources/static/**, **/Q*.class, **/test/**&quot;
        property &quot;sonar.coverage.jacoco.xmlReportPaths&quot;, &quot;${buildDir}/reports/jacoco/test/jacocoTestReport.xml&quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle 을 보니, 위의 &lt;a href=&quot;http://sonner-project.properties&quot;&gt;sonner-project.properties&lt;/a&gt; 에 명시되었던 정보가 들어는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;./gradlew sonar \\
  -Dsonar.projectKey=sonarqube-java \\
  -Dsonar.projectName=sonarqube-java-001 \\
  -Dsonar.host.url=http://sona-dev.com \\
  -Dsonar.token=ddddd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 gradle 명령어를 실행하면 golang 프로젝트와 마찬가지로 데이터를 전송할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DFugM/btso6RLfPQc/jjalz75rPB8qDvACQ0hLuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DFugM/btso6RLfPQc/jjalz75rPB8qDvACQ0hLuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DFugM/btso6RLfPQc/jjalz75rPB8qDvACQ0hLuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDFugM%2Fbtso6RLfPQc%2Fjjalz75rPB8qDvACQ0hLuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1009&quot; height=&quot;159&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;159&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;h1&gt;Sonarqube Github Action 적용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Sonarqube 를 Github Action 으로도 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애초에 우리가 개발을 진행할 때마다 일일이 sonar-scanner 명령어를 사용할 수 없기 때문에 GithubAction 을 통해 자동화를 진행할 수 있습니다.&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;15.png&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/48ARI/btso4yy03WO/Q2biFrVUPyfJF8kyPfCkb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/48ARI/btso4yy03WO/Q2biFrVUPyfJF8kyPfCkb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/48ARI/btso4yy03WO/Q2biFrVUPyfJF8kyPfCkb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F48ARI%2Fbtso4yy03WO%2FQ2biFrVUPyfJF8kyPfCkb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;999&quot; height=&quot;452&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;452&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;실제 깃허브에서도 Github Actions 을 만들 때 sonarqube 에 대한 템플릿을 제공하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SonarCloud 는 sonarqube-server 를 우리가 아닌, sonar 회사에서 만든 클라우드 플랫폼과 연동하는 것을 말하며, SonarQube 는 자체적으로 sonarqube-server 를 만들었을 때 사용합니다.&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;Golang, PHP 등 일반적 Language&lt;/h2&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;name: SonarQube analysis

on:
  pull_request:
    branches: [ &quot;main&quot;, &quot;develop&quot; ]
    types: [opened, synchronize]

jobs:
  Analysis:
    runs-on: ubuntu-latest

    steps:
      - name: Analyze with SonarQube
        uses: SonarSource/sonarqube-scan-action@7295e71c9583053f5bf40e9d4068a0c974603ec8
        env:
          SONAR_PROJECT_KEY: sonar-golang
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}   # Generate a token on SonarQube, add it to the secrets of this repo with the name SONAR_TOKEN (Settings &amp;gt; Secrets &amp;gt; Actions &amp;gt; add new repository secret)
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}   # add the URL of your instance to the secrets of this repo with the name SONAR_HOST_URL (Settings &amp;gt; Secrets &amp;gt; Actions &amp;gt; add new repository secret)
          PR_NUMBER: ${{ github.event.pull_request.number }}
        with:
          args:
            -Dsonar.projectKey=sonar-golang
            -Dsonar.projectName=${{ env.SONAR_PROJECT_KEY }}-${{ env.PR_NUMBER }}
&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;위의 깃헙 액션 yaml 파일을 보면, PR 이 opened , synchronize 될 때 동작하게 되어 있습니다.&lt;/li&gt;
&lt;li&gt;우리가 개발을 완료하고, 코드리뷰를 위해 PR을 생성하는 순간과, 코드 수정하고 Push 하는 순간에 위의 이벤트를 실행하도록 구성하였습니다.&lt;/li&gt;
&lt;li&gt;sonar_project_key 는 고정으로 설정되어있고, projectName 은 PR_NUMBER 와의 조합을 통해 해당 분석이 어느 PR 에 대한 분석인지 알 수 있습니다.&lt;/li&gt;
&lt;li&gt;위의 GITHUB_TOKEN 은 우리가 따로 셋팅할 필요 없이 깃허브에서 자체적으로 환경변수 값을 넣어줍니다.&lt;/li&gt;
&lt;li&gt;SONAR_TOKEN 과 SONAR_HOST_URL 은 Repository &amp;rarr; Settings &amp;rarr; Secrets and variables &amp;rarr; Actions 메뉴에서 설정할 수 있습니다.&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;16.png&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lAdeY/btso0tSQZV4/v8vWn5EDvOt5cZr5kV4Nik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lAdeY/btso0tSQZV4/v8vWn5EDvOt5cZr5kV4Nik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lAdeY/btso0tSQZV4/v8vWn5EDvOt5cZr5kV4Nik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlAdeY%2Fbtso0tSQZV4%2Fv8vWn5EDvOt5cZr5kV4Nik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;878&quot; height=&quot;724&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;724&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;Gradle Project Github Action&lt;/h2&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;name: sonarqube analytics

# 하기 내용에 해당하는 이벤트 발생 시 github action 동작
on:
  pull_request: # 모든 브랜치에서 PR이 일어났을 때 github action 동작
    branches: [ &quot;main&quot;, &quot;develop&quot; ]
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-22.04

    steps:
      - name: Checkout source code
        uses: actions/checkout@v2
  analysis:
    runs-on: ubuntu-22.04
    env:
      SONARQUBE_PROJECT_KEY: jungin
      SONARQUBE_URL: ${{secrets.SONAR_HOST_URL}}
      SONARQUBE_TOKEN: ${{secrets.SONAR_TOKEN}}
      PR_NUMBER: ${{github.event.pull_request.number}}
    steps:
      # 소스코드 체크아웃 수행
      - name: Checkout source code
        uses: actions/checkout@v2

      # Gralde 의 Scanner 발동, 위의 env 에서 선언한 환경변수와 함께 발동
      - name: Sonaqube Analysis
        run: ./gradlew test sonarqube
          -Dsonar.host.url=${{ env.SONARQUBE_URL }}
          -Dsonar.projectKey=${{ env.SONARQUBE_PROJECT_KEY }}
          -Dsonar.projectName=${{ env.SONARQUBE_PROJECT_KEY }}-${{ env.PR_NUMBER }}
          -Dsonar.login=${{ env.SONARQUBE_TOKEN }}
&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;Gradle 프로젝트 같은 경우 GithubAction 의 sonarqube-scan-action 을 활용하지 않고 gradlew 명령어를 통해 sonar-scan 을 진행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&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;a href=&quot;https://m.blog.naver.com/2883060/221400454152&quot;&gt;https://m.blog.naver.com/2883060/221400454152&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://moseskim.github.io/sonarqube/2016/01/29/sq-5_3_release.html&quot;&gt;http://moseskim.github.io/sonarqube/2016/01/29/sq-5_3_release.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Architecture</category>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/256</guid>
      <comments>https://catchdream.tistory.com/256#entry256comment</comments>
      <pubDate>Wed, 26 Jul 2023 19:03:56 +0900</pubDate>
    </item>
    <item>
      <title>글또 8기에 대한 회고</title>
      <link>https://catchdream.tistory.com/255</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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;story.png&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq41Dq/btsnHSSu1ni/ZtKOzRtJNGw20Hosrmf9N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq41Dq/btsnHSSu1ni/ZtKOzRtJNGw20Hosrmf9N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq41Dq/btsnHSSu1ni/ZtKOzRtJNGw20Hosrmf9N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq41Dq%2FbtsnHSSu1ni%2FZtKOzRtJNGw20Hosrmf9N0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;317&quot; data-filename=&quot;story.png&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;317&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;회고에 대해 작성하는 것은 어색하다. 오늘까지만 하더라도 sonarqube , 동시성 등 어떤 주제로 글을 써야하지 고민을 하다, 오늘이 마지막 글또 제출임을 알게되어 회고록을 뒤늦게 작성하게 되었다. 2023.02.08 일에 글또 8기 다짐글 (&lt;a href=&quot;https://catchdream.tistory.com/247&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://catchdream.tistory.com/247&lt;/a&gt;) 을 쓴지가 벌써 6개월이 지나다니.. 시간이 정말 빠르게 흐르는구나를 느끼기도 하면서 나 자신에 대해 반성하게 되는 순간들도 많았다.&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;h2 data-ke-size=&quot;size26&quot;&gt;8기 글또의 시작과 작성한 글들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 글또 다짐글은 &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;i&gt;정말 그게 최선이었을까?&lt;/i&gt;&lt;/span&gt;&lt;/b&gt; 로 시작한다. 간단한 API 기능 구현 하나라도, 관성적으로 개발을 진행하지는 않았는지, 내가 선택한 기술이 정말 최선의 선택이었는지 그에 대한 side-effect 는 고려하였는지 등 이때까지의 마음가짐을 되돌아보면서 다짐했다. 그리고 이런 구체적인 노력으로 기술에 대한 글을 작성하면서 업무와 공부가 어우러지는 선순환 구조를 기대하였다.&lt;/p&gt;
&lt;p data-ke-size=&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;그리고 나는 8기의 글또를 진행하면서 아래의 글들을 작성하였다.&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;a href=&quot;https://catchdream.tistory.com/247&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;글또 8기 다짐글&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://catchdream.tistory.com/248&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;분산트랜스코딩으로 알아보는 Boss-Worker 아키텍처&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://catchdream.tistory.com/249&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;대규모 채팅 아키텍처는 C10K 문제를 어떻게 해결할까?&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://catchdream.tistory.com/250&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;FFmpeg 으로 h.264, h.265 Encoding 성능 테스트&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://catchdream.tistory.com/251&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;영상의 Quality 와 av1 Encoding 성능테스트&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://catchdream.tistory.com/252&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Golang 2차원 배열 문제를 통한 array, slice 의 개념정리&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://medium.com/@arneg0shua/av1-h-265-트랜스코딩-테스트-ed9e54906358&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AV1, H.265 트랜스코딩 테스트&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://medium.com/@arneg0shua/애플리케이션-로그에-장애-흔적이-없다면-vi-활용편-d4e82c171e6f&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;애플리케이션 로그에 장애 흔적이 없다면?(+VI 활용편)&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://catchdream.tistory.com/254&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Golang 에서 Map 과 같은 참조타입을 다룰 때 주의해야할 점&lt;/a&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;KPT (Keep, Problem, Try)&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 href=&quot;https://zzsza.github.io/diary/2023/06/05/how-to-retrospect/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://zzsza.github.io/diary/2023/06/05/how-to-retrospect/&lt;/a&gt;) 중 KPT 방법을 통해 그간 잘해왔던 점과 문제점, 그리고 앞으로 시도해볼 점을 생각해보았다.&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;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;오히려 2주에 하나의 글이라는 주기가 생기면서, 소소한 주제들을 그때그때 빠르게 적기보다는 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;h4 data-ke-size=&quot;size20&quot;&gt;테스트 결과를 기준으로 한 글을 써보았던 경험&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 개발에 대한 생각과 그에 대한 근거를 Reference 나 다른 곳에서 내용을 찾는 과정만 진행했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 테스트와 샘플코드도 다른 사람의 자료를 바탕으로 이해하고, 실제 업무에 적용하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이번 글쓰기를 통해, 실제 글을 쓰기 위한 테스트를 진행해보기도 하고, 간단한 샘플코드도 작성해보는 경험을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책 또는 블로그에 쓰여진 내용이 정말로 맞는지 내가 실제로 코드를 짜보고 성능을 테스트해보는 경험을 하고, 이를 글로 정리한 부분은 나에게 커다란 도움이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 생각에 대한 검증이 필요할 때 그에 대한 레퍼런스를 찾는 노력도 중요하지만 직접 테스트를 해보고, 그에 대한 바탕으로 논리를 펼쳐나가는 것도 중요한 능력이라 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어려움을 느껴서 개선하고 싶은 것??&lt;/h3&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;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 아직 업무를 진행하면서 10년 뒤의 나의 모습, 10년차의 개발자로서의 나의 모습, 나는 어떤 역량을 가진 개발자일까에 대한 생각은 아직 확립이 되어있지는 않지만 나의 색깔을 녹여낼 수 있는 글들을 많이 써보고 싶다는 희망이 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구체적으로 시도할 내용&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;꾸준한 글쓰기와 레퍼런스 참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글또 9기에 다시 참여할 수 있다면 나는 다시 재참여를 하고 싶다. 2주정도의 주기를 통해 보다 퀄리티 있는 글쓰기 연습이 도움이 되고 있기 때문이다. 다만 내가 집중하고 싶은 것은 보다 깊이 알기이다. 하나의 주제에 대해 몇개 정도의 블로그 글들만 쭉 살펴보고, 한 권의 책정도만 살펴보는 정도가 아니라 깊이 탐구하고 연구해보는 습관을 길러보고 싶다. 그렇기 위해서는 내가 좋아하는 주제를 선정해야할 것이다.&amp;nbsp;나는 앞으로 어떤 개발자가 될 것인가. 어떤 것을 잘하는 개발자가 될 것인가에 대해 이번 글또 9기 전까지 탐구해보고 고민해볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 스프링을 잘하는 개발자, 나는 golang 을 잘하는 개발자 처럼 어떤 Langage 와 Tool 을 잘 활용하는 능력도 중요하지만 Language 와 Tool 을 잘쓰는 것만으로는 비즈니스 가치를 만들어낼 수는 없다. 내가 주로 어떤 도메인에 종사하고 싶은 건지, 그 도메인에서는 어떤 기술이 비즈니스 가치를 창출하는지, 다른 경쟁사들의 기술적 수준은 어떠한지 등 이러한 고민과 생각을 확립해나가고 싶다. 그리고 나에게 적합한 도메인을 찾았을 때, 그에 대한 전문성을 함양하고 싶다.&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;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;개발자로서의 나는 현재까지는 성장하고 있는 중이라 생각된다. 1년 전, 한달 전과 비교했을 때 분명 어제의 나보다는 오늘의 나가 보다 명확한 개발을 하고 있기 때문이다. 이러한 성장은 내가 앞으로 가야하는 방향과 이정표가 확실했을 때 가능하다고 생각한다. 내가 나아가야할 방향을 잃어버리면 우리는 헤메게 되고, 눈앞의 업무만 처리하는 관성적인 개발을 진행할 수 있기 때문이다. 그렇기 때문에 지금 회고하는 나 자신의 고민을 게속해서 고민하며 그에 대한 답을 내리는 노력을 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&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;&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>Essay</category>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/255</guid>
      <comments>https://catchdream.tistory.com/255#entry255comment</comments>
      <pubDate>Sun, 16 Jul 2023 18:56:57 +0900</pubDate>
    </item>
    <item>
      <title>Golang 2차원 배열 문제를 통한 array, slice 의 개념정리</title>
      <link>https://catchdream.tistory.com/252</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;791&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVS2gl/btsdZO0FQxv/oj1MXqX6kCjdRh6k8zwgK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVS2gl/btsdZO0FQxv/oj1MXqX6kCjdRh6k8zwgK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVS2gl/btsdZO0FQxv/oj1MXqX6kCjdRh6k8zwgK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVS2gl%2FbtsdZO0FQxv%2Foj1MXqX6kCjdRh6k8zwgK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;791&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;791&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: #f89009;&quot;&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Slice&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Make&lt;/b&gt;&lt;/span&gt; 이다. 해당 문제에서 하나의 배열을 2차원으로 만드는 방법으로는 슬라이스의 특징인 &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;정답을 반환하는 answer 2차원 배열을 만들고, param으로 주어지는 num_list 1차원 배열을 특정 길이만큼 잘라서 할당하는 풀이입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 위의 세 키워드 Array, Slice, Make 에 대해 개념을 정리해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;Array&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;Golang 에서 배열은 동일한 데이터 타입의 고정 크기 요소들의 순서화된 집합이다.&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;var myArray [5] int&lt;/li&gt;
&lt;li&gt;myArray := [5]int{1,2,3,4,5}&lt;/li&gt;
&lt;li&gt;myArray := [&amp;hellip;]int{6,7,8,9,10}
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열의 요소 개수 만큼 크기가 지정된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배열에서 값을 할당할 때 index 와 value 값을 대입하는 방법으로 할당한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;myArray[0] = 1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배열의 크기는 선언 시에 결정되며, 변경할 수 없다. 즉, 배열의 크기가 고정되어 있으므로 요소를 추가하거나 제거할 수 없다.&lt;/li&gt;
&lt;li&gt;따라서 배열은 선언하자마자 모든 인덱스는 초기값을 가지게 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;var myArray [5]int &amp;rarr; [0 0 0 0 0 ]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Slice&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;Golang 에서의 슬라이스는 배열을 기반으로 한 동적 크기의 배열이다.&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;var mySlice []int&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;var mySlice []int &amp;rarr; []&lt;/li&gt;
&lt;li&gt;따라서, 슬라이스의 초기값을 지정하기 않고 선언만 하면 Nil slice 가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;슬라이스는 값을 대입할 때 요소에 값을 할당하는 방법과 append 함수를 사용하여 새로운 요소를 추가하는 방법이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mySlice[0] = 5&lt;/li&gt;
&lt;li&gt;mySlice = append(mySlice, 5)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;슬라이스는 배열의 일부분을 슬라이스로 생성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;myArray := [5]int{1,2,3,4,5}
mySlice := myArray[1:4]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Slice 추가, 병합, 복사&lt;/h2&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;h4 data-ke-size=&quot;size20&quot;&gt;append (추가, 병합)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;append() 는 Golang 의 내장된 함수 (builtin.go) 이다.&lt;/li&gt;
&lt;li&gt;슬라이스는 append() 함수를 이용해서 데이터를 추가할 수 있다.&lt;/li&gt;
&lt;li&gt;용량 내에서는 길이를 변경하여 데이터를 추가하며, 용량이 초과하는 경우에는 설정한 용량만큼 새로운 배열을 생성하고, 기존 배열 값들을 모두 새 배열에 복제한 후 다시 슬라이스를 할당하는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# 슬라이스에 요소를 추가하는 방법

a := []int{1,2,3}
a = append(a, 4)

fmt.Println(a) -&amp;gt; [1 2 3 4]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# 두 슬라이스를 병합하는 방법

a := []int{1,2,3}
b := []int{4,5,6}
a = append(a, b)

fmt.Println(a) -&amp;gt; [1 2 3 4 5 6]
&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;copy (복사)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;copy() 는 Golang 의 내장된 함수 (builtin.go) 이다.&lt;/li&gt;
&lt;li&gt;슬라이스는 copy() 함수를 이용해서 데이터를 복사할 수 있다.&lt;/li&gt;
&lt;li&gt;copy(붙여넣을 슬라이스, 복사할 슬라이스)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a := []int{1,2,3}
b := make([]int, len(a), cap(a)*2)
copy(a, b)

fmt.Println(a) -&amp;gt; [1 2 3 4 5 6]
&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;b의 용량이 a보다 큼으로, copy 를 할 때 a의 요소를 모두 가지게된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a := []int{1,2,3}
b := []int{4,5}
copy(b, a)

fmt.Println(a) -&amp;gt; [1 2]
&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;b의 용량이 a보다 작거나 같을 때, b는 a의 요소들을 덮어씌우게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a := []int{1,2,3}
b := a[1:2]

fmt.Println(b) -&amp;gt; [2]

a := []int{1,2,3}
b := a[:2]

fmt.Println(b) -&amp;gt; [1,2]
&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;슬라이스 := 복사할 슬라이스(복사할 첫 인덱스 : 복사할 마지막 인덱스+1)&lt;/li&gt;
&lt;li&gt;앞의 첫 인덱스가 없을 경우 0부터 복사한다.&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;Make&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;Make 함수는 Golang 의 내장된 함수(builtin.go)로, slice, map, channel 등의 빈(empty) 데이터 구조를 생성하고 초기화할 때 사용됩니다.&lt;/li&gt;
&lt;li&gt;make(T, args)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T는 Make 함수로 생성하고자 하는 데이터 타입을 나타냅니다.&lt;/li&gt;
&lt;li&gt;args 는 데이터 구조를 초기화하기 위한 인자(argument)를 나타냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;s := make([]int, 5, 10)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 코드는 []int 타입의 길이가 5이며, 용량이 10인 슬라이스를 선언한 것을 의미합니다.&lt;/li&gt;
&lt;li&gt;길이가 5로 정해져있으므로, 초기 선언된 슬라이스는 [0 0 0 0 0] 값을 지니고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;s := make(map[string]int, 10)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 코드는 map[string]int 타입의 용량이 10인 슬라이스를 선언한 것을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;2.png&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HJdVD/btsd3fwaZpH/5iei9T4e3kCvzGOP4yQDJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HJdVD/btsd3fwaZpH/5iei9T4e3kCvzGOP4yQDJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HJdVD/btsd3fwaZpH/5iei9T4e3kCvzGOP4yQDJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHJdVD%2Fbtsd3fwaZpH%2F5iei9T4e3kCvzGOP4yQDJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;248&quot; height=&quot;204&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;204&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;h1&gt;Array vs Slice&lt;/h1&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;먼저 Array 와 Slice 에 대해 아래 표와 같이 차이점이 정리되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-05-06 at 10.58.24 AM.png&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blgii6/btsdZP6nMpq/WTu3B1GqytXbPegVtfjTgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blgii6/btsdZP6nMpq/WTu3B1GqytXbPegVtfjTgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blgii6/btsdZP6nMpq/WTu3B1GqytXbPegVtfjTgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblgii6%2FbtsdZP6nMpq%2FWTu3B1GqytXbPegVtfjTgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;869&quot; height=&quot;274&quot; data-filename=&quot;Screen Shot 2023-05-06 at 10.58.24 AM.png&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;224&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;타입&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;위의 표에 보면 배열의 타입은 값(value) 이며, 슬라이스는 참조(reference) 로 되어있다.&lt;/li&gt;
&lt;li&gt;배열은 하나의 값으로 불면의 성격을 가지며, 상수라고도 생각될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a := [3]int{1,2,3}
b := a
b[0] = 4
fmt.Println(a) -&amp;gt; [1 2 3]
fmt.Println(b) -&amp;gt; [4 2 3]
&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;/li&gt;
&lt;li&gt;슬라이스는 다른 변수에 할당하면 원래의 배열을 참조하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;a := []int{1,2,3}
b := a
b[0] = 4
fmt.Println(a) -&amp;gt; [4 2 3]
fmt.Println(b) -&amp;gt; [4 2 3]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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;용량(capacity) 은 데이터 구조가 저장할 수 있는 최대 수를 나타내며, 길이(lencgh) 는 데이터 구조에 저장된 요소의 수를 의미한다.&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;&amp;nbsp;&lt;/h3&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;배열의 타입은 값이며, 슬라이스의 타입은 참조입니다.&lt;/li&gt;
&lt;li&gt;따라서, 동일한 두 배열을 비교할 때는 값 끼리의 비교이므로 true 가 될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a := [3]int{1,2,3}
b := [3]int{1,2,3}

fmt.Println(a == b) -&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그러나 슬라이스는 배열의 참조이기 때문에, 두 슬라이스를 비교하는 것은 불가능하며, 두 슬라이스의 요소들의 일치여부는 비교할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&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;배열은 선언 시부터 배열의 고정된 크기가 정해지기 때문에 컴파일 시점에서 메모리가 할당됩니다.&lt;/li&gt;
&lt;li&gt;슬라이스는 동적으로 크기가 결정되기 때문에 메모리 공간이 달라질 수 있어 런타임 시점에서 메모리가 할당됩니다. 원래 슬라이스의 용량보다 많은 요소가 할당될 경우에는 더 큰 공간을 할당하고 기존 슬라이스의 요소를 복사하는 작업이 진행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&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;a href=&quot;https://go.dev/doc/effective_go&quot;&gt;https://go.dev/doc/effective_go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://edu.goorm.io/learn/lecture/2010/한-눈에-끝내는-고랭-기초/lesson/81406/슬라이스-slice&quot;&gt;https://edu.goorm.io/learn/lecture/2010/한-눈에-끝내는-고랭-기초/lesson/81406/슬라이스-slice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ponyozzang.tistory.com/459&quot;&gt;https://ponyozzang.tistory.com/459&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>GoLang</category>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/252</guid>
      <comments>https://catchdream.tistory.com/252#entry252comment</comments>
      <pubDate>Sat, 6 May 2023 10:56:32 +0900</pubDate>
    </item>
    <item>
      <title>대규모 채팅 아키텍처는 C10K 문제를 어떻게 해결할까?</title>
      <link>https://catchdream.tistory.com/249</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Overview&lt;/h1&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;대용량 채팅은 어떤 것을 의미할까요? 우리가 흔히 쓰는 카카오톡, 라인, 당근과 같이 개인과 개인 사이의 채팅이 아닌, 특정 목적을 위해 모인 불특정 다수가 함께 채팅하는 것을 의미합니다. 유튜브, OTT 의 인기와 더불어 유튜브 Live, 트위치, 라인의 Live, 아프리카 TV 와 같이 실시간 라이브방송으로 고객들과 소통하는 쌍방향 커뮤니케이션 서비스가 흥행하고 있습니다.&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;f1.png&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J36nu/btr3dT5eEJH/atSnlNkx0R0SHktY6poo11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J36nu/btr3dT5eEJH/atSnlNkx0R0SHktY6poo11/img.png&quot; data-alt=&quot;http://m.apparelnews.co.kr/news/news_view/?idx=193741&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J36nu/btr3dT5eEJH/atSnlNkx0R0SHktY6poo11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ36nu%2Fbtr3dT5eEJH%2FatSnlNkx0R0SHktY6poo11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;379&quot; data-filename=&quot;f1.png&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://m.apparelnews.co.kr/news/news_view/?idx=193741&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 라이브 방송으로 인한 대규모 채팅, 수천 또는 수백만 명의 동시접속자가 이루어질 수 있는 환경 이기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 우리는 흔히 &lt;b&gt;C10K&lt;/b&gt; 문제라고 하는 &lt;b&gt;1만개의 클라이언트를 동시에 처리할 수 있는 네트워크 I/O 모델 설계 방법&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;h3 data-ke-size=&quot;size23&quot;&gt;C10K (Concurrently handling 10,000 connections) 의 대표적 해결방법&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;C10K 문제의 대표적인 해결방법은 4가지 정도로 예시를 들 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이벤트 드리븐 아키텍처 (Event-driven architecture) : 이벤트를 발생시키고, 이를 비동기적으로 처리함으로써 성능을 개선합니다.&lt;/li&gt;
&lt;li&gt;멀티스레딩(Multithreading) : 하나의 프로세스 내에서 여러 개의 쓰레드를 생성하여, 동시에 여러 연결을 처리할 수 있도록 합니다.&lt;/li&gt;
&lt;li&gt;멀티 프로세싱(Multiprocessing) : 하나의 서버를 여러 개의 프로세스로 분할하여, 각각의 프로세스가 병렬적으로 연결을 처리하도록 합니다.&lt;/li&gt;
&lt;li&gt;비동기 I/O (Asynchronous I/O) : 입출력 작업을 블로킹하지 않고, 비동기적으로 처리하도록 합니다.&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;크게 4가지의 대표적인 방법이 있지만, 우리가 주목해야할 점은 &lt;i&gt;&lt;b&gt;동기/비동기&lt;/b&gt;&lt;/i&gt;와 &lt;i&gt;&lt;b&gt;단일/멀티&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;/li&gt;
&lt;li&gt;하나의 서버에서 여러 개의 스레드로 분할하여 작업을 각각의 프로세스가 병렬적으로 수행할 수 있는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두가지 주안점을 두고, 채팅 아키텍처에서는 어떻게 이 C10K 문제를 해결했는지, 당근마켓 아키텍처와 Line 의 Live Chatting 아키텍처를 비교해가며 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;당근마켓과 라인 Live 의 아키텍처를 통해 알아보기&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;먼저 당근마켓과 라인 Live 의 아키텍처를 궁금해하실 것 같아 먼저 두 아키텍처를 제시해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;당근마켓의 Chatting-Server 아키텍처&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;f2.png&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHI1L4/btr3bc5Smdy/1nTouJwMUkSzKOEPnnNop0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHI1L4/btr3bc5Smdy/1nTouJwMUkSzKOEPnnNop0/img.png&quot; data-alt=&quot;https://www.youtube.com/watch?v=lCxgddyxDyg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHI1L4/btr3bc5Smdy/1nTouJwMUkSzKOEPnnNop0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHI1L4%2Fbtr3bc5Smdy%2F1nTouJwMUkSzKOEPnnNop0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;973&quot; height=&quot;556&quot; data-filename=&quot;f2.png&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.youtube.com/watch?v=lCxgddyxDyg&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;당근마켓의 Chatting-Server 아키텍처입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년 9월 기준 1,700만명에 달하는 회원 수에 육박하는 당근마켓은 개인과 개인 간의 채팅 서비스를 주로 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 아키텍처로 개선 전 당근마켓은 REST API 를 통해 메시지를 풀링하는 방식이었습니다. 또한 기존에는 FCM 에 의존하고 있어, Push 메시지가 밀리면 유저가 이미 접속하고 있더라도 채팅 메시지를 받지 못하는 구조였습니다.&lt;/p&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 프로토콜 기반인 REST API 방식에서 WebSocket 네트워크 프로토콜로 변경하여 채팅 메시지의 송수신 작업과 FCM 으로 알림을 보내는 작업을 비동기로 방식으로 변경하는데 성공하였습니다.&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;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Question!!&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REST API 를 통해 메시지를 폴링하는 방식&lt;/li&gt;
&lt;li&gt;FCM 에 의존하고 있어 Push 메시지가 밀리면 유저가 채팅 메시지를 받지 못하는 구조&lt;/li&gt;
&lt;li&gt;WebSocket 프로토콜로 변경하면 어떻게 FCM 의 상태와 무관하게 비동기적으로 메시지를 처리할 수 있는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동기방식인 HTTP 프로토콜 vs 비동기 방식인 WebSocket 프로토콜&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 와 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;HTTP 는 클라이언트와 서버 사이에 요청(Request)과 응답(Response) 의 상호작용을 기본적으로 사용하는 프로토콜입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 흔히 들어본 HTTP 의 3-way handshake 과정이 적용되고 있는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간략하게 3-way handshake 의 동작과정을 설명한다면&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트가 서버에게 SYN 패킷을 보냅니다.&lt;/li&gt;
&lt;li&gt;서버는 클라이언트로부터 SYN 패킷을 받으면, 클라이언트로 SYN-ACK 패킷을 보냅니다.&lt;/li&gt;
&lt;li&gt;클라이언트는 서버로부터 SYN-ACK 을 받으면, ACK 패킷을 보내서 서버와의 연결을 확립합니다.&lt;/li&gt;
&lt;/ol&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;Client 에서 Requset 를 Server 에게 보내고, Server 는 Request 를 정상적으로 받았다는 수신 Response 를 보내며, 이를 Client 가 다시 정상적으로 받았을 때 Connection 을 확립하는 것으로 얘기할 수 있습니다. 맺어진 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;HTTP 는 클라이언트가 요청을 보내면, 서버가 응답을 보내는 식으로 이루어지기 때문에 주로 동기(Synchronous) 방식으로 작동하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&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;다시 돌아와서, 그럼 REST API 를 통해 메시지를 폴링한다는 방식은 무엇일까요?&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;h3 data-ke-size=&quot;size23&quot;&gt;Polling 방식&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;f3.png&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YmrQv/btr3gLyFCqb/0V3PKKyHzzwoAjFgJMCwC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YmrQv/btr3gLyFCqb/0V3PKKyHzzwoAjFgJMCwC0/img.png&quot; data-alt=&quot;가상 면접 사례로 배우는 대규모 시스템 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YmrQv/btr3gLyFCqb/0V3PKKyHzzwoAjFgJMCwC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYmrQv%2Fbtr3gLyFCqb%2F0V3PKKyHzzwoAjFgJMCwC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;624&quot; data-filename=&quot;f3.png&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;624&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;Polling 방식은 클라이언트가 사용 가능한 메시지가 있는지 주기적으로 서버에게 묻는 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고객에게 새로운 채팅 메시지가 왔는지 계속해서 서버에게 물어보는 방식이죠. 대부분의 대답이 No 이기 때문에 우리는 전달 받을 채팅 메시지가 없는데도 불구하고 계속해서 네트워크 비용을 소모해야 하는 상황이 발생하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&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;따라서 REST API 를 통해 메시지를 폴링한다는 의미는, 새로운 메시지가 왔는지 FCM (Firebase Cloud Messaging) 을 통해 계속해서 조회한다는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 FCM 은 Push 알림으로도 많이 사용되기 때문에 Push 작업이 밀리게 되면, 채팅 메시지를 조회를 요청했던 Request 는 하염없이 Response 를 기다리는 현상이 발생하게 된 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&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;그럼 WebSocket 은 HTTP와 무엇이 다르길래 비동기방식으로 처리 가능했을까요?&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;h3 data-ke-size=&quot;size23&quot;&gt;WebSocket&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;f4.png&quot; data-origin-width=&quot;529&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCzNwg/btr3ef78oYu/k0KbGnPByX7W5QjFe4xAZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCzNwg/btr3ef78oYu/k0KbGnPByX7W5QjFe4xAZk/img.png&quot; data-alt=&quot;가상 면접 사례로 배우는 대규모 시스템 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCzNwg/btr3ef78oYu/k0KbGnPByX7W5QjFe4xAZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCzNwg%2Fbtr3ef78oYu%2Fk0KbGnPByX7W5QjFe4xAZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;324&quot; data-filename=&quot;f4.png&quot; data-origin-width=&quot;529&quot; data-origin-height=&quot;324&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;웹소켓 (WebSocket) 은 서버가 클라이언트에게 비동기 메시지를 보낼 때 널리 사용하는 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 클라이언트와 서버가 웹소켓을 연결할 때에는 HTTP 연결로 이루어지지만, 연결이 이루어지면 연결된 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;&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;클라이언트는 서버 간에 WebSocket 연결이 설정되면, 이벤트 리스너를 등록하여 서버로부터 메시지를 비동기적으로 수신합니다. 이벤트 리스너는 해당 이벤트가 발생했을 때 실행될 콜백함수를 등록하여 이벤트에 대한 처리를 하는 역할을 합니다.&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;또한 WebSocket 커넥션은 websocket 요청이 들어올 때마다 하위 쓰레드로 커넥션을 관리하기 때문에, 각 client 별 websocket 에 대한 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;&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;Line Live 의 Chatting-Server 아키텍처&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;Line Live 의 Chatting-Server 아키텍처에서 중점적으로 다뤄보고 싶은 내용은 바로 Redis 의 Pub/Sub 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&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;f5.png&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR7BLD/btr3iJgrTWZ/i9mjEQIPxgWdACCtSsYehK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR7BLD/btr3iJgrTWZ/i9mjEQIPxgWdACCtSsYehK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR7BLD/btr3iJgrTWZ/i9mjEQIPxgWdACCtSsYehK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR7BLD%2Fbtr3iJgrTWZ%2Fi9mjEQIPxgWdACCtSsYehK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;409&quot; data-filename=&quot;f5.png&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;409&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;사용자 A가 채팅 메시지를 보내면, 채팅 메시지를 받는 사용자들에게 메시지가 즉시 전달되는 것이 아니라 Redis 서버의 내부 처리 큐에 등록하여 순차적으로 처리되는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 채팅 메시지가 수신자 모두에게 전달되기까지 기다릴 필요 없이 Redis 내부 큐에 등록되기만 하면 작업이 완료되는 것입니다.&lt;/p&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 의 Pub/Sub 구조를 통해 Chatting-Server 간 메시지 전송도 비동기적으로 처리하여 대량의 메시지를 처리하는 높은 성능을 확보하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;이러한 Redis 의 Pub/Sub 구조의 장단점은 다음과 같습니다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;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;Redis Pub/Sub 은 대량의 메시지를 처리할 수 있는 높은 성능을 가지고 있습니다.&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;Redis 의 Pub/Sub 은 여러 개의 서버로 확장하여 클러스터링을 구성할 수 있습니다.&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;Redis Pub/Sub 은 메시지를 즉시 처리하여 실시간성을 보장할 수 있습니다.&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;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;Redis Pub/Sub 은 메시지 전송 후 삭제되기 때문에 메시지의 지속성을 보장하지 않습니다.&lt;/li&gt;
&lt;li&gt;최대 1회 전송 (at-most-once) 패턴, 메시지가 유실될 수 있으나 최대 1회만 전송한다.&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;Redis Pub/Sub 은 Push 방식으로 Subscribe 에게 메시지를 밀어주기 때문에 Subscribe 가 많을 수록 성능이 저하될 수 있습니다.&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;Line Live Chatting-Server 는 Redis Pub/Sub 구조로 인해 메시지 유실 우려에 대해서는 MySQL 에 주기적으로 마이그레이션 하는 방법을 통해 극복을 하였습니다. 현재 네이버 Line Live Chatting Server 는 100대 이상의 인스턴스에서 동작하며 성능과 안정성을 증명해내고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;Pub/Sub 구조라면 Kafka ??&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;대규모 채팅 데이터 처리라고 하면 빼놓을 수 없는 키워드 중 하나가 바로 Kafka 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단, Kafka 에 대해 간략하게 설명한다면 Kafka 는 Source Application 과 Target Application 의 결합도를 낮추기 위한 메시지큐라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;f6.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ewvZ4V/btr3dSSPssx/8qUsyL7KGAdWoPFHz7IGuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ewvZ4V/btr3dSSPssx/8qUsyL7KGAdWoPFHz7IGuK/img.png&quot; data-alt=&quot;아파치 카프카 입문 (데브원영)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ewvZ4V/btr3dSSPssx/8qUsyL7KGAdWoPFHz7IGuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FewvZ4V%2Fbtr3dSSPssx%2F8qUsyL7KGAdWoPFHz7IGuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;858&quot; height=&quot;276&quot; data-filename=&quot;f6.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;276&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;Kafka 의 구성요소는 다음과 같습니다.&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 : Kafka Broker 는 카프카가 설치되어 있는 서버 단위를 말합니다.&lt;/li&gt;
&lt;li&gt;Topic : 카프카에서 데이터가 들어갈 수 있는 공간으로 큐의 역할을 합니다.&lt;/li&gt;
&lt;li&gt;Partition : Partition 은 Topic 하나 당 Partition 을 여러개 보유할 수 있으며, 데이터 대기열을 물리적으로 분산된 것을 말합니다.&lt;/li&gt;
&lt;li&gt;Producer : 메시지를 발행하는 주체입니다.&lt;/li&gt;
&lt;li&gt;Consumer : 메시지를 구독하는 주체입니다.&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;먼저 Kafka 를 사용한다면 chatting-server architecture 가 어떤식으로 구성되는지 아래 예시를 통해 들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;f7.png&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AmT15/btr3iJHxeaJ/lnEIR39KAwaUdpfUtO6CG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AmT15/btr3iJHxeaJ/lnEIR39KAwaUdpfUtO6CG1/img.png&quot; data-alt=&quot;https://www.joinc.co.kr/w/man/12/Kafka/chatting&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AmT15/btr3iJHxeaJ/lnEIR39KAwaUdpfUtO6CG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAmT15%2Fbtr3iJHxeaJ%2FlnEIR39KAwaUdpfUtO6CG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;358&quot; data-filename=&quot;f7.png&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.joinc.co.kr/w/man/12/Kafka/chatting&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;REST API 로 채팅방이 개설 요청을 하게 되면 Chatting-Server 는 Kafka 에 채팅방에 대한 Topic 을 생성합니다.&lt;/li&gt;
&lt;li&gt;Client 는 Chatting-Server 와 WebSocket 방식으로 커넥션을 가져갑니다.&lt;/li&gt;
&lt;li&gt;Client 는 WebSocket 으로 접속할 때 Request 로 던지는 채팅방_아이디를 기준으로 해당 Topic 을 Subscribe 합니다.&lt;/li&gt;
&lt;li&gt;메시지가 들어올 때마다 채팅방 Topic 을 Subsribe 하는 서버는 Client 에게 메시지를 전달합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우선 동작하는 건 알겠는데, Kafka 가 채팅 메시지 처리를 하는 것에 있어 장점이 있을까요?&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;Kafka 는 아래 3가지 장점을 가지고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;High throughput message capacity
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka 는 고 가용성으로, Producer (Websocket을 받는 Chatting-Server) 가 메시지를 발행하는 순간 Partition 에 메시지를 보관합니다.&lt;/li&gt;
&lt;li&gt;분산처리가 가능하기 때문에 짧은 시간 내에 엄청나게 많은 데이터를 Consumer 에게 전달할 수 있습니다.&lt;/li&gt;
&lt;li&gt;짧은 시간 내에 많은 데이터를 Consumer 에게 전달할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Scalability 와 Fault tolerant
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카프카는 확장성이 뛰어나므로, 브로커 중 몇대가 죽더라도 Replica 로 복제된 데이터로 안전하게 보관할 수 있습니다.&lt;/li&gt;
&lt;li&gt;가령, Websocket 으로 연결된 채팅 서버 중 1대가 죽거나 Restart 가 된다 하더라도, 해당 채팅메시지는 Kafka 의 Topic 에 보관되어 있기 때문에 재시작 후 다시 채팅 메시지를 받을 수 있습니다.&lt;/li&gt;
&lt;li&gt;기존, 연결이 끊어졌을 때 채팅메시지를 못 받았던 부분을 kafka 를 이용한다면 client 에서 다시 Reconnect 하는 것만으로 이전 채팅 메시지들을 다시 받을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Undeleted log
&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;&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;지금까지 대규모 채팅 아키텍처에서는 C10K 문제를 어떻게 해결할까에 대한 주제로 글을 적어봤습니다. 아직 부족한 게 많아 내용이 부실하거나 잘못된 부분이 있으면 가감없이 알려주시면 감사하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;h1&gt;Reference&lt;/h1&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;a href=&quot;https://cloud.google.com/pubsub/docs/overview?hl=ko&quot;&gt;https://cloud.google.com/pubsub/docs/overview?hl=ko&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@eastperson/Spring-Boot-환경에서-K6-Grafana를-활용한-부하테스트-해보기&quot;&gt;https://velog.io/@eastperson/Spring-Boot-환경에서-K6-Grafana를-활용한-부하테스트-해보기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=lCxgddyxDyg&quot;&gt;https://www.youtube.com/watch?v=lCxgddyxDyg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;가상 면접 사례로 배우는 대규모 시스템 설계&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.joinc.co.kr/w/man/12/Kafka/chatting&quot;&gt;https://www.joinc.co.kr/w/man/12/Kafka/chatting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;아파치 카프카 입문 (데브원영)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Architecture</category>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/249</guid>
      <comments>https://catchdream.tistory.com/249#entry249comment</comments>
      <pubDate>Sat, 11 Mar 2023 18:44:42 +0900</pubDate>
    </item>
    <item>
      <title>분산트랜스코딩으로 알아보는 Boss-Worker 아키텍처</title>
      <link>https://catchdream.tistory.com/248</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;트랜스 코딩이란 무엇일까?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AtcR2/btrZV75JUXk/NVbMvXweOKEsGiSqiw0JsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AtcR2/btrZV75JUXk/NVbMvXweOKEsGiSqiw0JsK/img.png&quot; data-alt=&quot;https://m.blog.naver.com/dna2073/221111113511&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AtcR2/btrZV75JUXk/NVbMvXweOKEsGiSqiw0JsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAtcR2%2FbtrZV75JUXk%2FNVbMvXweOKEsGiSqiw0JsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;727&quot; height=&quot;403&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://m.blog.naver.com/dna2073/221111113511&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;위의 그림과 같이 현대에서는 하나의 영상이 다양한 기기에서 재생됩니다. Computer, TV, Tablet 등 다양한 디스플레이에서 재생되는 것은 물론, 하나의 디스플레이에서도 우리는 여러 해상도와 비트레이트를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&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;  해상도 (Resolution) : 1인치 안에 표현되는 화소나 점의 개수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 비트레이트 (Bitrate) : 초당 처리하는 비트, 영상 재생 시 비트레이트가 높을 수록 하이 퀄리티의 영상을 볼&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;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHNi14/btrZR3bzj6x/8JlENhNwCobOYNP9q6CHbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHNi14/btrZR3bzj6x/8JlENhNwCobOYNP9q6CHbK/img.png&quot; data-alt=&quot;https://engineering.linecorp.com/ko/blog/line-transcoding-server-architecture-improvement-1/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHNi14/btrZR3bzj6x/8JlENhNwCobOYNP9q6CHbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHNi14%2FbtrZR3bzj6x%2F8JlENhNwCobOYNP9q6CHbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;277&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://engineering.linecorp.com/ko/blog/line-transcoding-server-architecture-improvement-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;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 동영상파일을 트랜스코딩하기 위해 업로드를 진행합니다.&lt;/li&gt;
&lt;li&gt;서버는 하나의 영상 파일을 영상파일과 오디오파일로 분리하는 작업을 진행합니다. (Demuxer)&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림에서의 모래시계는 과정별로 어느 정도의 시간이 소요된 것을 나타냅니다. 서버에서 해상도가 1280 x 720 픽셀이고, 길이가 90초, 초당 프레임수가 30인 동영상을 단일 CPU 코어에서 트랜스코딩하면 &amp;lsquo;비디오 디코딩(decoding) + 비디오 인코딩(encoding) 엔 약 3분 40초, &amp;lsquo;오디오 디코딩 + 오디오 인코딩&amp;rsquo; 엔 약 2.5초가 소요됩니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;refer.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://engineering.linecorp.com/ko/blog/line-transcoding-server-architecture-improvement-1/&quot;&gt;https://engineering.linecorp.com/ko/blog/line-transcoding-server-architecture-improvement-1/&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;p data-ke-size=&quot;size16&quot;&gt;그만큼 CPU 자원의 연산 작업이 많이 수행된다는 것을 의미합니다.&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;3.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPWRC7/btrZJ2Fg8t0/d9UWt22ozTQ7k9S665bqik/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPWRC7/btrZJ2Fg8t0/d9UWt22ozTQ7k9S665bqik/img.jpg&quot; data-alt=&quot;https://www.youtube.com/watch?v=1QENgCaP920&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPWRC7/btrZJ2Fg8t0/d9UWt22ozTQ7k9S665bqik/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPWRC7%2FbtrZJ2Fg8t0%2Fd9UWt22ozTQ7k9S665bqik%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;1280&quot; height=&quot;720&quot; data-filename=&quot;3.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.youtube.com/watch?v=1QENgCaP920&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;우리는 4~5 시간이 넘어가는 라이브 영상을 다시보기로 보고 싶을 때, 몇 시간을 기다리는 경우도 생길 수 있습니다.&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;그러면 우리는 동영상을 높은 고해상도로 다양한 포맷으로 트랜스코딩을 진행하고 싶을 때, 좀더 빠르게 트랜스코딩할 수 있는 방법이 없을까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 서버가 트랜스코딩 작업을 수행하는 것이 오래걸린다면, 우리는 여러 서버가 트랜스코딩을 진행할 수 있도록 작업을 분산해주면 될 것입니다.&lt;/p&gt;
&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 data-ke-size=&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;6.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Sfftv/btrZPS9cHUm/EZ8H9V0rvkmI7fAr8zROXk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Sfftv/btrZPS9cHUm/EZ8H9V0rvkmI7fAr8zROXk/img.jpg&quot; data-alt=&quot;https://magazine.hankyung.com/business/article/202202092726b&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Sfftv/btrZPS9cHUm/EZ8H9V0rvkmI7fAr8zROXk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSfftv%2FbtrZPS9cHUm%2FEZ8H9V0rvkmI7fAr8zROXk%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;630&quot; height=&quot;388&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://magazine.hankyung.com/business/article/202202092726b&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;  하나의 작업을 지시하는 역할과 작업을 진행하는 역할로 나뉘어 수행하는 모델을 Boss-Worker 모델이라고 합니다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;저희는 편의상 Boss 를 Scheduler, Worker를 Worker로 정의하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;분산 트랜스코딩 단계별 작업 과정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분산트랜스코딩 하고자 하는 영상을 업로드 후, 작업 요청을 받는다.&lt;/li&gt;
&lt;li&gt;요청받은 영상을 특정 시간 단위별로 자른다.&lt;/li&gt;
&lt;li&gt;각각 잘린 영상에 대해 트랜스코딩을 할 수 있도록 여러 서버에게 작업을 지시한다.&lt;/li&gt;
&lt;li&gt;모든 영상파일이 정상적으로 트랜스코딩을 완료하였을 경우 결과 영상들을 합친다.&lt;/li&gt;
&lt;/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-origin-width=&quot;545&quot; data-origin-height=&quot;323&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DxTIN/btrZK1Tn0rL/qGcpcNj10jhZ1Dak50BdBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DxTIN/btrZK1Tn0rL/qGcpcNj10jhZ1Dak50BdBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DxTIN/btrZK1Tn0rL/qGcpcNj10jhZ1Dak50BdBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDxTIN%2FbtrZK1Tn0rL%2FqGcpcNj10jhZ1Dak50BdBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;545&quot; height=&quot;323&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;323&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 먼저 스케줄러는 Callback (분산트랜스코딩을 수행해야 하는 작업이 생겼을 때 알림을 받아 이벤트를 동작하는 방식) 방식으로 분산 트랜스코딩 작업을 대기합니다.&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;3. 각각 작업을 Scheduler 서버가 Worker 서버에게 전달할 수 있도록 Message Queue 를 활용해 프로파일별 트랜스코딩 명령을 전달합니다.&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;560&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pPkqV/btrZJXw4C86/WK9Tp5fNbrLotfpbNqLcKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pPkqV/btrZJXw4C86/WK9Tp5fNbrLotfpbNqLcKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pPkqV/btrZJXw4C86/WK9Tp5fNbrLotfpbNqLcKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpPkqV%2FbtrZJXw4C86%2FWK9Tp5fNbrLotfpbNqLcKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;315&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;4. 각 Worker 서버에서 트랜스코딩 작업을 완료한 후 완료 결과를 Scheduler 에게 Message Queue 를 활용해 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Scheduler 는 모든 영상파일이 완료되었다는 응답을 받으면, 해당 영상파일을 합치는 작업을 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;  여기서 드는 의문점이 있습니다.&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;만일, 하나의 파일만 트랜스코딩을 하는 경우, 3개의 서버에게 일을 분업하여 작업을 진행한다면 3개의 서버 모두 일을 하게 되어 작업 시간은 대폭 감소하겠지만,&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;이를 알아보기 위해 실제 아래와 같은 시나리오로 테스트를 진행해보았습니다.&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;일반 트랜스코딩 VS 분산 트랜스코딩 성능 테스트&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;테스트 환경&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버는 5대로, 각 CPU 의 코어가 다르다.&lt;/li&gt;
&lt;li&gt;파일은 같은 Spec 의 파일로 테스트를 진행한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;File size : 326M&lt;/li&gt;
&lt;li&gt;Codec : avc&lt;/li&gt;
&lt;li&gt;Duration : 14m 15s&lt;/li&gt;
&lt;li&gt;Size : 1920 x 1080&lt;/li&gt;
&lt;li&gt;Bitrate : 3056kbps&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;테스트 목표 : 일반 트랜스코딩과 분산트랜스코딩의 성능차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1개의 파일 업로드를 진행했을 때&lt;/li&gt;
&lt;li&gt;5개의 파일을 동시에 업로드를 진행해보았을 때&lt;/li&gt;
&lt;li&gt;10개의 파일을 동시에 업로드를 진행해보았을 때&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개 파일을 트랜스코딩 시 각 서버별 소요되는 작업시간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server 정보 CPU Encoding 시간&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGHzMd/btrZIKSxJUd/NBlGbrFHEB4KReqGPIbfVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGHzMd/btrZIKSxJUd/NBlGbrFHEB4KReqGPIbfVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGHzMd/btrZIKSxJUd/NBlGbrFHEB4KReqGPIbfVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGHzMd%2FbtrZIKSxJUd%2FNBlGbrFHEB4KReqGPIbfVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;233&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;233&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;서버 별 CPU Spec 에 따라 트랜스코딩 작업 소요시간이 걸리는 양상을 알 수 있다.&lt;/li&gt;
&lt;li&gt;어떤 서버에게 Job 을 배분하는지는 현재 서버의 유휴상태 (작업 가능 여부) 따라 배분을 하지만, Server-2 와 Server-5 에 작업을 할당하게 되는 경우에는 최대 21분 정도가 소요될 수 있음을 알 수 있다.&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개 파일을 분산 트랜스코딩 시 걸리는 작업시간&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q60q6/btrZJW50Qdq/Hkki3x1FtoMOk5vEoHmqe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q60q6/btrZJW50Qdq/Hkki3x1FtoMOk5vEoHmqe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q60q6/btrZJW50Qdq/Hkki3x1FtoMOk5vEoHmqe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ60q6%2FbtrZJW50Qdq%2FHkki3x1FtoMOk5vEoHmqe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;809&quot; height=&quot;144&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;144&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개 파일을 분산 트랜스코딩 시 걸리는 시간은 평균적으로 대략 3분대임을 알 수 있다.&lt;/li&gt;
&lt;li&gt;파일을 분리하고, 합치는 시간은 각각 대략 5~6초 소요된다.
&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;일반 트랜스코딩과 작업소요시간을 비교해보았을 때 최소 약 4분에서 최대 18분 정도 성능차이를 알 수 있다.&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;5개 파일을 트랜스코딩 시 각 서버별 소요되는 작업시간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server 정보 CPU Encoding 시간&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn840j/btrZJDMuwOb/K47C8BEauLpkPObKkmxADk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn840j/btrZJDMuwOb/K47C8BEauLpkPObKkmxADk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn840j/btrZJDMuwOb/K47C8BEauLpkPObKkmxADk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn840j%2FbtrZJDMuwOb%2FK47C8BEauLpkPObKkmxADk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;233&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;233&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;5개의 파일을 트랜스코딩 시에도 위의 표와 같은 시간이 소요됨을 알 수 있다.&lt;/li&gt;
&lt;li&gt;Server-3 은 7m 40s 로 제일 빨리 트랜스코딩 작업이 완료되었으나, 이후 대기하고 있는 상태이다.&lt;/li&gt;
&lt;li&gt;Server-2 가 21m 40s 를 완료해야 비로소 5개 파일 트랜스코딩 작업이 모두 완료됨을 알 수 있다.&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개 파일을 분산 트랜스코딩 시 걸리는 작업시간&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pO6qn/btrZJ2yyOnF/8u7K4dbbtFKUqrVbXDlm2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pO6qn/btrZJ2yyOnF/8u7K4dbbtFKUqrVbXDlm2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pO6qn/btrZJ2yyOnF/8u7K4dbbtFKUqrVbXDlm2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpO6qn%2FbtrZJ2yyOnF%2F8u7K4dbbtFKUqrVbXDlm2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;818&quot; height=&quot;250&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;250&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;5개 파일을 분산 트랜스코딩 시 걸리는 시간은 평균적으로 대략 15분대로 나왔다.&lt;/li&gt;
&lt;li&gt;파일을 분리하고, 합치는 시간은 각각 대략 5~10초 소요된다.
&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;/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;10개 파일을 트랜스코딩할 때의 작업 소요시간 그래프&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;181&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VcVMy/btrZNLbnuJI/8VbgwtOk74jhjFfT4r9lzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VcVMy/btrZNLbnuJI/8VbgwtOk74jhjFfT4r9lzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VcVMy/btrZNLbnuJI/8VbgwtOk74jhjFfT4r9lzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVcVMy%2FbtrZNLbnuJI%2F8VbgwtOk74jhjFfT4r9lzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;181&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;181&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;10개 파일을 분산 트랜스코딩할 때의 작업 소요시간 그래프&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/omwdo/btrZKxdXf3b/Jqx3yGLUx6jtzKKL8CKu8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/omwdo/btrZKxdXf3b/Jqx3yGLUx6jtzKKL8CKu8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/omwdo/btrZKxdXf3b/Jqx3yGLUx6jtzKKL8CKu8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fomwdo%2FbtrZKxdXf3b%2FJqx3yGLUx6jtzKKL8CKu8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;266&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;266&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;li&gt;분산 트랜스코딩은 서버 Spec 과 무관하게 모든 서버의 CPU 자원을 골고루 사용하여 평균적인 작업소요시간을 단축 시킬 수 있는 장점이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;분산 트랜스코더의 장점&lt;/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;/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;4시간 지점의 구간때문에 트랜스코딩이 실패하는 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 트랜스코더인 경우, 5시간 걸리는 트랜스코딩이 4시간 때 에러가 발생하면 그 때 알 수 있음&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;분산 트랜스코더의 단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;용량이 작은 파일의 경우에는 일반 트랜스코딩보다 더 오래 걸릴 수 있다.
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;번외) Video 와 Audio 를 한꺼번에 분산트랜스코딩 한 경우 생기는 문제점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오와 영상을 한꺼번에 분산 트랜스코딩을 수행하게 되면 짤리는 위치가 생긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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-filename=&quot;a1.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d32rB9/btrZPSBnoA7/zzxbhPKsaLwGdlCefepH51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d32rB9/btrZPSBnoA7/zzxbhPKsaLwGdlCefepH51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d32rB9/btrZPSBnoA7/zzxbhPKsaLwGdlCefepH51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd32rB9%2FbtrZPSBnoA7%2FzzxbhPKsaLwGdlCefepH51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;60&quot; data-filename=&quot;a1.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;60&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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a2.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cV8SYs/btrZKoOIF7f/1lvJqIwOaEfeS2XUVQ7dg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cV8SYs/btrZKoOIF7f/1lvJqIwOaEfeS2XUVQ7dg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cV8SYs/btrZKoOIF7f/1lvJqIwOaEfeS2XUVQ7dg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcV8SYs%2FbtrZKoOIF7f%2F1lvJqIwOaEfeS2XUVQ7dg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;60&quot; data-filename=&quot;a2.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;60&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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a3.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s3Gx2/btrZPTUzHQW/ohiRmcBecnMVBYFMNBoiaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s3Gx2/btrZPTUzHQW/ohiRmcBecnMVBYFMNBoiaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s3Gx2/btrZPTUzHQW/ohiRmcBecnMVBYFMNBoiaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs3Gx2%2FbtrZPTUzHQW%2FohiRmcBecnMVBYFMNBoiaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;60&quot; data-filename=&quot;a3.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;60&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a4.png&quot; data-origin-width=&quot;1315&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zBYRJ/btrZLTAOp5t/RWoUeTrwbaPX7bRXbCBGrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zBYRJ/btrZLTAOp5t/RWoUeTrwbaPX7bRXbCBGrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zBYRJ/btrZLTAOp5t/RWoUeTrwbaPX7bRXbCBGrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzBYRJ%2FbtrZLTAOp5t%2FRWoUeTrwbaPX7bRXbCBGrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1315&quot; height=&quot;138&quot; data-filename=&quot;a4.png&quot; data-origin-width=&quot;1315&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확대해보면 위와 같이 검은색 줄이 보이는데, 일정한 주기로 오디오 음량이 줄어드는 끊김 현상을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 영상을 시청하게 되면 순간순간 오디오가 끊어지는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&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;a href=&quot;https://m.blog.naver.com/dna2073/221111113511&quot;&gt;https://m.blog.naver.com/dna2073/221111113511&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://engineering.linecorp.com/ko/blog/line-transcoding-server-architecture-improvement-1/&quot;&gt;https://engineering.linecorp.com/ko/blog/line-transcoding-server-architecture-improvement-1/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itrum.tistory.com/91&quot;&gt;https://itrum.tistory.com/91&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/0216497&quot;&gt;https://d2.naver.com/helloworld/0216497&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <author>계단</author>
      <guid isPermaLink="true">https://catchdream.tistory.com/248</guid>
      <comments>https://catchdream.tistory.com/248#entry248comment</comments>
      <pubDate>Sun, 19 Feb 2023 09:34:36 +0900</pubDate>
    </item>
  </channel>
</rss>