<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Suyeon's Blog</title>
    <link>https://suyeon96.tistory.com/</link>
    <description>호기심 많은 개발자의 기록</description>
    <language>ko</language>
    <pubDate>Wed, 6 May 2026 14:52:03 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>suyeon96</managingEditor>
    <image>
      <title>Suyeon's Blog</title>
      <url>https://tistory1.daumcdn.net/tistory/3934062/attach/d8386e3c9fba42c9bc8f882d7a6ec588</url>
      <link>https://suyeon96.tistory.com</link>
    </image>
    <item>
      <title>[가상화] Xen과 KVM 하이퍼바이저 아키텍처</title>
      <link>https://suyeon96.tistory.com/54</link>
      <description>&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://suyeon96.tistory.com/52&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[가상화] 하이퍼바이저와 가상화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://suyeon96.tistory.com/53&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[가상화] Full Virtualization &amp;amp; Para Virtualization (전가상화와 반가상화)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Xen Architecture&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Xen&lt;/code&gt;은 AWS, Citrix, 알리바바클라우드, 화웨이, 인텔, AMD, KT 등에서 정말 많이 사용하는 오픈소스 하이퍼바이저이다. Xen은 &lt;code&gt;대표적인 반가상화 하이퍼바이저&lt;/code&gt;로 등장했지만, HVM(하드웨어 기반 가상화) 기능을 활용하여 전가상화도 지원한다.&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;Untitled.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E4Pzg/btrsaTfKMoO/36QEDDnANBDyXWqkkiie91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E4Pzg/btrsaTfKMoO/36QEDDnANBDyXWqkkiie91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E4Pzg/btrsaTfKMoO/36QEDDnANBDyXWqkkiie91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE4Pzg%2FbtrsaTfKMoO%2F36QEDDnANBDyXWqkkiie91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;288&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인(Domain)은 Xen 환경에서 동작하고 있는 가상머신을 의미한다. 도메인은 크게 Dom0와 DomU 2가지로 나뉘는데, Dom0는 실제 물리 디바이스와 통신하기 위한 디바이스 드라이브가 존재하는 가상화된 시스템이라고 보면 된다. 다시 정리하면, 실제 하드웨어와 연결되는 디바이스 드라이브를 가지고 다른 도메인들을 제어하는 Guest OS인 것이다. 이를 제외한 특권이 없는 (Unprivileged) 나머지 도메인을 DomU라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flow를 보면, 각 도메인은 Hypercall로 xen 하이퍼바이저에게 입/출력 요청을 하게 되고, Xen 하이퍼바이저는 이를 도메인 0에게 전달한다. Dom0는 받은 요청을 실제 디바이스에 전달하고 이런 방식으로 실제 장치와 입/출력을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dom0에서는 하나의 장치를 여러 도메인이 공유할 수 있도록 해야하기에 장치를 에뮬레이션 하는 방법을 통해 이를 해결한다. Xen에서는 QEMU라는 오픈소스 에뮬레이터를 Dom0에 동작시켜 BIOS, 디크 컨트롤러 그래픽 어댑터, 네트워크 카드 같은 장치를 에뮬레이션한다.&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;Untitled 1.png&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;415&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/llmHU/btrsaSBaIbG/KG27y84SSiCoxhRul1rfYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/llmHU/btrsaSBaIbG/KG27y84SSiCoxhRul1rfYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/llmHU/btrsaSBaIbG/KG27y84SSiCoxhRul1rfYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FllmHU%2FbtrsaSBaIbG%2FKG27y84SSiCoxhRul1rfYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;460&quot; height=&quot;196&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;415&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 도메인들의 장치 드라이브가 Dom0의 장치 드라이브와 연결이 되어야 하는데, Dom0의 XenStore라는 곳이 이를 가능하도록 하고 XenBus라는 것을 통해서 통신하게 된다. 도메인 간 통신 방법으로 shared memory라는 것을 사용하고, 도메인 간 메모리 공유를 지원하기 위해 Grant Table이란 자료구조를 사용한다.&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;KVM Architecture&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KVM(Kernel-based Virtual Machine) 또한 전가상화 반가상화 모두 지원하지만, KVM은 &lt;code&gt;대표적인 전가상화 하이퍼바이저&lt;/code&gt;이다. KVM의 가장 큰 특징이 이전 글에서 다루었던 하드웨어 지원 가상화이기에 전가상화 기준의 provisioning 관점에서 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 KVM을 사용하기 위해서는 CPU에서 HVM(하드웨어 기반 가상화) 기능을 제공해야 한다. 간단하게 복습하자면, 예전에 Virtual Memory(가상메모리)를 지원하기 위해 CPU에서 Paging 기능을 하드웨어 차원에서 제공했던 것과 같이, 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;Untitled 2.png&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;665&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EolPj/btrr3nicHLP/yDhC2drS1jemuQSniRqunk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EolPj/btrr3nicHLP/yDhC2drS1jemuQSniRqunk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EolPj/btrr3nicHLP/yDhC2drS1jemuQSniRqunk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEolPj%2Fbtrr3nicHLP%2FyDhC2drS1jemuQSniRqunk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;325&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;665&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드웨어 기반 가상화를 위해 emulation이 필요한데 이 역할을 QEMU가 수행한다. QEMU로 OS 이미지를 실행하면 QEMU가 KVM에 요청을 하고 이 과정에서 vcpu 등이 생성된다. KVM이 Vm Entry 명령어를 실행하면 VMX root 모드에서 VMX non-root 모드로 전환된다. (trap 발생) non-root 모드의 Guest OS에서 실행되는 모든 system call은 trap이 걸려서 root모드로 이동해서 처리되고 다시 non-root 모드로 리턴하게 된다. 이렇게 vm을 사용하다가 guest os를 종료하면 KVM 디바이스 드라이버를 통해서 모든 리소스를 정리하고 끝나게 되는 것이다.&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;p data-ke-size=&quot;size16&quot;&gt;XEN과 KVM 하이퍼바이저의 차이는 사실 개념부터 조금 다른데, Xen은 하이퍼바이저 위에서 모든 처리를 한다는 개념이면, KVM은 하이퍼바이저를 커널의 서브 모듈로 취급한다. 하지만 두 하이퍼바이저 모두 전가상화와 반가상화 둘다 지원한다. 그래서 오늘날엔 하이퍼바이저가 전가상화니 반가상화니 구분하는 경계가 모호해졌다. 전가상화 또한 cpu의 발달에 따라 점차 하드웨어 가상화로 자리 잡고 있는 추세이다.&lt;/p&gt;
&lt;p data-ke-size=&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;Reference&lt;/h2&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://xenproject.org/&quot;&gt;https://xenproject.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.xenproject.org/wiki/Xen_Architecture&quot;&gt;https://wiki.xenproject.org/wiki/Xen_Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.itopening.com/4396/&quot;&gt;https://www.itopening.com/4396/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://l2men.tistory.com/18&quot;&gt;https://l2men.tistory.com/18&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://selfish-developer.com/entry/2-Xen-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0Hypercall?category=825819&quot;&gt;https://selfish-developer.com/entry/2-Xen-기본-구조Hypercall?category=825819&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.redhat.com/ko/topics/virtualization/what-is-KVM&quot;&gt;https://www.redhat.com/ko/topics/virtualization/what-is-KVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linux-kvm.org/page/Main_Page&quot;&gt;https://www.linux-kvm.org/page/Main_Page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.butcherslab.co.kr/entry/%EA%B0%80%EC%83%81%ED%99%94-KVM-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0&quot;&gt;https://www.butcherslab.co.kr/entry/가상화-KVM-기본-구조&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>IT/Cloud</category>
      <category>hypervisor</category>
      <category>kvm</category>
      <category>Virtualization</category>
      <category>xen</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/54</guid>
      <comments>https://suyeon96.tistory.com/54#entry54comment</comments>
      <pubDate>Wed, 9 Feb 2022 01:00:14 +0900</pubDate>
    </item>
    <item>
      <title>[가상화] Full Virtualization &amp;amp; Para Virtualization (전가상화와 반가상화)</title>
      <link>https://suyeon96.tistory.com/53</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://suyeon96.tistory.com/52&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 글&lt;/a&gt;에서 가상화와 하이퍼바이저에 대해서 알아보았다. 하이퍼바이저는 다시 가상화 방식에 따라 &lt;code&gt;Full Virtualization&lt;/code&gt;과 &lt;code&gt;Para Virtualization&lt;/code&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;Full Virtualization (전가상화)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Full Virtualization&lt;/code&gt;은 한글로 전가상화라고 한다. 말 그대로 하드웨어를 완전히 가상화하는 방식이다. 전부 가상화하기 때문에 Guest OS가 Host 시스템과 완전히 분리되어 실행된다. 따라서 Full Virtualization 방식에서 Guest OS는 자신이 가상머신의 OS인지 인지하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2NTSh/btrsaR3l9GF/mWEkYLt2wi1IFnZlyEkVJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2NTSh/btrsaR3l9GF/mWEkYLt2wi1IFnZlyEkVJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2NTSh/btrsaR3l9GF/mWEkYLt2wi1IFnZlyEkVJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2NTSh%2FbtrsaR3l9GF%2FmWEkYLt2wi1IFnZlyEkVJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;347&quot; height=&quot;296&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host 하드웨어와 Guest OS 사이에 Hypervisor가 분명 존재하나, VM 입장에서는 가상화된 하드웨어를 진짜(물리) 하드웨어로 인지하고 있기 때문에, 실제 물리 자원에 대해 요청을 보낸다고 생각하게 된다. 원래 &lt;code&gt;system call&lt;/code&gt;에 의해 App &amp;rarr; Kernal &amp;rarr; Hardware 순서로 서비스를 요청하는데, 이 과정을 하이퍼바이저 단에서 처리해 주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Hardware Assisted Full Virtualization&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Full Virtualization을 구현하는 방법은 &lt;code&gt;하드웨어 지원 가상화&lt;/code&gt;라는 방법과 &lt;code&gt;소프트웨어 적으로 구현&lt;/code&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;Dual-mode operation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(뒷 내용을 이해하기 위해 선행되어야 하는 내용이라 추가함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cE7bPS/btrr5U7BgqY/5iUQfNOYmk52Mvk0Hy67Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cE7bPS/btrr5U7BgqY/5iUQfNOYmk52Mvk0Hy67Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cE7bPS/btrr5U7BgqY/5iUQfNOYmk52Mvk0Hy67Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcE7bPS%2Fbtrr5U7BgqY%2F5iUQfNOYmk52Mvk0Hy67Gk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;601&quot; height=&quot;206&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, OS는 &lt;code&gt;Dual-mode operation(이중 동작 모드)&lt;/code&gt;라는 것을 지원한다. 사용자와 OS는 시스템 자원을 공유하는데, 사용자에게 제한을 두지 않으면 사용자가 메모리 내의 주요 자원들을 망가뜨릴 수 있기 때문에 이를 보호하기 위해 마련된 장치인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이중동작 모드는 사용자모드와 커널모드로 구성되어 있다. 사용자가 사용하는 application이 사용자모드에서 작동되다가 OS에게 시스템 요청을 하게 되는 경우, 커널모드로 바꿔 요청된 서비스를 실행한 후에 다시 사용자모드로 전환되는 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 본론으로 들어와서 하드웨어 지원 가상화 방법에 대해 살펴보려 한다. 가장 대표적으로 Intel CPU에서 하드웨어 지원 가상화를 위해 VT-x를 사용하는데, 이 메커니즘을 조금 뜯어보며 Trap과 Emulation에 대해 알아보자.&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;Trap &amp;amp; Emulation&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceM1O6/btrr2si7Mvx/h5DLbyzRO9dDl0Kt3UKmq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceM1O6/btrr2si7Mvx/h5DLbyzRO9dDl0Kt3UKmq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceM1O6/btrr2si7Mvx/h5DLbyzRO9dDl0Kt3UKmq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceM1O6%2Fbtrr2si7Mvx%2Fh5DLbyzRO9dDl0Kt3UKmq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;729&quot; height=&quot;278&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼바이저는 &lt;code&gt;Root모드&lt;/code&gt;에서 동작하고, 도메인은 &lt;code&gt;Non-root모드&lt;/code&gt;에서 동작하도록 되어 있다. Non-root 모드에서 동작하는 도메인이 특권 명령을 실행하면 &lt;code&gt;Trap&lt;/code&gt;이 발생하고 트랩 핸들러가 VM exit 명령을 수행해서 하이퍼바이저가 실행하도록 한다. 처리가 완료되면 다시 vm enter 명령을 통해 다시 도메인이 실행되도록 하드웨어가 명령어를 지원하는 방식인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hypervisor에서 이 모든 걸 다 처리해야 하고, 트랩이 발생할 때마다 에뮬레이션이 처리하는 오버헤드까지 생각하면 성능 저하가 생길 수밖에 없다.&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;Binary Translation&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKQyKy/btrsfXuVQbC/Eq4SE7Ll6FHT60Lahbjkik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKQyKy/btrsfXuVQbC/Eq4SE7Ll6FHT60Lahbjkik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKQyKy/btrsfXuVQbC/Eq4SE7Ll6FHT60Lahbjkik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKQyKy%2FbtrsfXuVQbC%2FEq4SE7Ll6FHT60Lahbjkik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;337&quot; height=&quot;284&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Guest OS는 다양한 종류의 OS가 올라갈 수 있기에, 가상화된 하드웨어에 요청을 할 때 OS마다 인터페이스가 각기 다르다. 이런 다양한 형식을 하나의 형식으로 번역해주는 작업을 &lt;code&gt;Binary Translation&lt;/code&gt; 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 Binary Translation 과정에서도 오버헤드가 발생하게 된다.&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;Hardware Assisted Full Virtualization vs Software Assisted Full Virtualization&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEX0Lp/btrshztNA5d/RntS3YrEH0mEdvsIwQESI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEX0Lp/btrshztNA5d/RntS3YrEH0mEdvsIwQESI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEX0Lp/btrshztNA5d/RntS3YrEH0mEdvsIwQESI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEX0Lp%2FbtrshztNA5d%2FRntS3YrEH0mEdvsIwQESI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;339&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;하드웨어 지원 전가상화&lt;/code&gt;의 경우 Trap과 Emulation을 이용하여 Guest OS의 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;Untitled 5.png&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDUkk/btrsc2pXg4a/QvuIkwlk4YddK2alVab5n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDUkk/btrsc2pXg4a/QvuIkwlk4YddK2alVab5n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDUkk/btrsc2pXg4a/QvuIkwlk4YddK2alVab5n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDUkk%2Fbtrsc2pXg4a%2FQvuIkwlk4YddK2alVab5n1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;460&quot; height=&quot;348&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;code&gt;소프트웨어 적으로 전가상화&lt;/code&gt;를 처리하는 경우 Binary Translation을 이용하여 각 단계에서 모든 명령에 대해 하나하나 다 가상화하는 방법으로 진행된다.&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;Para Virtualization (반가상화)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 계속 언급했듯이 &lt;code&gt;Full Virtualization&lt;/code&gt;의 경우 거쳐야 할 단계가 많기에 오버헤드가 발생하며, 성능이 느려진다. 그래서 이를 해결하고자 &lt;code&gt;Para Virtualization&lt;/code&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;Untitled 6.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KzMrS/btrr2X4Y93d/bkML741LLPYSKl8GQ4NBmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KzMrS/btrr2X4Y93d/bkML741LLPYSKl8GQ4NBmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KzMrS/btrr2X4Y93d/bkML741LLPYSKl8GQ4NBmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKzMrS%2Fbtrr2X4Y93d%2FbkML741LLPYSKl8GQ4NBmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;749&quot; height=&quot;309&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Para Virtualization은 한글로 반가상화라고도 하는데, 핵심은 &lt;code&gt;Hyper Call&lt;/code&gt;이다. Para Virtualization에 사용되는 Guest OS는 Hyper Call이라는 인터페이스를 통해 하이퍼바이저에 직접 요청을 날린다. 방식은 사실 OS에서 Application이 커널에게 system call로 서비스를 요청하는 방식과 동일하다. 요청을 날리는 주체가 Guest OS이고, 받는 대상이 하이퍼바이저라는 점이 다를 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UCmnk/btrr5VrRriS/fjKuxNR8ZefVpNh50kKQeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UCmnk/btrr5VrRriS/fjKuxNR8ZefVpNh50kKQeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UCmnk/btrr5VrRriS/fjKuxNR8ZefVpNh50kKQeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUCmnk%2Fbtrr5VrRriS%2FfjKuxNR8ZefVpNh50kKQeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;337&quot; height=&quot;285&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전가상화에서의 Guest OS는 자신이 Guest OS인지 모른다고 하였다. 하지만 반가상화에서는 Guest OS가 Hypervisor에 직접 Hyper Call을 날려야 하기에, 자신이 Guest OS라는 사실을 인지해야만 한다. 따라서 반가상화 하이퍼바이저에 올라가는 Guest OS는 커널을 수정하여, Guest용 OS를 따로 만들어야 한다.&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;예시로 Para Virtualization 이해하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전가상화는 모든 명령어를 가상화해야 하지만, 반가상화에서는 굳이 모든 명령어를 가상화할 필요가 없다. 이러한 메커니즘과 반가상화에서는 왜 Guest OS의 커널을 수정해야 하는지 개발자 관점에서 설명을 하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;xor eax, eax    # eax register 초기화
mov cr0, eax    # control register 값 설정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용되는 어셈블리 명령어를 예시로 드려하는데, 어셈블리를 처음 접하는 분들을 위해 간단히 설명을 하자면... 첫 번째는 범용레지스터 중에 산술연산이나 논리연산을 수행하는 eax라는 레지스터가 있는데, xor연산으로 이 레지스터의 값을 0으로 초기화시키는 명령어이다. 두 번째는 프로세서 상태와 동작모드를 제어하는 여러가지 제어 flag를 가지는 cr0라는 레지스터에 flag 값을 설정하는 명령어이다.&lt;/p&gt;
&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;전가상화에서는 내가 Guest OS인지 모르기 때문에 모든 명령어를 가상화하게 된다. 반면 반가상화에서는 이렇게 꼭 필요한 중요한 명령어만 가상화한다. 필요한 명령어만 가상화하니까 당연히 모든 명령어를 가상화하는 전가상화 보다 성능이 좋을 수밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 반가상화를 위해서는 중요한 명령어와 아닌 명령어를 구분할 수 있어야 하는데, 그렇기에 Guest OS의 커널의 수정이 필요한 것이다. 그리고 Guest OS가 하이퍼바이저에게 명령어를 가상화해달라고 직접 요청하는데, 이게 Hyper Call이다.&lt;/p&gt;
&lt;p data-ke-size=&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;Reference&lt;/h2&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/ilikebigmac/222009981745&quot;&gt;https://m.blog.naver.com/ilikebigmac/222009981745&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://bugsfixed.blogspot.com/2016/10/blog-post.html#full-virtualization-vs-para-virtualzation&quot;&gt;http://bugsfixed.blogspot.com/2016/10/blog-post.html#full-virtualization-vs-para-virtualzation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.unixarena.com/2017/12/para-virtualization-full-virtualization-hardware-assisted-virtualization.html&quot;&gt;https://www.unixarena.com/2017/12/para-virtualization-full-virtualization-hardware-assisted-virtualization.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techdifferences.com/difference-between-full-virtualization-and-paravirtualization.html&quot;&gt;https://techdifferences.com/difference-between-full-virtualization-and-paravirtualization.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dong-co.tistory.com/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dong-co.tistory.com/47&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Cloud</category>
      <category>HVM</category>
      <category>hypervisor</category>
      <category>Virtualization</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/53</guid>
      <comments>https://suyeon96.tistory.com/53#entry53comment</comments>
      <pubDate>Tue, 8 Feb 2022 01:00:19 +0900</pubDate>
    </item>
    <item>
      <title>[가상화] 하이퍼바이저와 가상화</title>
      <link>https://suyeon96.tistory.com/52</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;가상화 (Virtualization)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가상화란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 일반적으로 사용하는 PC는 cpu와 memory를 적게 사용하는 경우 전력도 적게 공급되도록 설계되어 있다. 반면, 서버의 경우 항상 최상의 성능을 유지해야 하기 때문에 전력을 최대로 공급해야 할뿐더러 서버를 조금 사용하든 많이 사용하든 IDC 임대료는 동일하다. 그렇다면 서버의 남는 cpu와 memory를 놀리지 말고 활용할 수 있는 방법을 찾아야 했다.&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;Untitled.png&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AwvbU/btrr3nbjbdw/aexlUxF29CVGk8ALfjbWTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AwvbU/btrr3nbjbdw/aexlUxF29CVGk8ALfjbWTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AwvbU/btrr3nbjbdw/aexlUxF29CVGk8ALfjbWTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAwvbU%2Fbtrr3nbjbdw%2FaexlUxF29CVGk8ALfjbWTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;168&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 간단한 방법은 동시에 여러 개의 Application을 동시에 올리는 방법이다. 웹서버, DB서버, DNS 등등... 그러나 이런 경우 서로 간의 영향이 생길뿐더러 서버가 다운되었을 때 모든 서비스가 중단되는 위험이 있다. 보안 문제도 필연적으로 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서 가상화의 개념이 시작된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;가상화&lt;/code&gt;를 처음 접하는 경우 너무나도 추상적인 단어에 감을 잡기 어려울 수 있다. 사실 가상화라는 단어는 일종의 마케팅 용어라고 필자는 생각한다. 그래서 조금 쉽게 설명하자면, 가상화라는 것은 물리적인 하드웨어를 &lt;code&gt;논리적&lt;/code&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;VM과 컨테이너&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화의 핵심은 &lt;code&gt;Isolation&lt;/code&gt;이다. 논리적으로 격리가 제대로 이루어지면, 격리된 각각의 가상 시스템에서 문제가 생겨도, 다른 영역에 영향을 미치지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘날 가상화는 크게 2가지 유형이 존재하는데, &lt;code&gt;가상머신(vm)&lt;/code&gt;과 &lt;code&gt;컨테이너(container)&lt;/code&gt;이다. 이 두 개는 개발자라면 혹은 IT에 발을 들였다면 이미 사용 중이거나 익숙한 단어일 것이다. 불과 몇 년 만에 얼마나 클라우드 transformation이 진행되었는지 실감이 난다. 앞으로는 더더욱 클라우드 기반 환경 위에서 모든 서비스가 운영되고, 개발이 진행될 것이다.&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;Untitled 1.png&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MZDN8/btrr3noSUv4/NYNRNLCLzIrkjYWm84GQv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MZDN8/btrr3noSUv4/NYNRNLCLzIrkjYWm84GQv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MZDN8/btrr3noSUv4/NYNRNLCLzIrkjYWm84GQv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMZDN8%2Fbtrr3noSUv4%2FNYNRNLCLzIrkjYWm84GQv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;281&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠깐 옆으로 샜는데, 오늘은 활용적인 측면에서가 아니라 가상화 관점에서 이들을 바라볼 것이다. 두 개의 차이점을 간단하게 설명하자면, vm은 하이퍼바이저를 이용하여 리소스 전체를 가상화하는 방법이고, 컨테이너는 OS수준에서 프로세스를 컨테이너 형태로 격리하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 차이점이 극명하며 그에 따른 장단점도 분명하기에, 각각의 기술에 대한 배경과 철학을 이해하면 큰 도움이 될 것이다. 이 글에서는 하이퍼바이저 가상화에 대해서 다룰 예정이다. (&lt;s&gt;컨테이너도 원해요? 그럼 500원&lt;/s&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;하이퍼바이저 (Hypervisor)&lt;/h2&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDEnSx/btrsfXV0iwu/lNfDKQm9SHCUN0IkE6yRk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDEnSx/btrsfXV0iwu/lNfDKQm9SHCUN0IkE6yRk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDEnSx/btrsfXV0iwu/lNfDKQm9SHCUN0IkE6yRk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDEnSx%2FbtrsfXV0iwu%2FlNfDKQm9SHCUN0IkE6yRk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;331&quot; height=&quot;248&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vm을 만들기 위해서는 위 그림에서 보는 것처럼 물리적인 하드웨어를 논리적으로 가상화해야 한다. 이때 이를 담당하는 애가 바로 &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;아직 이해가 안 된다면 Hypervisor라는 이름이 왜 붙었는지 뜯어보면 조금 더 명확하게 알 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In general, operating systems are referred as supervisors.&lt;br /&gt;As a hypervisor software is a supervisor of a &amp;ldquo;supervisor&amp;rdquo;, it is called hypervisor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vm은 각각 독립적인 OS를 가지며, Application을 띄우고 Process를 올리는 것을 OS가 수행하고 관리한다. 따라서 일반적으로 운영체제를 supervisor라고 부른다. 위 그림에서 Guest OS가 supervisor 라면, 하이퍼바이저는 supervisor의 supervisor인 셈인 것이다.&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;TYPE 1 vs TYPE 2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼바이저는 크게 &lt;code&gt;Type1 하이퍼바이저&lt;/code&gt;와 &lt;code&gt;Type2 하이퍼바이저&lt;/code&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;Untitled 3.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;487&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WSdll/btrr7q6bwxZ/S9OM0XSiPxFde1or59CLgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WSdll/btrr7q6bwxZ/S9OM0XSiPxFde1or59CLgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WSdll/btrr7q6bwxZ/S9OM0XSiPxFde1or59CLgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWSdll%2Fbtrr7q6bwxZ%2FS9OM0XSiPxFde1or59CLgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;377&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;487&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Type1 방식은 Native 혹은 베어메탈 형 하이퍼바이저라고도 불린다. 위 그림에서 볼 수 있듯이 Type1 하이퍼바이저는 베어메탈 하드웨어 위에 직접 설치되어 구동된다. 대표적인 Type1 하이퍼바이저에는 Xen, KVM, XenServer(Citrix), Hyper-V(Microsoft), ESX-i(vmware) 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Type2 방식은 Hosted 하이퍼바이저라고 불린다. Type2 하이퍼바이저는 다른 Application과 마찬가지로 Host OS 위에 설치된다. 베어메탈 하드웨어 위에 Host OS가 설치되고, 그 위에 하이퍼바이저가 실행되는 형태인 것이다. 테스트환경을 구성할 때 자주 사용하는 Oracle VirtualBox나 vmware Workstation이 여기에 해당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 Type2 방식이 Host OS라는 하나의 Layer가 더 존재하므로, 성능면에서 Type1이 Type2보다 유리하다. 실제 IDC를 클라우드화 시키는데 사용되는 하이퍼바이저도 모두 Type1 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;하이퍼바이저는 다시 가상화 방식에 따라 Full Virtualization과 Para Virtualization으로 분류된다. &lt;a href=&quot;https://suyeon96.tistory.com/53&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;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&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://medium.com/@yavuzsert/q-a-hypervisors-58d6bccfd909&quot;&gt;https://medium.com/@yavuzsert/q-a-hypervisors-58d6bccfd909&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Cloud</category>
      <category>hypervisor</category>
      <category>Virtualization</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/52</guid>
      <comments>https://suyeon96.tistory.com/52#entry52comment</comments>
      <pubDate>Mon, 7 Feb 2022 23:00:20 +0900</pubDate>
    </item>
    <item>
      <title>[Network] ONOS로 Open vSwitch 제어하기</title>
      <link>https://suyeon96.tistory.com/51</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글 또한 작년 여름에 실습하며 정리한 내용인데, 블로그 활동을 잠시 쉬게 되면서 오늘에서야 포스팅하게 되었다. 이 글을 읽기 전 &lt;a href=&quot;https://suyeon96.tistory.com/category/IT/Cloud&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cloud 카테고리&lt;/a&gt;의 [Network] Prefix가 붙은 글을 먼저 읽는 것을 권장한다.&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;ONOS란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONOS(Open Network Operating System)는 SDN/NFV 솔루션을 구축하기 위한 오픈소스 SDN 컨트롤러이다. (Apache 2.0 라이센스)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONOS는 단순화된 프로그래밍 상의 인터페이스로 새로운 동적 네트워크 서비스를 생성하고 구축할 수 있는 유연성을 제공하며, 통신사급 솔루션을 구축하고자 하는 사업자의 요구를 충족시키기 위해 설계되었다. ONOS는 네트워크의 구성과 실시간 제어를 모두 제어하므로, 네트워크 패브릭 내에서 라우팅과 스위칭 제어 프로토콜을 실행할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONOS 프로젝트는 고가용성, 확장성, 탄력성을 제공하는 컨트롤러 개발을 목적으로 하며, 대규모 네트워크 사업자들이 사용할 만한 수준의 고성능 SDN 컨트롤러 플랫폼을 지향한다. 또한 ONOS는 OpenFlow를 포함한 다양한 Southbound API 프로토콜을 지원한다.&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;ONOS 실습 시나리오&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실습 내용 및 아키텍처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 아~주 간단한 SDN 환경을 구성해보려 한다. Open vSwitch로 docker container들 간의 네트워크 통신을 구성하고, SDN 컨트롤러인 ONOS를 통해 이를 제어할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;791&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgNAqU/btrr9gn3GLN/tSN51wzEPADunBDwLEubP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgNAqU/btrr9gn3GLN/tSN51wzEPADunBDwLEubP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgNAqU/btrr9gn3GLN/tSN51wzEPADunBDwLEubP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgNAqU%2Fbtrr9gn3GLN%2FtSN51wzEPADunBDwLEubP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1570&quot; height=&quot;791&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1570&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;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;vm : AWS EC2&lt;/li&gt;
&lt;li&gt;os : Ubuntu Server 18.04 LTS (HVM)&lt;/li&gt;
&lt;li&gt;spec : 2Core 8GB (t3.large)&lt;/li&gt;
&lt;li&gt;disk : 80GB(ssd)&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. OVS 설치&lt;/h3&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;$ sudo apt install -y openvswitch-switch&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ovs 설치 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ sudo ovs-vsctl show
  2d739b44-181a-4749-acad-8b4152da30c4
      ovs_version: &quot;2.9.8&quot;

$ ps -el | grep ovs
  5 S     0  2804     1  0  70 -10 -  5350 poll_s ?        00:00:00 ovsdb-server
  5 S     0  2868     1  0  70 -10 -  6734 poll_s ?        00:00:00 ovs-vswitchd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwtsn5/btrsc1KHeJn/ykub6h26oPgOKKaPk0Ybc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwtsn5/btrsc1KHeJn/ykub6h26oPgOKKaPk0Ybc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwtsn5/btrsc1KHeJn/ykub6h26oPgOKKaPk0Ybc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwtsn5%2Fbtrsc1KHeJn%2Fykub6h26oPgOKKaPk0Ybc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;801&quot; height=&quot;99&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Docker 설치&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;$ sudo apt install docker.io&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 아래와 같은 에러가 난다면...&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;E: Unable to locate package docker.io
E: Couldn't find any package by glob 'docker.io'
E: Couldn't find any package by regex 'docker.io'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apt update를 한번 해주자&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;$ sudo apt-get update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker 설치 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;$ docker version
    Client:
     Version:           20.10.2
     API version:       1.41
     Go version:        go1.13.8
     Git commit:        20.10.2-0ubuntu1~18.04.2
     Built:             Tue Mar 30 21:24:16 2021
     OS/Arch:           linux/amd64
     Context:           default
     Experimental:      true

    Server:
     Engine:
      Version:          20.10.2
      API version:      1.41 (minimum version 1.12)
      Go version:       go1.13.8
      Git commit:       20.10.2-0ubuntu1~18.04.2
      Built:            Mon Mar 29 19:27:41 2021
      OS/Arch:          linux/amd64
      Experimental:     false
     containerd:
      Version:          1.4.4-0ubuntu1~18.04.2
      GitCommit:
     runc:
      Version:          1.0.0~rc95-0ubuntu1~18.04.1
      GitCommit:
     docker-init:
      Version:          0.19.0
      GitCommit:

$ docker info
    Client:
     Context:    default
     Debug Mode: false

    Server:
     Containers: 0
      Running: 0
      Paused: 0
      Stopped: 0
     Images: 0
     Server Version: 20.10.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 container를 올리지 않았기 때문에 count가 0 임을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker network 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ docker network ls
    NETWORK ID     NAME      DRIVER    SCOPE
    653f9e41c710   bridge    bridge    local
    f070c409eca0   host      host      local
    babc348f1c09   none      null      local

$ docker network inspect bridge
    [
        {
            &quot;Name&quot;: &quot;bridge&quot;,
            &quot;Id&quot;: &quot;653f9e41c710a0ad44198a509be9de04a099d902dace631ca45cf234ec983e44&quot;,
            &quot;Created&quot;: &quot;2021-07-07T07:39:52.188188984Z&quot;,
            &quot;Scope&quot;: &quot;local&quot;,
            &quot;Driver&quot;: &quot;bridge&quot;,
            &quot;EnableIPv6&quot;: false,
            &quot;IPAM&quot;: {
                &quot;Driver&quot;: &quot;default&quot;,
                &quot;Options&quot;: null,
                &quot;Config&quot;: [
                    {
                        &quot;Subnet&quot;: &quot;172.17.0.0/16&quot;
                    }
                ]
            },
            &quot;Internal&quot;: false,
            &quot;Attachable&quot;: false,
            &quot;Ingress&quot;: false,
            &quot;ConfigFrom&quot;: {
                &quot;Network&quot;: &quot;&quot;
            },
            &quot;ConfigOnly&quot;: false,
            &quot;Containers&quot;: {},
            &quot;Options&quot;: {
                &quot;com.docker.network.bridge.default_bridge&quot;: &quot;true&quot;,
                &quot;com.docker.network.bridge.enable_icc&quot;: &quot;true&quot;,
                &quot;com.docker.network.bridge.enable_ip_masquerade&quot;: &quot;true&quot;,
                &quot;com.docker.network.bridge.host_binding_ipv4&quot;: &quot;0.0.0.0&quot;,
                &quot;com.docker.network.bridge.name&quot;: &quot;docker0&quot;,
                &quot;com.docker.network.driver.mtu&quot;: &quot;1500&quot;
            },
            &quot;Labels&quot;: {}
        }
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. ovs-docker 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ovs와 docker 연결을 간단하게 control 하기 위해 ovs-docker utility를 이용할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(자세한 내용은 &lt;a href=&quot;https://github.com/openvswitch/ovs/blob/master/utilities/ovs-docker&quot;&gt;https://github.com/openvswitch/ovs/blob/master/utilities/ovs-docker&lt;/a&gt; 참고)&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ cd /usr/bin

$ sudo wget https://raw.githubusercontent.com/openvswitch/ovs/master/utilities/ovs-docker
    --2021-07-07 07:54:06--  https://raw.githubusercontent.com/openvswitch/ovs/master/utilities/ovs-docker
    Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.109.133, 185.199.108.133, ...
    Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 8064 (7.9K) [text/plain]
    Saving to: &amp;lsquo;ovs-docker.1&amp;rsquo;

    ovs-docker.1             100%[==================================&amp;gt;]   7.88K  --.-KB/s    in 0s

    2021-07-07 07:54:06 (74.8 MB/s) - &amp;lsquo;ovs-docker.1&amp;rsquo; saved [8064/8064]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. OVS Bridge 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ovs-vsctl(OVS 제어 CLI)로 ovs1 브릿지를 생성한다.&lt;/p&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;$ ovs-vsctl add-br ovs1&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;생성한 ovs1 브릿지에 호스트 외부 접속 인터페이스 생성&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ ifconfig ovs1 173.16.1.1 netmask 255.255.255.0 up&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;code&gt;ifconfig&lt;/code&gt; 명령어로 확인해보면 ovs스위치(ovs1)가 생성된 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ ifconfig
    docker0: flags=4099&amp;lt;UP,BROADCAST,MULTICAST&amp;gt;  mtu 1500
            inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
            ether 02:42:0f:30:4d:ce  txqueuelen 0  (Ethernet)
            RX packets 0  bytes 0 (0.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 0  bytes 0 (0.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    ens5: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 9001
            inet 10.0.1.76  netmask 255.255.255.0  broadcast 10.0.1.255
            inet6 fe80::1b:19ff:feff:ef86  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 02:1b:19:ff:ef:86  txqueuelen 1000  (Ethernet)
            RX packets 73502  bytes 106507952 (106.5 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 6360  bytes 625896 (625.8 KB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    lo: flags=73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu 65536
            inet 127.0.0.1  netmask 255.0.0.0
            inet6 ::1  prefixlen 128  scopeid 0x10&amp;lt;host&amp;gt;
            loop  txqueuelen 1000  (Local Loopback)
            RX packets 228  bytes 20072 (20.0 KB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 228  bytes 20072 (20.0 KB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    ovs1: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
            inet 173.16.1.1  netmask 255.255.255.0  broadcast 173.16.1.255
            inet6 fe80::580b:adff:fe18:1348  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 5a:0b:ad:18:13:48  txqueuelen 1000  (Ethernet)
            RX packets 0  bytes 0 (0.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 6  bytes 516 (516.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blmhDh/btrr16nb6Pt/EDBusCACDmfQAAcmFOdeI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blmhDh/btrr16nb6Pt/EDBusCACDmfQAAcmFOdeI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blmhDh/btrr16nb6Pt/EDBusCACDmfQAAcmFOdeI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblmhDh%2Fbtrr16nb6Pt%2FEDBusCACDmfQAAcmFOdeI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;137&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;137&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;5. container 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 docker container 2개를 생성해주자.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;$ sudo docker run -t -i -d --name container1 alpine
    4f982ff32be0f196800fd6ac0c389914218f680d583c23d103761944d5d75d8c

$ sudo docker run -t -i -d --name container2 alpine
    5a3c1d7d1ec8ba8592d5a8d458ba3ab1aca4f2e9cb623987460292930cc92fcd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Container ID 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;$ sudo docker ps
    CONTAINER ID   IMAGE     COMMAND     CREATED          STATUS          PORTS     NAMES
    5a3c1d7d1ec8   alpine    &quot;/bin/sh&quot;   15 seconds ago   Up 13 seconds             container2
    4f982ff32be0   alpine    &quot;/bin/sh&quot;   36 seconds ago   Up 34 seconds             container1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;각 container의 네트워크 인터페이스 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ sudo docker exec container1 ifconfig
    eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
              inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:16 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:1392 (1.3 KiB)  TX bytes:0 (0.0 B)

    lo        Link encap:Local Loopback
              inet addr:127.0.0.1  Mask:255.0.0.0
              UP LOOPBACK RUNNING  MTU:65536  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
$ sudo docker exec container1 ping 1.1.1.1

$ sudo docker exec container2 ifconfig
    eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:03
              inet addr:172.17.0.3  Bcast:172.17.255.255  Mask:255.255.0.0
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:54 errors:0 dropped:0 overruns:0 frame:0
              TX packets:42 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:4856 (4.7 KiB)  TX bytes:3948 (3.8 KiB)

    lo        Link encap:Local Loopback
              inet addr:127.0.0.1  Mask:255.0.0.0
              UP LOOPBACK RUNNING  MTU:65536  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
$ sudo docker exec container2 ping 1.1.1.1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHJB70/btrr1JL38mk/RfQik8uGsH86F4KJuRc8k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHJB70/btrr1JL38mk/RfQik8uGsH86F4KJuRc8k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHJB70/btrr1JL38mk/RfQik8uGsH86F4KJuRc8k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHJB70%2Fbtrr1JL38mk%2FRfQik8uGsH86F4KJuRc8k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;372&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;372&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;6. container와 ovs bridge 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ovs1과 container를 연결해주기 위해서는 각각 port를 만들고 ip를 할당하고 링크해줘야 하는데, 이걸 간단하게 수행하기 위해 아까 설치한 &lt;code&gt;ovs-docker&lt;/code&gt;를 이용한다.&lt;/p&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;$ sudo ovs-docker add-port ovs1 eth1 container1 --ipaddress=173.16.1.2/24
$ sudo ovs-docker add-port ovs1 eth1 container2 --ipaddress=173.16.1.3/24&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;다시 각 container의 네트워크 인터페이스를 확인해보면, OVS와 연결된 &lt;code&gt;eth1&lt;/code&gt;을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 각 container 간 ping 테스트도 정상적으로 수행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(글이 길어져서 container2만 기재함)&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ sudo docker exec container2 ifconfig # check for Internet
    eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:03
              inet addr:172.17.0.3  Bcast:172.17.255.255  Mask:255.255.0.0
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:424 errors:0 dropped:0 overruns:0 frame:0
              TX packets:410 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:39996 (39.0 KiB)  TX bytes:38948 (38.0 KiB)

    eth1      Link encap:Ethernet  HWaddr CE:C5:78:89:8C:50
              inet addr:173.16.1.3  Bcast:0.0.0.0  Mask:255.255.255.0
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:10 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:796 (796.0 B)  TX bytes:0 (0.0 B)

    lo        Link encap:Local Loopback
              inet addr:127.0.0.1  Mask:255.0.0.0
              UP LOOPBACK RUNNING  MTU:65536  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

$ sudo docker exec container2 ping 173.16.1.2
    PING 173.16.1.2 (173.16.1.2): 56 data bytes
    64 bytes from 173.16.1.2: seq=0 ttl=64 time=0.922 ms
    64 bytes from 173.16.1.2: seq=1 ttl=64 time=0.176 ms
    64 bytes from 173.16.1.2: seq=2 ttl=64 time=0.111 ms
    64 bytes from 173.16.1.2: seq=3 ttl=64 time=0.101 ms
    64 bytes from 173.16.1.2: seq=4 ttl=64 time=0.114 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. ONOS 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지금까지 구성한 환경에서 OVS는 L2 스위치 역할을 수행한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 본 포스팅의 목적이었던 SDN Controller ONOS를 설치하고 연결할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ONOS 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;$ sudo docker run -t -d -p 1181:8181 -p 1101:8101 -p 1653:6653 --name onos1 onosproject/onos
    latest: Pulling from onosproject/onos
    7595c8c21622: Pull complete
    d13af8ca898f: Pull complete
    70799171ddba: Pull complete
    b6c12202c5ef: Pull complete
    a3caae5bc1ad: Pull complete
    c041e8f95d65: Pull complete
    ed1837af27c7: Pull complete
    Digest: sha256:f6b624201b99aa3fbfb25ee9410f48c4a1b3be8bf5ebafb62816f1b694142224
    Status: Downloaded newer image for onosproject/onos:latest
    4916b224f3200337a78489c6a2198a193020daa825be3fec21366a3a78298127&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;8181 : web dashboard 접속을 위한 port&lt;/li&gt;
&lt;li&gt;8101 : CLI 접속을 위한 ssh port&lt;/li&gt;
&lt;li&gt;6653 : ovs - onos 통신을 위한 port&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 ONOS를 구성할 수 있기 때문에 임의로 포트포워딩을 해줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;http://{ipaddress:1181}/onos/ui&lt;/code&gt;로 ONOS web에 접속할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1181로 포트포워딩을 하지 않은 경우 8181로 접속)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1040&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSKdhp/btrr3ifEwiv/8yVMnZmw1MPdtAZlV4EBB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSKdhp/btrr3ifEwiv/8yVMnZmw1MPdtAZlV4EBB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSKdhp/btrr3ifEwiv/8yVMnZmw1MPdtAZlV4EBB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSKdhp%2Fbtrr3ifEwiv%2F8yVMnZmw1MPdtAZlV4EBB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1040&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1040&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;default 계정으로 로그인하면 아래와 같은 화면을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(ID : onos, Password : rocks)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1040&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clwgB0/btrr1jfNwY4/VlriCleiNVk21jBgrTh2VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clwgB0/btrr1jfNwY4/VlriCleiNVk21jBgrTh2VK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clwgB0/btrr1jfNwY4/VlriCleiNVk21jBgrTh2VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclwgB0%2Fbtrr1jfNwY4%2FVlriCleiNVk21jBgrTh2VK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1040&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1040&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;ovs 정보를 확인해보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ sudo ovs-vsctl show
    2d739b44-181a-4749-acad-8b4152da30c4
        Bridge &quot;ovs1&quot;
            Port &quot;da5a724dd0214_l&quot;
                Interface &quot;da5a724dd0214_l&quot;
            Port &quot;7abbaea7e4494_l&quot;
                Interface &quot;7abbaea7e4494_l&quot;
            Port &quot;ovs1&quot;
                Interface &quot;ovs1&quot;
                    type: internal
        ovs_version: &quot;2.9.8&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker ps 확인&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ docker ps
    CONTAINER ID   IMAGE              COMMAND                  CREATED        STATUS        PORTS                                                                                        NAMES
    4916b224f320   onosproject/onos   &quot;./bin/onos-service &amp;hellip;&quot;   14 hours ago   Up 14 hours   6640/tcp, 9876/tcp, 0.0.0.0:1653-&amp;gt;6653/tcp, 0.0.0.0:1101-&amp;gt;8101/tcp, 0.0.0.0:1181-&amp;gt;8181/tcp   onos1
    5a3c1d7d1ec8   alpine             &quot;/bin/sh&quot;                15 hours ago   Up 15 hours                                                                                                container2
    4f982ff32be0   alpine             &quot;/bin/sh&quot;                15 hours ago   Up 15 hours                                                                                                container1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. ONOS(SDN Controller)에 OVS 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ovs1을 ONOS controller에 연결한다.&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;$ sudo ovs-vsctl set-controller ovs1 tcp:172.17.0.4:6653&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;다시 ovs 정보를 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ovs1 Bridge에 Controller가 추가된 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ sudo ovs-vsctl show
    2d739b44-181a-4749-acad-8b4152da30c4
        Bridge &quot;ovs1&quot;
            Controller &quot;tcp:172.17.0.4:6653&quot;
            Port &quot;da5a724dd0214_l&quot;
                Interface &quot;da5a724dd0214_l&quot;
            Port &quot;7abbaea7e4494_l&quot;
                Interface &quot;7abbaea7e4494_l&quot;
            Port &quot;ovs1&quot;
                Interface &quot;ovs1&quot;
                    type: internal
        ovs_version: &quot;2.9.8&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brAJZD/btrr3jMo4AD/sYpll7YmKqEGE7a9tkXKTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brAJZD/btrr3jMo4AD/sYpll7YmKqEGE7a9tkXKTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brAJZD/btrr3jMo4AD/sYpll7YmKqEGE7a9tkXKTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrAJZD%2Fbtrr3jMo4AD%2FsYpll7YmKqEGE7a9tkXKTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;194&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;vm의 네트워크 인터페이스 구성 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ ifconfig
    7abbaea7e4494_l: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
            inet6 fe80::5419:c9ff:fead:8bf0  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 56:19:c9:ad:8b:f0  txqueuelen 1000  (Ethernet)
            RX packets 71544  bytes 6060936 (6.0 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 67802  bytes 6979026 (6.9 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    da5a724dd0214_l: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
            inet6 fe80::8c9b:76ff:fe2c:c856  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 8e:9b:76:2c:c8:56  txqueuelen 1000  (Ethernet)
            RX packets 57500  bytes 5465320 (5.4 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 67852  bytes 6983926 (6.9 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    docker0: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
            inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
            inet6 fe80::42:fff:fe30:4dce  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 02:42:0f:30:4d:ce  txqueuelen 0  (Ethernet)
            RX packets 191928  bytes 27898779 (27.8 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 174754  bytes 39969595 (39.9 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    ens5: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 9001
            inet 10.0.1.76  netmask 255.255.255.0  broadcast 10.0.1.255
            inet6 fe80::1b:19ff:feff:ef86  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 02:1b:19:ff:ef:86  txqueuelen 1000  (Ethernet)
            RX packets 717884  bytes 834421238 (834.4 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 209467  bytes 31302450 (31.3 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    lo: flags=73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu 65536
            inet 127.0.0.1  netmask 255.0.0.0
            inet6 ::1  prefixlen 128  scopeid 0x10&amp;lt;host&amp;gt;
            loop  txqueuelen 1000  (Local Loopback)
            RX packets 454  bytes 44426 (44.4 KB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 454  bytes 44426 (44.4 KB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    ovs1: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
            inet 173.16.1.1  netmask 255.255.255.0  broadcast 173.16.1.255
            inet6 fe80::580b:adff:fe18:1348  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 5a:0b:ad:18:13:48  txqueuelen 1000  (Ethernet)
            RX packets 55  bytes 4396 (4.3 KB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 36  bytes 2616 (2.6 KB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    veth5225a25: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
            inet6 fe80::8485:afff:fe25:9c5f  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 86:85:af:25:9c:5f  txqueuelen 0  (Ethernet)
            RX packets 73191  bytes 6952974 (6.9 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 73227  bytes 6955458 (6.9 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    vethb61a685: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
            inet6 fe80::fc67:f0ff:fe39:9a90  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether fe:67:f0:39:9a:90  txqueuelen 0  (Ethernet)
            RX packets 73207  bytes 6954486 (6.9 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 73247  bytes 6957398 (6.9 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

    vethed92c01: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
            inet6 fe80::1cff:1bff:fe40:2d5c  prefixlen 64  scopeid 0x20&amp;lt;link&amp;gt;
            ether 1e:ff:1b:40:2d:5c  txqueuelen 0  (Ethernet)
            RX packets 45530  bytes 16678311 (16.6 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 28389  bytes 26064505 (26.0 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. ONOS 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초에는 Default Drivers와 ONOS GUI2 만 running 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 Application을 찾아서 올려주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1040&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buq1Fn/btrr1i8ZltX/BRdVDXZelVii5isDzipUq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buq1Fn/btrr1i8ZltX/BRdVDXZelVii5isDzipUq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buq1Fn/btrr1i8ZltX/BRdVDXZelVii5isDzipUq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbuq1Fn%2Fbtrr1i8ZltX%2FBRdVDXZelVii5isDzipUq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1040&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1040&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;토폴로지를 확인하면 OVS bridge에 container 2개가 연결된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1040&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvMdYA/btrr2r5fghS/rTEKcxNEcr3Ir1W1l87MYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvMdYA/btrr2r5fghS/rTEKcxNEcr3Ir1W1l87MYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvMdYA/btrr2r5fghS/rTEKcxNEcr3Ir1W1l87MYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvMdYA%2Fbtrr2r5fghS%2FrTEKcxNEcr3Ir1W1l87MYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1040&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1040&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NJnOv/btrscOkgUkt/lMT6rDdDAe7pIwEfRBzO90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NJnOv/btrscOkgUkt/lMT6rDdDAe7pIwEfRBzO90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NJnOv/btrscOkgUkt/lMT6rDdDAe7pIwEfRBzO90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNJnOv%2FbtrscOkgUkt%2FlMT6rDdDAe7pIwEfRBzO90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1186&quot; height=&quot;475&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;475&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;10. docker network 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bridge 정보 확인&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ docker network inspect bridge
    [
        {
            &quot;Name&quot;: &quot;bridge&quot;,
            &quot;Id&quot;: &quot;653f9e41c710a0ad44198a509be9de04a099d902dace631ca45cf234ec983e44&quot;,
            &quot;Created&quot;: &quot;2021-07-07T07:39:52.188188984Z&quot;,
            &quot;Scope&quot;: &quot;local&quot;,
            &quot;Driver&quot;: &quot;bridge&quot;,
            &quot;EnableIPv6&quot;: false,
            &quot;IPAM&quot;: {
                &quot;Driver&quot;: &quot;default&quot;,
                &quot;Options&quot;: null,
                &quot;Config&quot;: [
                    {
                        &quot;Subnet&quot;: &quot;172.17.0.0/16&quot;
                    }
                ]
            },
            &quot;Internal&quot;: false,
            &quot;Attachable&quot;: false,
            &quot;Ingress&quot;: false,
            &quot;ConfigFrom&quot;: {
                &quot;Network&quot;: &quot;&quot;
            },
            &quot;ConfigOnly&quot;: false,
            &quot;Containers&quot;: {
                &quot;4916b224f3200337a78489c6a2198a193020daa825be3fec21366a3a78298127&quot;: {
                    &quot;Name&quot;: &quot;onos1&quot;,
                    &quot;EndpointID&quot;: &quot;24f811b1e99723ba5eac54f3754106488a4a7e688283b5e33268135e37382469&quot;,
                    &quot;MacAddress&quot;: &quot;02:42:ac:11:00:04&quot;,
                    &quot;IPv4Address&quot;: &quot;172.17.0.4/16&quot;,
                    &quot;IPv6Address&quot;: &quot;&quot;
                },
                &quot;4f982ff32be0f196800fd6ac0c389914218f680d583c23d103761944d5d75d8c&quot;: {
                    &quot;Name&quot;: &quot;container1&quot;,
                    &quot;EndpointID&quot;: &quot;9a9012e246ca997921de6b15a75ea60cb81cbe59878e17c4e776a991ad92188a&quot;,
                    &quot;MacAddress&quot;: &quot;02:42:ac:11:00:02&quot;,
                    &quot;IPv4Address&quot;: &quot;172.17.0.2/16&quot;,
                    &quot;IPv6Address&quot;: &quot;&quot;
                },
                &quot;5a3c1d7d1ec8ba8592d5a8d458ba3ab1aca4f2e9cb623987460292930cc92fcd&quot;: {
                    &quot;Name&quot;: &quot;container2&quot;,
                    &quot;EndpointID&quot;: &quot;858a1b678e7771ffcc439d5594e7c8903a5fca66e4ffb82d84315dcbde4570cc&quot;,
                    &quot;MacAddress&quot;: &quot;02:42:ac:11:00:03&quot;,
                    &quot;IPv4Address&quot;: &quot;172.17.0.3/16&quot;,
                    &quot;IPv6Address&quot;: &quot;&quot;
                }
            },
            &quot;Options&quot;: {
                &quot;com.docker.network.bridge.default_bridge&quot;: &quot;true&quot;,
                &quot;com.docker.network.bridge.enable_icc&quot;: &quot;true&quot;,
                &quot;com.docker.network.bridge.enable_ip_masquerade&quot;: &quot;true&quot;,
                &quot;com.docker.network.bridge.host_binding_ipv4&quot;: &quot;0.0.0.0&quot;,
                &quot;com.docker.network.bridge.name&quot;: &quot;docker0&quot;,
                &quot;com.docker.network.driver.mtu&quot;: &quot;1500&quot;
            },
            &quot;Labels&quot;: {}
        }
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;host 정보도 확인해보자&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ docker network inspect host
    [
        {
            &quot;Name&quot;: &quot;host&quot;,
            &quot;Id&quot;: &quot;f070c409eca02b9afd47922696154def19c06157830181310936ac09749a6875&quot;,
            &quot;Created&quot;: &quot;2021-07-07T07:39:52.129644722Z&quot;,
            &quot;Scope&quot;: &quot;local&quot;,
            &quot;Driver&quot;: &quot;host&quot;,
            &quot;EnableIPv6&quot;: false,
            &quot;IPAM&quot;: {
                &quot;Driver&quot;: &quot;default&quot;,
                &quot;Options&quot;: null,
                &quot;Config&quot;: []
            },
            &quot;Internal&quot;: false,
            &quot;Attachable&quot;: false,
            &quot;Ingress&quot;: false,
            &quot;ConfigFrom&quot;: {
                &quot;Network&quot;: &quot;&quot;
            },
            &quot;ConfigOnly&quot;: false,
            &quot;Containers&quot;: {},
            &quot;Options&quot;: {},
            &quot;Labels&quot;: {}
        }
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iptable 확인&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ iptables -t nat -L -n
    Chain PREROUTING (policy ACCEPT)
    target     prot opt source               destination
    DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination

    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

    Chain POSTROUTING (policy ACCEPT)
    target     prot opt source               destination
    MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0
    MASQUERADE  tcp  --  172.17.0.4           172.17.0.4           tcp dpt:8181
    MASQUERADE  tcp  --  172.17.0.4           172.17.0.4           tcp dpt:8101
    MASQUERADE  tcp  --  172.17.0.4           172.17.0.4           tcp dpt:6653

    Chain DOCKER (2 references)
    target     prot opt source               destination
    RETURN     all  --  0.0.0.0/0            0.0.0.0/0
    DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:1181 to:172.17.0.4:8181
    DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:1101 to:172.17.0.4:8101
    DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:1653 to:172.17.0.4:6653&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 10.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5y7n4/btrr2sbX1He/phxZk4uEpcVc8LiBEUVmck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5y7n4/btrr2sbX1He/phxZk4uEpcVc8LiBEUVmck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5y7n4/btrr2sbX1He/phxZk4uEpcVc8LiBEUVmck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5y7n4%2Fbtrr2sbX1He%2FphxZk4uEpcVc8LiBEUVmck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;402&quot; data-filename=&quot;Untitled 10.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;402&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;Reference&lt;/h3&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://opennetworking.org/onos/&quot;&gt;https://opennetworking.org/onos/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.onosproject.org/display/ONOS/ONOS&quot;&gt;https://wiki.onosproject.org/display/ONOS/ONOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.openvswitch.org/&quot;&gt;https://www.openvswitch.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.koreascience.or.kr/article/JAKO201522562218745.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.koreascience.or.kr/article/JAKO201522562218745.pdf&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>IT/Cloud</category>
      <category>onos</category>
      <category>Open vSwitch</category>
      <category>OVS</category>
      <category>SDN Controller</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/51</guid>
      <comments>https://suyeon96.tistory.com/51#entry51comment</comments>
      <pubDate>Tue, 1 Feb 2022 03:00:31 +0900</pubDate>
    </item>
    <item>
      <title>[Network] Virtual Network와 Open vSwitch (네트워크 가상화)</title>
      <link>https://suyeon96.tistory.com/50</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 올리려고 정리해놨던 글인데, 블로그 활동을 잠시 쉬다가 오랜만에 포스팅한다. Cloud 카테고리에서 Network 영역을 지속해서 다루는 이유는, 네트워크가 가상화의 핵심 요소이며 Cloud를 이해하기 위한 기본적인 소양이기 때문이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;기존 network 인프라&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mgKLb/btrr2XwtDeB/hIsfitzkoyg99W7hk5ZNak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mgKLb/btrr2XwtDeB/hIsfitzkoyg99W7hk5ZNak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mgKLb/btrr2XwtDeB/hIsfitzkoyg99W7hk5ZNak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmgKLb%2Fbtrr2XwtDeB%2FhIsfitzkoyg99W7hk5ZNak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;206&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 레거시 인프라에서는 Application을 호스팅하기 위해 물리적인 서버와 네트워크 장비들이 필요했다. 서버들 간 통신을 가능하도록 하기 위해서 각 서버에는 외부 네트워크에 연결되는 NIC(Network Interface Card)가 포함되어 있어야 하며, NIC은 네트워크 인프라를 통해 endpoint 간 통신을 가능하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 볼 수 있듯이 스위치는 이러한 endpoint 간 효율적인 패킷 통신을 지원하기 위한 장비인 것이다.&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;가상화된 network 인프라&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jd1o2/btrr4xjPtgl/SuWtl2GkQJhW9O8IF39eiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jd1o2/btrr4xjPtgl/SuWtl2GkQJhW9O8IF39eiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jd1o2/btrr4xjPtgl/SuWtl2GkQJhW9O8IF39eiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJd1o2%2Fbtrr4xjPtgl%2FSuWtl2GkQJhW9O8IF39eiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;351&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 VM(가상머신) 환경에서는 물리적 하드웨어를 가상화하여 여러 운영체제와 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;하이퍼바이저는 각 VM에 대해 하나 이상의 가상 NIC(vNIC)을 생성할 수 있다. vNIC은 NIC의 인터페이스를 구현하여 마치 VM의 물리적 NIC처럼 역할을 수행한다. 이렇게 하이퍼바이저는 가상 네트워크의 동적 구현을 허용하도록 하며, 가상 스위치(vSwitch)를 통해 VM 간 통신이 가능하도록 한다. 또한 하이퍼바이저는 서버의 물리적인 NIC을 하이퍼바이저의 가상화된 네트워크 인프라에 연결하여, 외부 네트워크와의 통신을 가능하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 가상화로 인해 Virtual Appliance 등 다른 많은 혁신이 가능해졌는데, Virtual Switch에 대해 조금 더 알아보자.&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;Virtual Switching&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 가상화를 개발하는데 있어 주요 항목 중 하나는 가상 스위치의 개발이다. 가상 스위치는 vNIC을 물리적 NIC에 연결하고, local 통신을 위해 vNIC을 다른 서버의 vNIC들과 연결한다. 흥미로운 점은 가상 스위치 내에서의 속도 limit이 네트워크 속도에 구애받는 것이 아니라, 메모리 대역폭에 관련되어 있다는 점이다. 이는 네트워크 인프라의 오버헤드를 최소화시키며, Local VM들 간 호율적인 통신을 가능하도록 한다. (물리적인 네트워크는 물리적 서버 내에서 VM들 간 트래픽이 격리된 상태에서 서버 간의 통신에만 사용된다.)&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;Untitled 2.png&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eqoCR5/btrsc1KGwLr/IBmERyUMUpqfgkmJSlLsvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eqoCR5/btrsc1KGwLr/IBmERyUMUpqfgkmJSlLsvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eqoCR5/btrsc1KGwLr/IBmERyUMUpqfgkmJSlLsvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeqoCR5%2Fbtrsc1KGwLr%2FIBmERyUMUpqfgkmJSlLsvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;262&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 리눅스는 이미 커널 내에 Layer2 스위치를 포함하고 있는데, 왜 가상 스위치가 필요할까? 여러 이유가 있지만, 가장 중요한 핵심 중 하나는 Distributed Virtual Switch(분산 가상 스위치)이다. 이 스위치 유형은 근본적인 서버 아키텍처를 투명하게 만드는 방식으로 서버 간 bridging을 가능하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 볼 수 있듯이, 한 서버 내의 가상 스위치는 다른 서버의 가상 스위치와 투명하게 결합할 수 있으며, 다른 서버의 분산 가상 스위치에 연결할 수 있다. 이는 서버 간의 VM 마이그레이션 혹은 Virtual Interface의 마이그레이션을 훨씬 간단하게 만들어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 서버 내에서 로컬 트래픽을 격리하게 되면, 트래픽을 외부(network analyzer 등)에서 볼 수 없다는 문제가 있다. 이 문제를 해결하기 위해 OpenFlow, NetFlow, sFlow와 같은 다양한 체계를 통해 원격으로 트래픽을 제어하고 모니터링할 수 있다.&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;Open vSwitch&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Open vSwitch는 가상 스위치 분야에서 가장 중요한 프로젝트이다. 초기에 분산 가상 스위치의 구현체들은 closed되었고, 독립적인 하이퍼바이저 세트로만 운영되도록 제한되었다. 하지만 오늘날의 클라우드 환경에서는 여러 하이퍼바이저가 공존할 수 있는 이기종 환경을 지원하는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Open vSwitch는 Apache 2.0 라이선스에 따라 오픈 소스로 사용할 수 있는 multilayer 가상 스위치이며, Zen, KVM, VirtualBox 등 다양한 오픈소스 하이퍼바이저 솔루션을 지원한다.&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;Untitled 3.png&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LWI6r/btrr9fQfmob/csX4oxUSD4NdGbUJx3iCs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LWI6r/btrr9fQfmob/csX4oxUSD4NdGbUJx3iCs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LWI6r/btrr9fQfmob/csX4oxUSD4NdGbUJx3iCs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLWI6r%2Fbtrr9fQfmob%2FcsX4oxUSD4NdGbUJx3iCs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;418&quot; height=&quot;348&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Open vSwitch는 스위치 데몬과 flow-based 스위칭을 관리하는 companion 커널 모듈로 구성된다. NetFlow, sFlow, CLI 및 RSPAN과 같은 표준 관리 인터페이스를 지원할 뿐만 아니라, OpenFlow를 사용하여 프로그램 확장 및 제어를 수용할 수 있다. 또한 Open vSwitch는 OVSDB라는 관리 프로토콜을 사용한다.&lt;/p&gt;
&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;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.ibm.com/tutorials/l-virtual-networking/&quot;&gt;https://developer.ibm.com/tutorials/l-virtual-networking/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.bmc.com/blogs/virtual-network/&quot;&gt;https://www.bmc.com/blogs/virtual-network/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.openvswitch.org/&quot;&gt;https://www.openvswitch.org/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Open_vSwitch&quot;&gt;https://en.wikipedia.org/wiki/Open_vSwitch&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Cloud</category>
      <category>Open vSwitch</category>
      <category>OVS</category>
      <category>Virtual Network</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/50</guid>
      <comments>https://suyeon96.tistory.com/50#entry50comment</comments>
      <pubDate>Sun, 30 Jan 2022 23:34:09 +0900</pubDate>
    </item>
    <item>
      <title>[Network] SDN/NFV의 관계와 차이 (feat. VNF, CNF)</title>
      <link>https://suyeon96.tistory.com/49</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 SDDC 또는 5G에 대하여 이야기할 때 SDN/NFV라는 2개의 용어가 항상 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 VNF, 더 나아가 CNF까지 함께 이야기하곤 한다.&lt;/p&gt;
&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;SDN (Software Defined Networking)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 설명하자면 &lt;b&gt;SDN이란 소프트웨어를 이용하여 네트워크 리소스를 가상화하는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한줄로 설명하기에는 너무나도 추상적이며 불친절하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 핵심적인 부분을 설명하자면, SDN은 네트워크 장치의 Control Plane(제어부)과 Data Plane(전송부)을 분리하는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 중앙에서 전체 네트워크를 프로그래밍하고 관리할 수 있으며, 하나의 SDN 컨트롤러가 여러 개의 네트워크 장치를 제어할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 SDN을 한 단어로 표현하자면 &lt;b&gt;네트워크 추상화(Abstraction)&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;SDN에 대한 자세한 설명은 이전글(&lt;a href=&quot;https://suyeon96.tistory.com/48&quot;&gt;[Network] SDN이란?&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;h2 data-ke-size=&quot;size26&quot;&gt;NFV (Network Function Virtualization)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NFV란 기존의 네트워크 하드웨어 장비를 소프트웨어 형태로 가상화하는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 라우터(Router), 방화벽(Firewall), 로드밸런서(Load Balancer), IDS, IPS, DNS, 캐싱 등의 물리적인 네트워크 기능을 여러 사용자 또는 필요한(+저렴한) 리소스(서버, 스토리지 등)에서 사용할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NFV 또한 일반적인 리소스 가상화의 특징을 그대로 가지고 있다.&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;이는 특히 SDN 환경에서 큰 효과를 발휘한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(SDN에 대한 이전 글을 보고왔다면 이 말을 바로 이해할 수 있을 것이다)&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;SDN과 NFV의 차이와 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 &lt;b&gt;SDN과 NFV의 차이&lt;/b&gt;를 정리해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1335&quot; data-origin-height=&quot;701&quot; width=&quot;620&quot; height=&quot;326&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckEoIF/btraInxzZcR/tFwBOda8bNTmqSxc0qmgqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckEoIF/btraInxzZcR/tFwBOda8bNTmqSxc0qmgqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckEoIF/btraInxzZcR/tFwBOda8bNTmqSxc0qmgqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckEoIF%2FbtraInxzZcR%2FtFwBOda8bNTmqSxc0qmgqK%2Fimg.png&quot; data-origin-width=&quot;1335&quot; data-origin-height=&quot;701&quot; width=&quot;620&quot; height=&quot;326&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SDN&lt;/b&gt;은 네트워크의 제어 기능과 전달 기능을 소프트웨어로 분리하여 추상화시키는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀더 엄밀히 말하면 Control Plain을 소프트웨어화 시키는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSI 7Layer 기준으로 SDN은 2-3 Layer를 담당하며, 스위치, 라우터, 무선 access 포인트 같은 네트워크 인프라를 최적화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NFV&lt;/b&gt;는 네트워크 Appliance 기능을 가상화하여 하드웨어와 소프트웨어로 분리하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 기능 가상화에 초점이 맞춰져 있으며, 이는 Data Plain을 가상화한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSI 7Layer 기준으로 NFV는 4-7 Layer를 포함하며, Edge 인증, 로드밸런서, 방화벽, WAN 컨트롤러 등의 네트워크 기능의 배포를 최적화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN과 NFV는 서로 상호 보완적이지만, 독립적인 구성도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN 없이 NFV만 구현할 수도 있고, SDN을 구현하는데 반드시 NFV만을 사용해야 하는것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NFV는 SDN을 구현하기 위한 하나의 usecase인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 SDN 환경에서 NFV의 효율이 매우 뛰어나기 때문에 이 둘은 항상(?) 붙어다니고 있다.&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;NFV Architecture&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 ETSI(&lt;span style=&quot;color: #4d5156;&quot;&gt;유럽통신표준협회)&lt;/span&gt;는 NFV 인프라 아키텍처를 &lt;code&gt;VNF&lt;/code&gt;, &lt;code&gt;NFVi&lt;/code&gt;, &lt;code&gt;MANO&lt;/code&gt; 3가지 구성요소로 정의한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;761&quot; width=&quot;620&quot; height=&quot;480&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfQgb4/btrazuLIvy6/9G3Vkh2tLk7igkicj9zh30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfQgb4/btrazuLIvy6/9G3Vkh2tLk7igkicj9zh30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfQgb4/btrazuLIvy6/9G3Vkh2tLk7igkicj9zh30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfQgb4%2FbtrazuLIvy6%2F9G3Vkh2tLk7igkicj9zh30%2Fimg.png&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;761&quot; width=&quot;620&quot; height=&quot;480&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;VNFs (Virtualized Network Functions)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VNF는 네트워크 기능을 제공하는 소프트웨어 애플리케이션이며, VM(가상머신)으로 배포된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VNF에 대해서는 아래에서 조금 더 자세하게 설명할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NFVI (NFV Infrastructure)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NFVI는 VNF를 호스팅하기 위해 하드웨어 리소스를 제공하고, VNF가 네트워크 기능을 수행할 수 있도록 소프트웨어를 지원하는 가상화 계층이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 호스트 서버의 HW자원을 가상화하고 여러 VM들을 독립적으로 실행하기 위해 하이퍼바이저를 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 NFVI 플랫폼(VIM)으로는 OpenStack이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MANO (Management and Network Orchestration)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NFV 인프라 관리를 위한 프레임워크이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 VNF를 생성하고 설정하며, 전체 NFV를 조직하고 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영자 중심 커뮤니티인 OSM(Open Source MANO)에서는 상업용 NFV네트워크 요구사항을 만족하는 오픈 소스 MANO 스택을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&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;VNF (Virtual Network Function) / CNF (Cloud-Native Network Function)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 보았듯이 NFV로부터 VNF라는 개념도 함께 등장하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VNF란 네트워크 기능을 가상머신 상에서 구현한 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 하드웨어를 emulate한 vm을 이용하면, Cloud 컴퓨팅 환경을 완벽하게 가상화할 수 있다.&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;Cloud-Native Network Function(CNF)&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;712&quot; width=&quot;640&quot; height=&quot;455&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCDtu9/btraInK9A5Q/jAWLJxkJnp8jNiMN0eQo9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCDtu9/btraInK9A5Q/jAWLJxkJnp8jNiMN0eQo9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCDtu9/btraInK9A5Q/jAWLJxkJnp8jNiMN0eQo9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCDtu9%2FbtraInK9A5Q%2FjAWLJxkJnp8jNiMN0eQo9K%2Fimg.png&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;712&quot; width=&quot;640&quot; height=&quot;455&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Cloud Native 구조에서는 VNF가 모듈화되어 VNF 구성요소들이 기능 또는 특성별로 마이크로서비스들로 분리되고, 마이크로서비스들은 컨테이너에 배포된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로서비스들은 재사용이 가능하며 뛰어난 확장성을 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, MSA 구조는 서비스 민첩성이 향상되는 동시에 복잡성도 증가하기 때문에 CNF의 생성, 배포, 확장을 자동화하여 네트워크 운영의 편의성과 효율성을 높이는 일은 매우 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&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;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://codilime.com/the-difference-between-sdn-and-nfv-a-simple-guide/&quot;&gt;https://codilime.com/the-difference-between-sdn-and-nfv-a-simple-guide/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.netmanias.com/ko/post/blog/14631/5g-sdn-nfv/evolution-to-5g-cloud-native-1&quot;&gt;https://www.netmanias.com/ko/post/blog/14631/5g-sdn-nfv/evolution-to-5g-cloud-native-1&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://post.naver.com/viewer/postView.nhn?volumeNo=12596096&amp;amp;memberNo=34920570&quot;&gt;https://post.naver.com/viewer/postView.nhn?volumeNo=12596096&amp;amp;memberNo=34920570&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://osm.etsi.org/&quot;&gt;https://osm.etsi.org/&lt;/a&gt;&lt;/p&gt;</description>
      <category>IT/Cloud</category>
      <category>CNF</category>
      <category>NFV</category>
      <category>SDN</category>
      <category>vnf</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/49</guid>
      <comments>https://suyeon96.tistory.com/49#entry49comment</comments>
      <pubDate>Wed, 28 Jul 2021 10:23:54 +0900</pubDate>
    </item>
    <item>
      <title>[Network] SDN(Software Defined Network) 이란?</title>
      <link>https://suyeon96.tistory.com/48</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;SDN이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SDN(Software Defined Network)&lt;/b&gt;이란 소프트웨어를 통해 네트워크 리소스를 가상화하고 추상화하는 네트워크 인프라에 대한 접근 방식을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 쉽게 설명하자면, 소프트웨어 애플리케이션과 API를 이용하여 네트워크를 프로그래밍하고, 중앙에서 전체 네트워크를 제어하고 관리하는 것이다.&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;SDN 작동방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN에서 가장 핵심은 네트워크 장비의 &lt;b&gt;Control Plane(제어부)와 Data Plane(전송부)의 분리&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Control Plane은 네트워크 장비를 제어하는 뇌에 해당하고, Data Plane은 데이터를 전송하는 역할을 하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;627&quot; data-filename=&quot;Untitled.png&quot; width=&quot;515&quot; height=&quot;344&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ux31d/btrastS3hRc/BHUTpzFMPSQBbrE4arJiVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ux31d/btrastS3hRc/BHUTpzFMPSQBbrE4arJiVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ux31d/btrastS3hRc/BHUTpzFMPSQBbrE4arJiVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUx31d%2FbtrastS3hRc%2FBHUTpzFMPSQBbrE4arJiVK%2Fimg.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;627&quot; data-filename=&quot;Untitled.png&quot; width=&quot;515&quot; height=&quot;344&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;기존의 라우터(Router)라는 네트워크 장비에는 제어부와 전송부가 같이 존재한다.&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;&lt;span style=&quot;color: #dddddd;&quot;&gt;(* 첨언으로 Cisco는 전 세계의 네트워크 장비를 수십 년간 거의 독점적으로 공급하며 어마어마하게 성장하였다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN을 적용하면 제어부와 전송부를 분리한다.&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;SDN 장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 비용 절감&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어부는 여러 네트워크 장비를 제어할 수 있기 때문에 관리가 관소화 되고 운영에 들어가는 비용을 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 각 장비의 사양을 각각의 기능에 최적화시킬 수 있으므로 더 이상 눈물을 머금고 리소스의 낭비를 보고 있을 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 확장성 및 유연성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;가상화&quot;라는 기술을 설명할 때 항상 따라오는 장점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN 또한 네트워크를 가상화한다는 점에서 마찬가지이다.&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;h2 data-ke-size=&quot;size26&quot;&gt;SDN의 역사&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 지표에서 볼 수 있듯이 네트워크를 프로그래밍 하는 것은 과거 20년 전부터 지속해서 발전해왔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;557&quot; data-filename=&quot;Untitled 1.png&quot; width=&quot;689&quot; height=&quot;378&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tR9Tt/btranWa5Uc8/xiVsGwot1tg6FT5fce7sDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tR9Tt/btranWa5Uc8/xiVsGwot1tg6FT5fce7sDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tR9Tt/btranWa5Uc8/xiVsGwot1tg6FT5fce7sDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtR9Tt%2FbtranWa5Uc8%2FxiVsGwot1tg6FT5fce7sDk%2Fimg.png&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;557&quot; data-filename=&quot;Untitled 1.png&quot; width=&quot;689&quot; height=&quot;378&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Legacy 네트워크와 별개로 우리는 네트워크를 Software화 시키는 것이 왜 중요한지, 이를 위해 역사상 어떤 노력들이 진행되어 왔는지 살펴볼 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 지표를 가져온 논문(&lt;a href=&quot;https://www.cs.princeton.edu/courses/archive/fall13/cos597E/papers/sdnhistory.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The Road to SDN: An Intellectual History of Programmable Networks&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;p data-ke-size=&quot;size16&quot;&gt;하지만 SDN의 창시자로 불리는 Scott Shenker(UC버클리 교수)과 Nick Mckeown(스탠포드 대학 교수) 2명은 알아두도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들을 비롯한 SDN 창시자들은 ONF(Open Networking Foundation)라는 비영리 단체를 만들고, OpenFlow와 SDN Controller ONOS(Open Network Operating System)를 개발하였다.&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;구글 G-scale 발표&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 SDN이 현업에 적용되며 빠르게 발전한 계기는 2012년 4월 구글에서 &lt;b&gt;G-scale SDN 적용 사례&lt;/b&gt;를 발표하고 나서부터가 아닌가 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;G-Scale은 구글이 2010년에 시작한 OpenFlow 프로젝트로 전 세계에 흩어져있는 구글 데이터센터 백본(Backbone) 구간을 SDN 기반으로 전환하는 프로젝트이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1586&quot; data-origin-height=&quot;709&quot; data-filename=&quot;Untitled 2.png&quot; width=&quot;714&quot; height=&quot;319&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT2wVS/btraoDoNLHu/JHo3GwaGZ4cwSK5AcpQRk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT2wVS/btraoDoNLHu/JHo3GwaGZ4cwSK5AcpQRk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT2wVS/btraoDoNLHu/JHo3GwaGZ4cwSK5AcpQRk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT2wVS%2FbtraoDoNLHu%2FJHo3GwaGZ4cwSK5AcpQRk1%2Fimg.png&quot; data-origin-width=&quot;1586&quot; data-origin-height=&quot;709&quot; data-filename=&quot;Untitled 2.png&quot; width=&quot;714&quot; height=&quot;319&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;백본(Backbone) 네트워크는 사용자 네트워크와 다르게 한번에 대용량 데이터가 전송된다.&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;이에 구글은 자체적으로 네트워크 장비를 제작하고 Openflow를 도입하여 SDN을 구현하고 이를 해결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN 적용으로 구글은 크게 3가지 이득을 볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 50% &amp;rarr; 100%&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 Legacy Network 인프라의 경우 리소스 활용률이 평균 50%가 채 되지 못한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 네트워크 장비들이 벤더에 종속되어 있어 호환성 문제로 기능을 100% 활용할 수 없을 뿐만 아니라 전체 네트워크 스위치들에 대한 컨트롤이 불가능하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급했듯이 다양한 네트워크 상황에 딱맞는 장비를 구할 수도 없기 때문에 오버스펙의 장비를 구매해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 구글은 SDN을 구현하여 거의 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;&lt;b&gt;2. WAN(Wide Area Network) 분야에서의 경로 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WAN대역에서 가장 빠른 경로를 계산하여 데이터 전송 속도를 높이고, 사용자에게는 빠르고 고품질의 서비스를 제공하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글은 전 세계를 대상으로 하는 서비스이기 때문에 WAN 대역에서의 데이터 전송 성능 향상은 더욱 중요하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 비용 절감&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN 컨트롤러와 화이트박스 스위치의 조합을 통해 데이터센터 내의 네트워크 구축 비용을 획기적으로 낮췄다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화이트박스 스위치(WhiteBox Switch)란 SDN에서 나온 개념으로 기존 레거시 장비의 제어 방식이 공개가 안 돼서 &amp;lsquo;블랙박스&amp;rsquo;라고 불리는 것과 반대의 개념이다. 네트워크 장비의 동작 방식을 사용자가 결정하고 투명하게 공개되어 있다는 의미이다.&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;이후 세계의 통신사들은 주도적으로 SDN을 도입하고 네트워크를 가상화하기 시작하였으며, 네트워크 장비 제조사들 또한 SDN의 주도권을 가지기 위해 적극적으로 뛰어들게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 오래되고 대표적인 SDN업체 Nicira가 2012년 VMware에 인수되었다. Cisco는 2014년 Tail-f와 Insieme을 인수하고, 오픈 커뮤니티를 이끌며 오픈 데이라이트(Open Daylight)라는 SDN Controller를 개발하였다.&lt;/p&gt;
&lt;p data-ke-size=&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;SDN Architecture&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SDN 아키텍처&lt;/b&gt;는 &lt;code&gt;Application Layer&lt;/code&gt;, &lt;code&gt;Control Plane(SDN 컨트롤러)&lt;/code&gt;, &lt;code&gt;Data Plane(SDN 전송 장비)&lt;/code&gt; 으로 구성되는 3 계층 구조로 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 계층 간 연동을 위해서 Southbound Interface(ex. OpenFlow)와 Northbound Interface가 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;480&quot; data-filename=&quot;Untitled 3.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzRIri/btrav8nirjG/WmaH2Yc9ULZT0kK4KSCja0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzRIri/btrav8nirjG/WmaH2Yc9ULZT0kK4KSCja0/img.png&quot; data-alt=&quot;출처 : Unveil the Myths About SDN Switch FS Community&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzRIri/btrav8nirjG/WmaH2Yc9ULZT0kK4KSCja0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzRIri%2Fbtrav8nirjG%2FWmaH2Yc9ULZT0kK4KSCja0%2Fimg.png&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;480&quot; data-filename=&quot;Untitled 3.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : Unveil the Myths About SDN Switch FS Community&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Application Layer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing, Loadbalance, ACL 등 네트워크를 제어하는 데 사용되는 소프트웨어 애플리케이션들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN 컨트롤러에서 제공받은 API와 여러 서비스들을 이용하여 Control 기능을 구현한다.&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;SDN Controller&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDN 컨트롤러는 전체 네트워크 자원에 대한 중앙 집중적 제어를 담당하는 네트워크 운영체제 역할을 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙 집중적 네트워크 제어를 위해 글로벌 뷰를 기반으로 포워딩을 제어하고 토폴로지 및 자원의 상태를 관리하는 기본 기능을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 여러 네트워크 장비와 통신할 수 있도록 South-bound API를 제공하거나 추가할 수 있으며, 여러 가지 기능의 애플리케이션을 개발하고 다른 운영 도구과 통신할 수 있게 해주는 North-bound API도 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 SDN Controller에는 위에서 언급했듯이 ONF의 주도로 만들어진 ONOS가 있으며, 이 외에도 Floodlight, Trema, Maestro, Opendaylight 등이 있다.&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;Data Plane&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data Plane의 SDN 디바이스는 SDN 컨트롤러의 지시에 따라 패킷 전송을 수행하는 네트워크 장치를 통칭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 구글의 사례에서 보았듯이 SDN의 개념과 화이트박스 스위치와를 이용하여 레거시 장비 대비 획기적으로 낮은 가격으로 데이터센터의 네트워크를 구축할 수 있다.&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;OpenFlow&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 레거시 장비에서는 Control Planer과 Data Plane이 하나의 장비 내에 같이 탑재되었기 때문에 통신이 필요 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 SDN에서는 Control Planer과 Data Plane이 구분되며, SDN 컨트롤러는 Data Plane 계층에 대한 포워딩을 제어(Match &amp;amp; Action)하고 정보를 수집하기 위해 Southbound 인터페이스를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표준 통신(RPC) 규격 중 하나가 바로 ONF에서 정의하고있는 &lt;b&gt;OpenFlow Protocol&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;186&quot; data-filename=&quot;Untitled 4.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KWBfC/btrax7n8IXO/4QdEIkKaMrxtgTXRhIUim1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KWBfC/btrax7n8IXO/4QdEIkKaMrxtgTXRhIUim1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KWBfC/btrax7n8IXO/4QdEIkKaMrxtgTXRhIUim1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKWBfC%2Fbtrax7n8IXO%2F4QdEIkKaMrxtgTXRhIUim1%2Fimg.png&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;186&quot; data-filename=&quot;Untitled 4.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;즉, OpenFlow는 이론으로만 가능했던 SDN을 구현하기 위해 처음으로 제정된 최초의 표준 통신 인터페이스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenFlow는 OpenFlow 스위치, OpenFlow 컨트롤러로 구성되며, 흐름(flow) 정보를 제어하여 패킷의 전달 경로 및 방식을 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenFlow 스위치 내부에는 패킷 전달 경로와 방식에 대한 정보를 가지고 있는 FlowTable이라는 것이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷이 발생하면 제일 먼저 FlowTable이 해당 패킷에 대한 정보를 가지고 있는지 확인하고, 없다면 패킷에 대한 제어 정보를 OpenFlow 컨트롤러에 요청하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenFlow 컨트롤러 내의 패킷 제어 정보는 외부에서 API를 통해 입력할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&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://opennetworking.org/sdn-definition/&quot;&gt;https://opennetworking.org/sdn-definition/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cs.princeton.edu/courses/archive/fall13/cos597E/papers/sdnhistory.pdf&quot;&gt;https://www.cs.princeton.edu/courses/archive/fall13/cos597E/papers/sdnhistory.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.koreascience.or.kr/article/JAKO201302757805044.pdf&quot;&gt;https://www.koreascience.or.kr/article/JAKO201302757805044.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sktinsight.com/98995&quot;&gt;https://www.sktinsight.com/98995&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/387756&quot;&gt;https://d2.naver.com/helloworld/387756&lt;/a&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;</description>
      <category>IT/Cloud</category>
      <category>cloud</category>
      <category>G-Scale</category>
      <category>network</category>
      <category>Openflow</category>
      <category>SDN</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/48</guid>
      <comments>https://suyeon96.tistory.com/48#entry48comment</comments>
      <pubDate>Mon, 26 Jul 2021 10:27:00 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] AWS CloudFormation으로 인프라 배포 자동화</title>
      <link>https://suyeon96.tistory.com/47</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;IaC (Infrastructure as Code)란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IaC(Infrastructure as Code)&lt;/b&gt;란 Code로 인프라를 구축하고 관리하며 배포하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code 기반으로 인프라를 제어하고 관리한다는 것은 수많은 이점을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 반복되는 인프라 작업을 자동화시킬 수 있다. 자연스럽게 작업시간이 단축되며, 사람이 수동으로 작업하며 발생할 수 있는 휴먼에러를 최소화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 인프라 구성사항 및 작업내용을 문서화할 수 있다. 인프라의 일관성을 향상시킬 수 있으며, 버전을 관리할 수 있게 된다. 버전관리는 IaC의 큰 장점 중 하나이다.&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;IaC는 DevOps 개념에서 아주 중요한 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영자가 하던 인프라 작업의 대부분을 code로 처리할 수 있게 되며 개발과 운영의 경계가 옅어지고 있다. 인프라 관리를 위한 code와 애플리케이션 실행을 위한 code의 경계도 좁아지고 있으며, 인프라 프로비저닝과 애플리케이션 배포를 동시에 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 DevOps는 충분히 수면위로 떠올랐으며, IaC 또한 더 이상 새로운 기술이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로는 멀지 않은 미래에 대부분의 인프라 관리와 작업이 code 기반으로 완전하게 전환될 것이라고 생각한다. (즉, Operator 직무도 개발을 기본으로 해야 한다는 이야기... 반대로 말하면 개발자도 인프라를 알아야 한다는 이야기...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IaC 툴에는 대표적으로 Terraform, Chef, Ansible, Puppet, AWS CloudFormation 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 포스팅에서는 AWS CloudFormation을 통해 AWS 인프라를 자동으로 배포하는 방법에 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 AWS CloudFormation은 무료이다. (&lt;code&gt;AWS::*&lt;/code&gt;, &lt;code&gt;Alexa::*&lt;/code&gt;, &lt;code&gt;Custom::*&lt;/code&gt;과 같은 네임스페이스의 리소스 공급자에서 사용하는 경우)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 외부의 리소스 공급자를 사용하는 경우 핸들러 작업 당 요금이 부과된다. (참고 : &lt;a href=&quot;https://aws.amazon.com/cloudformation/pricing/&quot;&gt;https://aws.amazon.com/cloudformation/pricing/&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS CloudFormation 동작 원리&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;301&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cw21Mm/btq9ReafqYG/cGO6bEpFGKrcmn1wfaKjq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cw21Mm/btq9ReafqYG/cGO6bEpFGKrcmn1wfaKjq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cw21Mm/btq9ReafqYG/cGO6bEpFGKrcmn1wfaKjq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcw21Mm%2Fbtq9ReafqYG%2FcGO6bEpFGKrcmn1wfaKjq1%2Fimg.png&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;301&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. template 작성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON/YAML 코드로 템플릿을 작성한다. &lt;code&gt;.json&lt;/code&gt;, &lt;code&gt;.yaml&lt;/code&gt;, &lt;code&gt;.template&lt;/code&gt;, &lt;code&gt;.txt&lt;/code&gt; 등 모든 확장명으로 파일을 저장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿은 5가지 유형의 요소로 구성된다.&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;출력 값의 선택적 목록(예: 웹 애플리케이션에 대한 전체 URL)&lt;/li&gt;
&lt;li&gt;정적 구성 값을 조회하는 데 사용되는 데이터 테이블의 선택적 목록(예: AMI 이름)&lt;/li&gt;
&lt;li&gt;AWS 리소스 및 해당 구성 값 목록&lt;/li&gt;
&lt;li&gt;템플릿 파일 형식 버전 번호&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. template 업로드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 템플릿을 local 또는 S3 버킷에 저장하여 업로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. stack 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloudformation은 스택이라는 하나의 단위로 관련 리소스를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택의 모든 리소스는 템플릿으로 정의되며, Cloudformation은 템플릿 코드를 읽어 스택을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 템플릿에 파라미터가 포함된 경우 스택을 생성할 때 입력 값을 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. stack 설정 및 resource 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3단계와 4단계의 구분은 사실 의미가 없긴 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿에 정의된 리소스가 생성된 후 CloudFormation은 유저에게 스택이 생성되었다고 보고한다.&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;CloudFormation은 수정된 템플릿을 원본 템플릿과 비교하여 변경 사항을 검토하고 스택을 업데이트한다.&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;AWS CloudFormation 실습 시나리오&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. network 계층 배포&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;network 인프라 구성을 위한 템플릿 파일은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿에 대한 해석은 따로 하지 않겠다. AWS를 이용해보았다면 코드만 보아도 대충 감은 잡힐 것이다. 자세한 내역이 궁금하다면 &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-guide.html&quot;&gt;가이드문서&lt;/a&gt;를 참고 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;lab-network.yaml&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;AWSTemplateFormatVersion: 2010-09-09
Description: &amp;gt;-
  Network Template: Sample template that creates a VPC with DNS and public IPs enabled.

# This template creates:
#   VPC
#   Internet Gateway
#   Public Route Table
#   Public Subnet

######################
# Resources section
######################

Resources:

  ## VPC

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsSupport: true
      EnableDnsHostnames: true
      CidrBlock: 10.0.0.0/16

  ## Internet Gateway

  InternetGateway:
    Type: AWS::EC2::InternetGateway

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  ## Public Route Table

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  ## Public Subnet

  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: !Select 
        - 0
        - !GetAZs 
          Ref: AWS::Region

  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      NetworkAclId: !GetAtt 
        - VPC
        - DefaultNetworkAcl

######################
# Outputs section
######################

Outputs:

  PublicSubnet:
    Description: The subnet ID to use for public web servers
    Value: !Ref PublicSubnet
    Export:
      Name: !Sub '${AWS::StackName}-SubnetID'

  VPC:
    Description: VPC ID
    Value: !Ref VPC
    Export:
      Name: !Sub '${AWS::StackName}-VPCID'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.1 템플릿 지정&lt;/b&gt;&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-origin-width=&quot;1307&quot; data-origin-height=&quot;702&quot; data-filename=&quot;Untitled 1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wz0iM/btq9RdvwFW2/sFjEEpTkamN7afqGmspD40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wz0iM/btq9RdvwFW2/sFjEEpTkamN7afqGmspD40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wz0iM/btq9RdvwFW2/sFjEEpTkamN7afqGmspD40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWz0iM%2Fbtq9RdvwFW2%2FsFjEEpTkamN7afqGmspD40%2Fimg.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;702&quot; data-filename=&quot;Untitled 1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.2 Stack 세부정보 지정&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;539&quot; data-filename=&quot;Untitled 2.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GRBml/btq9KwpNDoN/tHgSToUdXknEdgxjGvz3L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GRBml/btq9KwpNDoN/tHgSToUdXknEdgxjGvz3L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GRBml/btq9KwpNDoN/tHgSToUdXknEdgxjGvz3L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGRBml%2Fbtq9KwpNDoN%2FtHgSToUdXknEdgxjGvz3L1%2Fimg.png&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;539&quot; data-filename=&quot;Untitled 2.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.3 Stack 옵션 구성&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;535&quot; data-filename=&quot;Untitled 3.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eq1Pnu/btq9R4LZoj8/53WX3dZvUXmT1hmNWR3Cyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eq1Pnu/btq9R4LZoj8/53WX3dZvUXmT1hmNWR3Cyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eq1Pnu/btq9R4LZoj8/53WX3dZvUXmT1hmNWR3Cyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feq1Pnu%2Fbtq9R4LZoj8%2F53WX3dZvUXmT1hmNWR3Cyk%2Fimg.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;535&quot; data-filename=&quot;Untitled 3.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.4 Stack 생성&lt;/b&gt;&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-origin-width=&quot;1455&quot; data-origin-height=&quot;732&quot; data-filename=&quot;Untitled 4.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qScvG/btq9NSeUtE2/gBTnCPS5UCUazgKKcvsVBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qScvG/btq9NSeUtE2/gBTnCPS5UCUazgKKcvsVBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qScvG/btq9NSeUtE2/gBTnCPS5UCUazgKKcvsVBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqScvG%2Fbtq9NSeUtE2%2FgBTnCPS5UCUazgKKcvsVBK%2Fimg.png&quot; data-origin-width=&quot;1455&quot; data-origin-height=&quot;732&quot; data-filename=&quot;Untitled 4.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.5 Resources 확인&lt;/b&gt;&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-origin-width=&quot;1395&quot; data-origin-height=&quot;655&quot; data-filename=&quot;Untitled 5.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eDRhNW/btq9MEOZ8da/Spw8EBiyBK3KKiMrdrOx1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eDRhNW/btq9MEOZ8da/Spw8EBiyBK3KKiMrdrOx1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eDRhNW/btq9MEOZ8da/Spw8EBiyBK3KKiMrdrOx1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeDRhNW%2Fbtq9MEOZ8da%2FSpw8EBiyBK3KKiMrdrOx1k%2Fimg.png&quot; data-origin-width=&quot;1395&quot; data-origin-height=&quot;655&quot; data-filename=&quot;Untitled 5.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.6 Events 확인&lt;/b&gt;&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-origin-width=&quot;1396&quot; data-origin-height=&quot;840&quot; data-filename=&quot;Untitled 6.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM7gf0/btq9MFtEEMf/8O9Ulty9dZJDBHSk3wEmc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM7gf0/btq9MFtEEMf/8O9Ulty9dZJDBHSk3wEmc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM7gf0/btq9MFtEEMf/8O9Ulty9dZJDBHSk3wEmc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM7gf0%2Fbtq9MFtEEMf%2F8O9Ulty9dZJDBHSk3wEmc1%2Fimg.png&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;840&quot; data-filename=&quot;Untitled 6.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.7 Outputs 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택 간에 정보를 공유하기 위해서 스택의 Output을 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자의 경우 위 템플릿의 마지막 문단에서 &lt;code&gt;PublicSubnet ID&lt;/code&gt;와 &lt;code&gt;VPC ID&lt;/code&gt;를 Output으로 지정해놓았고, 생성된 리소스들의 ID가 정상적으로 출력되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;391&quot; data-filename=&quot;Untitled 7.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nMNCp/btq9LqQYPsp/obRtWFFPUUih6nOK6DLobK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nMNCp/btq9LqQYPsp/obRtWFFPUUih6nOK6DLobK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nMNCp/btq9LqQYPsp/obRtWFFPUUih6nOK6DLobK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnMNCp%2Fbtq9LqQYPsp%2FobRtWFFPUUih6nOK6DLobK%2Fimg.png&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;391&quot; data-filename=&quot;Untitled 7.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. application 계층 배포&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;code&gt;lab-network&lt;/code&gt; 스택에서 생성한 리소스에 웹서버를 띄우기 위한 템플릿을 준비하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;lab-application.yaml&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;AWSTemplateFormatVersion: 2010-09-09
Description: &amp;gt;-
  Application Template: Demonstrates how to reference resources from a different stack.
  This template provisions an EC2 instance in a VPC Subnet provisioned in a different stack.

# This template creates:
#   Amazon EC2 instance
#   Security Group

######################
# Parameters section
######################

Parameters:

  NetworkStackName:
    Description: &amp;gt;-
      Name of an active CloudFormation stack that contains the networking
      resources, such as the VPC and subnet that will be used in this stack.
    Type: String
    MinLength: 1
    MaxLength: 255
    AllowedPattern: '^[a-zA-Z][-a-zA-Z0-9]*$'
    Default: lab-network

  AmazonLinuxAMIID:
    Type: AWS::SSM::Parameter::Value&amp;lt;AWS::EC2::Image::Id&amp;gt;
    Default: /aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2

######################
# Resources section
######################

Resources:

  WebServerInstance:
    Type: AWS::EC2::Instance
    Metadata:
      'AWS::CloudFormation::Init':
        configSets:
          All:
            - ConfigureSampleApp
        ConfigureSampleApp:
          packages:
            yum:
              httpd: []
          files:
            /var/www/html/index.html:
              content: |
                &amp;lt;img src=&quot;&amp;lt;https://s3.amazonaws.com/cloudformation-examples/cloudformation_graphic.png&amp;gt;&quot; alt=&quot;AWS CloudFormation Logo&quot;/&amp;gt;
                &amp;lt;h1&amp;gt;Congratulations, you have successfully launched the AWS CloudFormation sample.&amp;lt;/h1&amp;gt;
              mode: 000644
              owner: apache
              group: apache
          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true
    Properties:
      InstanceType: t2.micro
      ImageId: !Ref AmazonLinuxAMIID
      NetworkInterfaces:
        - GroupSet:
            - !Ref WebServerSecurityGroup
          AssociatePublicIpAddress: true
          DeviceIndex: 0
          DeleteOnTermination: true
          SubnetId:
            Fn::ImportValue:
              !Sub ${NetworkStackName}-SubnetID
      Tags:
        - Key: Name
          Value: Web Server
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          yum update -y aws-cfn-bootstrap
          # Install the files and packages from the metadata
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --configsets All --region ${AWS::Region}
          # Signal the status from cfn-init
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M

  DiskVolume:
    Type: AWS::EC2::Volume
    Properties:
      Size: 100
      AvailabilityZone: !GetAtt WebServerInstance.AvailabilityZone
      Tags:
        - Key: Name
          Value: Web Data
    DeletionPolicy: Snapshot

  DiskMountPoint:
    Type: AWS::EC2::VolumeAttachment
    Properties:
      InstanceId: !Ref WebServerInstance
      VolumeId: !Ref DiskVolume
      Device: /dev/sdh

  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable HTTP ingress
      VpcId:
        Fn::ImportValue:
          !Sub ${NetworkStackName}-VPCID
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: Web Server Security Group

######################
# Outputs section
######################

Outputs:
  URL:
    Description: URL of the sample website
    Value: !Sub '&amp;lt;http://$&amp;gt;{WebServerInstance.PublicDnsName}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.1 stack 생성&lt;/b&gt;&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-origin-width=&quot;1300&quot; data-origin-height=&quot;605&quot; data-filename=&quot;Untitled 8.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PCVXj/btq9O6X3umf/a7K4jdWNudkRQKXkNP3kJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PCVXj/btq9O6X3umf/a7K4jdWNudkRQKXkNP3kJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PCVXj/btq9O6X3umf/a7K4jdWNudkRQKXkNP3kJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPCVXj%2Fbtq9O6X3umf%2Fa7K4jdWNudkRQKXkNP3kJk%2Fimg.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;605&quot; data-filename=&quot;Untitled 8.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;다른 것이 있다면 parameter에 &lt;code&gt;lab-network&lt;/code&gt;를 지정해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;760&quot; data-filename=&quot;Untitled 9.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0fpct/btq9O8IlC0c/lj3Zqhsror2dd460mjyUmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0fpct/btq9O8IlC0c/lj3Zqhsror2dd460mjyUmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0fpct/btq9O8IlC0c/lj3Zqhsror2dd460mjyUmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0fpct%2Fbtq9O8IlC0c%2Flj3Zqhsror2dd460mjyUmk%2Fimg.png&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;760&quot; data-filename=&quot;Untitled 9.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.2 Resources 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿에 정의한 대로 EC2 인스턴스, 보안그룹, 디스크볼륨이 정상적으로 생성된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;467&quot; data-filename=&quot;Untitled 10.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rt0bA/btq9LrI47Wy/l8VEvakouKnugEbU8AGbvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rt0bA/btq9LrI47Wy/l8VEvakouKnugEbU8AGbvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rt0bA/btq9LrI47Wy/l8VEvakouKnugEbU8AGbvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frt0bA%2Fbtq9LrI47Wy%2Fl8VEvakouKnugEbU8AGbvk%2Fimg.png&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;467&quot; data-filename=&quot;Untitled 10.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.3 Outputs 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 웹서버를 띄웠기 때문에 외부에서 http(80)로 접속할 수 있도록 인스턴스의 &lt;code&gt;PublicDnsName&lt;/code&gt;을 출력하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;351&quot; data-filename=&quot;Untitled 11.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cflMZ4/btq9KmnNqlG/srk9jcQ6lVtUgnoxU4qquK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cflMZ4/btq9KmnNqlG/srk9jcQ6lVtUgnoxU4qquK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cflMZ4/btq9KmnNqlG/srk9jcQ6lVtUgnoxU4qquK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcflMZ4%2Fbtq9KmnNqlG%2Fsrk9jcQ6lVtUgnoxU4qquK%2Fimg.png&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;351&quot; data-filename=&quot;Untitled 11.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.4 Access 확인&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;926&quot; data-filename=&quot;Untitled 12.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Aalxl/btq9NQg9wjv/VRVP4N1EAxn5e14LtFAue0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Aalxl/btq9NQg9wjv/VRVP4N1EAxn5e14LtFAue0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Aalxl/btq9NQg9wjv/VRVP4N1EAxn5e14LtFAue0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAalxl%2Fbtq9NQg9wjv%2FVRVP4N1EAxn5e14LtFAue0%2Fimg.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;926&quot; data-filename=&quot;Untitled 12.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Stack update&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;code&gt;lab-application&lt;/code&gt; 스택을 업데이트하여 리소스가 변경되는 것을 테스트해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 템플릿에서 파일 contents를 변경하고 보안그룹에 인바운드 SSH 트래픽을 허용하도록 하는 코드를 추가하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;lab-application2.yaml&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;AWSTemplateFormatVersion: 2010-09-09
Description: &amp;gt;-
  Application Template: Demonstrates how to reference resources from a different stack.
  This template provisions an EC2 instance in a VPC Subnet provisioned in a different stack.

# This template creates:
#   Amazon EC2 instance
#   Security Group

######################
# Parameters section
######################

Parameters:

  NetworkStackName:
    Description: &amp;gt;-
      Name of an active CloudFormation stack that contains the networking
      resources, such as the VPC and subnet that will be used in this stack.
    Type: String
    MinLength: 1
    MaxLength: 255
    AllowedPattern: '^[a-zA-Z][-a-zA-Z0-9]*$'
    Default: lab-network

  AmazonLinuxAMIID:
    Type: AWS::SSM::Parameter::Value&amp;lt;AWS::EC2::Image::Id&amp;gt;
    Default: /aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2

######################
# Resources section
######################

Resources:

  WebServerInstance:
    Type: AWS::EC2::Instance
    Metadata:
      'AWS::CloudFormation::Init':
        configSets:
          All:
            - ConfigureSampleApp
        ConfigureSampleApp:
          packages:
            yum:
              httpd: []
          files:
            /var/www/html/index.html:
              content: |
                &amp;lt;img src=&quot;&amp;lt;https://s3.amazonaws.com/cloudformation-examples/cloudformation_graphic.png&amp;gt;&quot; alt=&quot;AWS CloudFormation Logo&quot;/&amp;gt;
                &amp;lt;h1&amp;gt;Congratulations, you have successfully launched the AWS CloudFormation sample.&amp;lt;/h1&amp;gt;
                &amp;lt;h3&amp;gt;Update Stack Test - by Suyeon&amp;lt;h3&amp;gt;
              mode: 000644
              owner: apache
              group: apache
          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true
    Properties:
      InstanceType: t2.micro
      ImageId: !Ref AmazonLinuxAMIID
      NetworkInterfaces:
        - GroupSet:
            - !Ref WebServerSecurityGroup
          AssociatePublicIpAddress: true
          DeviceIndex: 0
          DeleteOnTermination: true
          SubnetId:
            Fn::ImportValue:
              !Sub ${NetworkStackName}-SubnetID
      Tags:
        - Key: Name
          Value: Web Server
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          yum update -y aws-cfn-bootstrap
          # Install the files and packages from the metadata
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --configsets All --region ${AWS::Region}
          # Signal the status from cfn-init
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M

  DiskVolume:
    Type: AWS::EC2::Volume
    Properties:
      Size: 100
      AvailabilityZone: !GetAtt WebServerInstance.AvailabilityZone
      Tags:
        - Key: Name
          Value: Web Data
    DeletionPolicy: Snapshot

  DiskMountPoint:
    Type: AWS::EC2::VolumeAttachment
    Properties:
      InstanceId: !Ref WebServerInstance
      VolumeId: !Ref DiskVolume
      Device: /dev/sdh

  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable HTTP ingress
      VpcId:
        Fn::ImportValue:
          !Sub ${NetworkStackName}-VPCID
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: Web Server Security Group

######################
# Outputs section
######################

Outputs:
  URL:
    Description: URL of the sample website
    Value: !Sub '&amp;lt;http://$&amp;gt;{WebServerInstance.PublicDnsName}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.1 Update Stack&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;699&quot; data-filename=&quot;Untitled 13.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R4hlS/btq9KmuA19N/cj25oIyNrFKY6xsZziztKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R4hlS/btq9KmuA19N/cj25oIyNrFKY6xsZziztKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R4hlS/btq9KmuA19N/cj25oIyNrFKY6xsZziztKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR4hlS%2Fbtq9KmuA19N%2Fcj25oIyNrFKY6xsZziztKk%2Fimg.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;699&quot; data-filename=&quot;Untitled 13.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.2 변경사항 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Review 단계에서 변경사항을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Replacement = False&lt;/code&gt;라는 것은 각 리소스에 최소한의 변경만 있으며, 해당 리소스에 대한 참조는 변경할 필요가 없다는 것을 의미한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;389&quot; data-filename=&quot;Untitled 14.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mgXac/btq9KmnNq3K/qgVPHgrkmMnkbqvApt2G01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mgXac/btq9KmnNq3K/qgVPHgrkmMnkbqvApt2G01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mgXac/btq9KmnNq3K/qgVPHgrkmMnkbqvApt2G01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmgXac%2Fbtq9KmnNq3K%2FqgVPHgrkmMnkbqvApt2G01%2Fimg.png&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;389&quot; data-filename=&quot;Untitled 14.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.3 update 중...&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Status를 보면 update 중인 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1377&quot; data-origin-height=&quot;699&quot; data-filename=&quot;Untitled 15.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MenID/btq9Lqi8eSi/qQkf1Sn0YwVHZe2H0rgO8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MenID/btq9Lqi8eSi/qQkf1Sn0YwVHZe2H0rgO8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MenID/btq9Lqi8eSi/qQkf1Sn0YwVHZe2H0rgO8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMenID%2Fbtq9Lqi8eSi%2FqQkf1Sn0YwVHZe2H0rgO8k%2Fimg.png&quot; data-origin-width=&quot;1377&quot; data-origin-height=&quot;699&quot; data-filename=&quot;Untitled 15.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.4 Events 확인&lt;/b&gt;&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-origin-width=&quot;1074&quot; data-origin-height=&quot;263&quot; data-filename=&quot;Untitled 16.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lBcPX/btq9Lp5DOtF/GFrBQmthsFMs8y6GpUXLpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lBcPX/btq9Lp5DOtF/GFrBQmthsFMs8y6GpUXLpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lBcPX/btq9Lp5DOtF/GFrBQmthsFMs8y6GpUXLpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlBcPX%2Fbtq9Lp5DOtF%2FGFrBQmthsFMs8y6GpUXLpK%2Fimg.png&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;263&quot; data-filename=&quot;Untitled 16.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.5 리소스 변경 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Security Group 대시보드에서 SSH(22) rule이 추가된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;633&quot; data-filename=&quot;Untitled 17.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WhYWt/btq9NsOjSdr/5clqoAkuIdFURHF3ukvYk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WhYWt/btq9NsOjSdr/5clqoAkuIdFURHF3ukvYk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WhYWt/btq9NsOjSdr/5clqoAkuIdFURHF3ukvYk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWhYWt%2Fbtq9NsOjSdr%2F5clqoAkuIdFURHF3ukvYk0%2Fimg.png&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;633&quot; data-filename=&quot;Untitled 17.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. AWS CloudFormation Designer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CloudFormation Designer는 템플릿을 간편하게 확인하고 제어하기 위한 그래픽 기반의 도구이다.&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-origin-width=&quot;1529&quot; data-origin-height=&quot;768&quot; data-filename=&quot;Untitled 18.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cylbCs/btq9Kn76Pqj/BlHeIp5RKx5t4YkDInqeY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cylbCs/btq9Kn76Pqj/BlHeIp5RKx5t4YkDInqeY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cylbCs/btq9Kn76Pqj/BlHeIp5RKx5t4YkDInqeY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcylbCs%2Fbtq9Kn76Pqj%2FBlHeIp5RKx5t4YkDInqeY1%2Fimg.png&quot; data-origin-width=&quot;1529&quot; data-origin-height=&quot;768&quot; data-filename=&quot;Untitled 18.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;765&quot; data-filename=&quot;Untitled 19.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MYoUS/btq9NsHyUta/51ht0aDZsiUnXvfkxqkLJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MYoUS/btq9NsHyUta/51ht0aDZsiUnXvfkxqkLJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MYoUS/btq9NsHyUta/51ht0aDZsiUnXvfkxqkLJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMYoUS%2Fbtq9NsHyUta%2F51ht0aDZsiUnXvfkxqkLJk%2Fimg.png&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;765&quot; data-filename=&quot;Untitled 19.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;5. Stack 삭제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5.1 Delete Stack&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 Stack을 삭제할 차례이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;235&quot; data-filename=&quot;Untitled 20.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0EDIF/btq9NtfoH31/1vfM1YvjpvDXkcckoHKE7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0EDIF/btq9NtfoH31/1vfM1YvjpvDXkcckoHKE7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0EDIF/btq9NtfoH31/1vfM1YvjpvDXkcckoHKE7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0EDIF%2Fbtq9NtfoH31%2F1vfM1YvjpvDXkcckoHKE7K%2Fimg.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;235&quot; data-filename=&quot;Untitled 20.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5.2 Events 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Events를 보면 스택의 리소스들이 삭제되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;713&quot; data-filename=&quot;Untitled 21.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b510x6/btq9PUJRIA9/SP4n8yV7fC2yKSrIfC8r00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b510x6/btq9PUJRIA9/SP4n8yV7fC2yKSrIfC8r00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b510x6/btq9PUJRIA9/SP4n8yV7fC2yKSrIfC8r00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb510x6%2Fbtq9PUJRIA9%2FSP4n8yV7fC2yKSrIfC8r00%2Fimg.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;713&quot; data-filename=&quot;Untitled 21.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5.3 DeletionPolicy&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제 정책을 이용하면 스택이 삭제될 때 리소스를 보존하거나 백업할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;DeletionPolicy&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;code&gt;Retain&lt;/code&gt; 옵션을 지정하면 스택 삭제 시 리소스가 유지된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Snapshot&lt;/code&gt; 옵션을 지정하면 해당 리소스를 삭제하기 전에 리소스의 스냅샷을 생성한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Delete&lt;/code&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;code&gt;Snapshot&lt;/code&gt; 옵션을 선택한 경우 아래와 같이 DataVolume에 대한 스냅샷이 생성된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;462&quot; data-filename=&quot;Untitled 22.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bumxH2/btq9R4kVlDr/fojUN9RvsfPguW8YakMhuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bumxH2/btq9R4kVlDr/fojUN9RvsfPguW8YakMhuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bumxH2/btq9R4kVlDr/fojUN9RvsfPguW8YakMhuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbumxH2%2Fbtq9R4kVlDr%2FfojUN9RvsfPguW8YakMhuk%2Fimg.png&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;462&quot; data-filename=&quot;Untitled 22.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Cloud</category>
      <category>AWS</category>
      <category>CloudFormation</category>
      <category>IaC</category>
      <category>Infrastructure as Code</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/47</guid>
      <comments>https://suyeon96.tistory.com/47#entry47comment</comments>
      <pubDate>Sat, 17 Jul 2021 13:53:57 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] 단계별 시나리오를 통해 알아보는 EC2 Auto Scaling</title>
      <link>https://suyeon96.tistory.com/46</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;AutoScaling이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 컴퓨팅의 대표적인 장점 중 하나는 유연성이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유연성이라는 단어는 상당히 추상적이며 많은 의미를 내포하고 있지만 그 중 핵심은 빠르고 쉽게 서비스를 확장하거나 축소하는 것을 의미할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Auto Scaling&lt;/b&gt;은 이 유연성을 돋보이게 하는 핵심 기술로 CPU, Memory, Disk, Network 등의 시스템 Metric값이나 Application을 &lt;b&gt;모니터링하고 size를 자동으로 조절&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;Auto Scaling을 통해 얻을 수 있는 이득은 크게 2가지로 정의할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예상치 못한 서비스 부하에 효과적으로 대응할 수 있다.&lt;/li&gt;
&lt;li&gt;최대한 저렴한 비용으로 안정적이고 예측 가능한 성능을 유지할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS Auto Scaling Resource&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서는 인스턴스, DB 등 다양한 서비스에서 Auto Scaling을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 오늘 포스팅에서는 EC2 Auto Scaling에 대해 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;268&quot; data-filename=&quot;Untitled.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gbtc2/btq9pDnTGt4/XBhvDfRrP8motuqxDsoHwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gbtc2/btq9pDnTGt4/XBhvDfRrP8motuqxDsoHwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gbtc2/btq9pDnTGt4/XBhvDfRrP8motuqxDsoHwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGbtc2%2Fbtq9pDnTGt4%2FXBhvDfRrP8motuqxDsoHwK%2Fimg.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;268&quot; data-filename=&quot;Untitled.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;EC2 Auto Scaling 실습 시나리오&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. S3 버킷 및 IAM 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 ec2에 연결할 S3 버킷을 생성한다.&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-origin-width=&quot;840&quot; data-origin-height=&quot;79&quot; data-filename=&quot;Untitled 1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PqhBh/btq9lmNCI8H/IzrSm1yAKRKX9gmCflpKUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PqhBh/btq9lmNCI8H/IzrSm1yAKRKX9gmCflpKUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PqhBh/btq9lmNCI8H/IzrSm1yAKRKX9gmCflpKUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPqhBh%2Fbtq9lmNCI8H%2FIzrSm1yAKRKX9gmCflpKUK%2Fimg.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;79&quot; data-filename=&quot;Untitled 1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;web 서버를 띄우고 나서 보여줄 index.html를 작성하고 저장한다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;AUSG Bigchat&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;Auto Scaling Test!!&amp;lt;/h1&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;838&quot; data-origin-height=&quot;279&quot; data-filename=&quot;Untitled 2.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGh1YC/btq9pC3zSXi/NncK70lFYfPVyOEwXxxakK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGh1YC/btq9pC3zSXi/NncK70lFYfPVyOEwXxxakK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGh1YC/btq9pC3zSXi/NncK70lFYfPVyOEwXxxakK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGh1YC%2Fbtq9pC3zSXi%2FNncK70lFYfPVyOEwXxxakK%2Fimg.png&quot; data-origin-width=&quot;838&quot; data-origin-height=&quot;279&quot; data-filename=&quot;Untitled 2.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Auto Scaling을 통해 생성될 ec2 인스턴스에 s3 access 권한을 부여하기 위해 IAM도 생성해주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;80&quot; data-filename=&quot;Untitled 3.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqJsDz/btq9lmUo5LL/lPVSIs4UwWfXBHjaA4g241/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqJsDz/btq9lmUo5LL/lPVSIs4UwWfXBHjaA4g241/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqJsDz/btq9lmUo5LL/lPVSIs4UwWfXBHjaA4g241/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqJsDz%2Fbtq9lmUo5LL%2FlPVSIs4UwWfXBHjaA4g241%2Fimg.png&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;80&quot; data-filename=&quot;Untitled 3.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Policy : AmazonS3FullAccess&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. VPC 및 Subnet 구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auto Scaling을 테스트할 인프라 구성을 위해 VPC를 하나 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;182&quot; data-filename=&quot;Untitled 4.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FqRwS/btq9mlON3pe/IopFxTrHCV5iNM7mKKpwRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FqRwS/btq9mlON3pe/IopFxTrHCV5iNM7mKKpwRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FqRwS/btq9mlON3pe/IopFxTrHCV5iNM7mKKpwRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFqRwS%2Fbtq9mlON3pe%2FIopFxTrHCV5iNM7mKKpwRk%2Fimg.png&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;182&quot; data-filename=&quot;Untitled 4.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Public Subnet도 2개 생성해주고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;108&quot; data-filename=&quot;Untitled 5.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEQFW5/btq9gRnjFGj/jL2JxUaYvKYEUi8boOWKUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEQFW5/btq9gRnjFGj/jL2JxUaYvKYEUi8boOWKUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEQFW5/btq9gRnjFGj/jL2JxUaYvKYEUi8boOWKUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEQFW5%2Fbtq9gRnjFGj%2FjL2JxUaYvKYEUi8boOWKUK%2Fimg.png&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;108&quot; data-filename=&quot;Untitled 5.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;이전 포스팅에서 다뤘던 것처럼 Internet Gateway 생성 후 VPC와 연결해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;81&quot; data-filename=&quot;Untitled 6.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clCmfS/btq9fceTWvO/VEL4E5ShvToZ1OB7XSgAC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clCmfS/btq9fceTWvO/VEL4E5ShvToZ1OB7XSgAC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clCmfS/btq9fceTWvO/VEL4E5ShvToZ1OB7XSgAC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclCmfS%2Fbtq9fceTWvO%2FVEL4E5ShvToZ1OB7XSgAC1%2Fimg.png&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;81&quot; data-filename=&quot;Untitled 6.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Route Table 생성 후 Public Subnet 2개와 연결해주고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;78&quot; data-filename=&quot;Untitled 7.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1FuMg/btq9jvYmXIy/5d2Jjk6SakzpC5p343kpPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1FuMg/btq9jvYmXIy/5d2Jjk6SakzpC5p343kpPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1FuMg/btq9jvYmXIy/5d2Jjk6SakzpC5p343kpPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1FuMg%2Fbtq9jvYmXIy%2F5d2Jjk6SakzpC5p343kpPk%2Fimg.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;78&quot; data-filename=&quot;Untitled 7.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;인터넷 연결을 위해 0.0.0.0/0 (모든 네트워크)에 대해서 Routes를 추가해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;121&quot; data-filename=&quot;Untitled 8.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LjsUk/btq9nB4WK0P/LRJlKagc7jzu7E526CRLPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LjsUk/btq9nB4WK0P/LRJlKagc7jzu7E526CRLPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LjsUk/btq9nB4WK0P/LRJlKagc7jzu7E526CRLPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLjsUk%2Fbtq9nB4WK0P%2FLRJlKagc7jzu7E526CRLPK%2Fimg.png&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;121&quot; data-filename=&quot;Untitled 8.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. EC2 AMI 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auto Scaling에 사용할 AMI를 생성할 차례이다.&lt;/p&gt;
&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-origin-width=&quot;1217&quot; data-origin-height=&quot;70&quot; data-filename=&quot;Untitled 9.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTDyIL/btq9lm1bgHZ/MRgrLvExcStfW1iUZE9Fy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTDyIL/btq9lm1bgHZ/MRgrLvExcStfW1iUZE9Fy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTDyIL/btq9lm1bgHZ/MRgrLvExcStfW1iUZE9Fy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTDyIL%2Fbtq9lm1bgHZ%2FMRgrLvExcStfW1iUZE9Fy0%2Fimg.png&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;70&quot; data-filename=&quot;Untitled 9.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;AMI : Amazon Linux 2 AMI (HVM), SSD Volume Type&lt;/li&gt;
&lt;li&gt;Instance Type : t3.micro (2vCPU 1GiB)&lt;/li&gt;
&lt;li&gt;Network : 위에서 만든 &lt;code&gt;ausg-bigchat-as-test-VPC&lt;/code&gt; VPC&lt;/li&gt;
&lt;li&gt;Subnet : 위에서 만든 &lt;code&gt;as-test-Subnet-Public1&lt;/code&gt; 서브넷&lt;/li&gt;
&lt;li&gt;IAM Role : 위에서 만든 &lt;code&gt;s3_fullaccess_as_test&lt;/code&gt; IAM&lt;/li&gt;
&lt;li&gt;User Data&lt;br /&gt;
&lt;pre id=&quot;code_1626020278173&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

yum install -y httpd
systemctl start httpd.service
systemctl enable httpd.service

aws s3 cp s3://ausg-bigchat-as-test/index.html /var/www/html &amp;ndash;region ap-northeast-2​&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user data란 ec2 인스턴스가 올라가면서 수행할 action을 정해주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 시나리오에서는 간단하게 웹서버(httpd)를 설치하고 아까 만든 S3버킷에서 index.html을 받아올 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 인스턴스를 기준으로 AMI를 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;438&quot; data-filename=&quot;Untitled 10.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HBk0p/btq9pCvKUm6/AXWYXHBF7h9uVZ31kJo2ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HBk0p/btq9pCvKUm6/AXWYXHBF7h9uVZ31kJo2ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HBk0p/btq9pCvKUm6/AXWYXHBF7h9uVZ31kJo2ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHBk0p%2Fbtq9pCvKUm6%2FAXWYXHBF7h9uVZ31kJo2ck%2Fimg.png&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;438&quot; data-filename=&quot;Untitled 10.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;77&quot; data-filename=&quot;Untitled 11.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V0LBE/btq9mlOOcmI/w4Cz2haYlXVr3PofTOnk7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V0LBE/btq9mlOOcmI/w4Cz2haYlXVr3PofTOnk7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V0LBE/btq9mlOOcmI/w4Cz2haYlXVr3PofTOnk7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV0LBE%2Fbtq9mlOOcmI%2Fw4Cz2haYlXVr3PofTOnk7k%2Fimg.png&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;77&quot; data-filename=&quot;Untitled 11.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Elastic Load Balancer 구축&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1319&quot; data-origin-height=&quot;77&quot; data-filename=&quot;Untitled 12.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7YQBO/btq9fwYvo84/7KTjUvKjCWN2eWyib7aZAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7YQBO/btq9fwYvo84/7KTjUvKjCWN2eWyib7aZAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7YQBO/btq9fwYvo84/7KTjUvKjCWN2eWyib7aZAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7YQBO%2Fbtq9fwYvo84%2F7KTjUvKjCWN2eWyib7aZAk%2Fimg.png&quot; data-origin-width=&quot;1319&quot; data-origin-height=&quot;77&quot; data-filename=&quot;Untitled 12.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Type : Application (L7)&lt;/li&gt;
&lt;li&gt;체계 : internet-facing&lt;/li&gt;
&lt;li&gt;IP 주소 유형 : ipv4&lt;/li&gt;
&lt;li&gt;VPC 및 Subnet : 위에서 만든 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TargetGroup을 생성하고 대상으로 생성해놓은 ec2 web서버를 지정한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;96&quot; data-filename=&quot;Untitled 14.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mN8hV/btq9kQIxs5R/S9Yu3WTBV1DdWYkkKeYo41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mN8hV/btq9kQIxs5R/S9Yu3WTBV1DdWYkkKeYo41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mN8hV/btq9kQIxs5R/S9Yu3WTBV1DdWYkkKeYo41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmN8hV%2Fbtq9kQIxs5R%2FS9Yu3WTBV1DdWYkkKeYo41%2Fimg.png&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;96&quot; data-filename=&quot;Untitled 14.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;98&quot; data-filename=&quot;Untitled 15.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xfl8D/btq9gXWcUiB/JRpwXaBhRWdjB83WgZWQb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xfl8D/btq9gXWcUiB/JRpwXaBhRWdjB83WgZWQb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xfl8D/btq9gXWcUiB/JRpwXaBhRWdjB83WgZWQb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxfl8D%2Fbtq9gXWcUiB%2FJRpwXaBhRWdjB83WgZWQb1%2Fimg.png&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;98&quot; data-filename=&quot;Untitled 15.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Autoscaling Launch Configuration 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auto Scaling Launch Configuration(시작 구성) 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;95&quot; data-filename=&quot;Untitled 16.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bppLSJ/btq9iBrCSNT/fQwMdPV50iU9kcuuUtdAZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bppLSJ/btq9iBrCSNT/fQwMdPV50iU9kcuuUtdAZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bppLSJ/btq9iBrCSNT/fQwMdPV50iU9kcuuUtdAZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbppLSJ%2Fbtq9iBrCSNT%2FfQwMdPV50iU9kcuuUtdAZk%2Fimg.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;95&quot; data-filename=&quot;Untitled 16.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Amazon Machine Image(AMI) : web서버 인스턴스로부터 만든 이미지&lt;/li&gt;
&lt;li&gt;Instance Type : t3.micro (2 vCPU, 1 GiB, EBS 전용)&lt;/li&gt;
&lt;li&gt;IAM : s3_fullaccess_as_test&lt;/li&gt;
&lt;li&gt;Security Group : 동일하게&lt;/li&gt;
&lt;li&gt;Key pair : 동일하게&lt;/li&gt;
&lt;li&gt;user data
&lt;pre id=&quot;code_1626020479253&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

yum install -y httpd
systemctl start httpd.service
systemctl enable httpd.service

aws s3 cp s3://ausg-bigchat-as-test/index.html /var/www/html &amp;ndash;region ap-northeast-2​&lt;/code&gt;&lt;/pre&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;5. Auto Scaling Group 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) Launch Configuration 선택&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만들었듯이 Launch Configuration은 오토스케일링 그룹에서 생성될 서버의 사양과 설정을 정의하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;758&quot; data-filename=&quot;Untitled 17.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccjAQw/btq9kQhtdSW/RVKsPkjMeripnlDU1mwkY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccjAQw/btq9kQhtdSW/RVKsPkjMeripnlDU1mwkY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccjAQw/btq9kQhtdSW/RVKsPkjMeripnlDU1mwkY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccjAQw%2Fbtq9kQhtdSW%2FRVKsPkjMeripnlDU1mwkY1%2Fimg.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;758&quot; data-filename=&quot;Untitled 17.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;div&gt;&lt;b&gt;2) &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;VPC 및 Subnet 선택&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;742&quot; data-filename=&quot;Untitled 18.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQpmu/btq9hyoiUCp/K7LYqc0RwTu1ECQ4nsua61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQpmu/btq9hyoiUCp/K7LYqc0RwTu1ECQ4nsua61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQpmu/btq9hyoiUCp/K7LYqc0RwTu1ECQ4nsua61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQpmu%2Fbtq9hyoiUCp%2FK7LYqc0RwTu1ECQ4nsua61%2Fimg.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;742&quot; data-filename=&quot;Untitled 18.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. ELB 연결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elastic Load Balancing(ELB) 상태 확인을 활성화하면 연결된 로드 밸런서가 Amazon EC2 상태 확인에 실패할 때뿐만 아니라 비정상으로 보고할 때 Amazon EC2 Auto Scaling에서 인스턴스를 교체할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;747&quot; data-filename=&quot;Untitled 19.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fw3Ds/btq9gZl7Nfs/6HcSRkaG8XpCgfYqh6Zyhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fw3Ds/btq9gZl7Nfs/6HcSRkaG8XpCgfYqh6Zyhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fw3Ds/btq9gZl7Nfs/6HcSRkaG8XpCgfYqh6Zyhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFw3Ds%2Fbtq9gZl7Nfs%2F6HcSRkaG8XpCgfYqh6Zyhk%2Fimg.png&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;747&quot; data-filename=&quot;Untitled 19.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;267&quot; data-filename=&quot;Untitled 20.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWXcG3/btq9gX2XIZJ/ZCNNKU7S8MaS2kB1qqWeX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWXcG3/btq9gX2XIZJ/ZCNNKU7S8MaS2kB1qqWeX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWXcG3/btq9gX2XIZJ/ZCNNKU7S8MaS2kB1qqWeX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWXcG3%2Fbtq9gX2XIZJ%2FZCNNKU7S8MaS2kB1qqWeX1%2Fimg.png&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;267&quot; data-filename=&quot;Untitled 20.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태확인 유예 기간은 300초가 적당하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우후죽순으로 빨리빨리 만들어져봐야 소용없기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) Auto Scaling Group Size&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Desired Size : 오토스케일링 시 원하는 서버 수를 지정&lt;/li&gt;
&lt;li&gt;Min Size : 오토스케일링 그룹의 최소 유지 서버 수 (Desired &amp;gt; = MinSize)&lt;/li&gt;
&lt;li&gt;Max Size : 오토스케일링 그룹의 최대 보유 서버 수 (Desired &amp;lt; = MaxSize)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;486&quot; data-filename=&quot;Untitled 21.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qSYWY/btq9gzgUoU8/CsBcMkvDwTI8PwNbPXGxM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qSYWY/btq9gzgUoU8/CsBcMkvDwTI8PwNbPXGxM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qSYWY/btq9gzgUoU8/CsBcMkvDwTI8PwNbPXGxM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqSYWY%2Fbtq9gzgUoU8%2FCsBcMkvDwTI8PwNbPXGxM1%2Fimg.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;486&quot; data-filename=&quot;Untitled 21.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) scaling policies&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오토스케일링 그룹에 포함된 가상 서버의 시스템 메트릭 값이 미리 지정한 임계치를 초과할 경우 가상서버 수를 늘리고(Scale-out), 임계치 미만이면 가성 서버 수를 줄이도록(Scale-in) 설정할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;583&quot; data-filename=&quot;Untitled 22.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEXsQG/btq9nDPdm9A/VuER5An7QwHgc0U10fezU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEXsQG/btq9nDPdm9A/VuER5An7QwHgc0U10fezU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEXsQG/btq9nDPdm9A/VuER5An7QwHgc0U10fezU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEXsQG%2Fbtq9nDPdm9A%2FVuER5An7QwHgc0U10fezU0%2Fimg.png&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;583&quot; data-filename=&quot;Untitled 22.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지표 유형에는 아래와 같은 것들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평균 CPU 사용률&lt;/li&gt;
&lt;li&gt;평균 네트워크 입력 (bytes)&lt;/li&gt;
&lt;li&gt;평균 네트워크 출력 (bytes)&lt;/li&gt;
&lt;li&gt;대상당 Application Load Balancer 요청 수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) 알림 추가&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;793&quot; data-origin-height=&quot;550&quot; data-filename=&quot;Untitled 23.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsscOy/btq9nDaBTug/rTZnPx6evkYaMAa21obIi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsscOy/btq9nDaBTug/rTZnPx6evkYaMAa21obIi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsscOy/btq9nDaBTug/rTZnPx6evkYaMAa21obIi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsscOy%2Fbtq9nDaBTug%2FrTZnPx6evkYaMAa21obIi0%2Fimg.png&quot; data-origin-width=&quot;793&quot; data-origin-height=&quot;550&quot; data-filename=&quot;Untitled 23.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;6. Autoscaling Group 생성 후 인스턴스 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;359&quot; data-filename=&quot;Untitled 24.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGXCYl/btq9gXWcZBH/d2Ymf7K7kWKUgXK9k3l0L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGXCYl/btq9gXWcZBH/d2Ymf7K7kWKUgXK9k3l0L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGXCYl/btq9gXWcZBH/d2Ymf7K7kWKUgXK9k3l0L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGXCYl%2Fbtq9gXWcZBH%2Fd2Ymf7K7kWKUgXK9k3l0L1%2Fimg.png&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;359&quot; data-filename=&quot;Untitled 24.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;78&quot; data-filename=&quot;Untitled 25.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOVS6/btq9gXBQCFy/0FEkCLIInGkSCFitHK4Duk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOVS6/btq9gXBQCFy/0FEkCLIInGkSCFitHK4Duk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOVS6/btq9gXBQCFy/0FEkCLIInGkSCFitHK4Duk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOVS6%2Fbtq9gXBQCFy%2F0FEkCLIInGkSCFitHK4Duk%2Fimg.png&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;78&quot; data-filename=&quot;Untitled 25.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Desired Size를 2로 지정하였기 때문에 2대의 ec2 인스턴스가 올라온 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. ELB Target Group 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scale-out 된 인스턴스들이 ELB에 자동으로 연결된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;414&quot; data-filename=&quot;Untitled 26.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUduof/btq9lT55ZFK/1vBQagOlOjIW9UcyiRLCJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUduof/btq9lT55ZFK/1vBQagOlOjIW9UcyiRLCJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUduof/btq9lT55ZFK/1vBQagOlOjIW9UcyiRLCJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUduof%2Fbtq9lT55ZFK%2F1vBQagOlOjIW9UcyiRLCJK%2Fimg.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;414&quot; data-filename=&quot;Untitled 26.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;현재 Scale-out 된 인스턴스들에 웹서버 설치가 되지 않아 &lt;code&gt;unhealthy&lt;/code&gt; 상태로 체크된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 Auto Scaling Group 생성 시 Health Check Type에 &lt;code&gt;ELB&lt;/code&gt;를 체크하였기 때문에 &lt;code&gt;unhealthy&lt;/code&gt; 상태의 인스턴스를 삭제하고 새로 생성하는 작업을 반복한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;425&quot; data-filename=&quot;Untitled 27.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2pAoU/btq9hyIBxrE/qIvakKY4F9VrrIqscHi8L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2pAoU/btq9hyIBxrE/qIvakKY4F9VrrIqscHi8L0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2pAoU/btq9hyIBxrE/qIvakKY4F9VrrIqscHi8L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2pAoU%2Fbtq9hyIBxrE%2FqIvakKY4F9VrrIqscHi8L0%2Fimg.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;425&quot; data-filename=&quot;Untitled 27.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;인프라 구성을 맞추기 위해 기존에 등록한 AMI용 인스턴스는 언바인딩 하였으며, ELB DNS 접속도 정상적으로 되는 것을 확인하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;200&quot; data-filename=&quot;Untitled 28.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WmOsX/btq9pCWO0aS/W8piYK6W04UjCaZkEc0in0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WmOsX/btq9pCWO0aS/W8piYK6W04UjCaZkEc0in0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WmOsX/btq9pCWO0aS/W8piYK6W04UjCaZkEc0in0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWmOsX%2Fbtq9pCWO0aS%2FW8piYK6W04UjCaZkEc0in0%2Fimg.png&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;200&quot; data-filename=&quot;Untitled 28.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;483&quot; data-filename=&quot;Untitled 29.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfKFnD/btq9kPJAwad/uK3DnQzQ2k1KKKKlKj5NsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfKFnD/btq9kPJAwad/uK3DnQzQ2k1KKKKlKj5NsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfKFnD/btq9kPJAwad/uK3DnQzQ2k1KKKKlKj5NsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfKFnD%2Fbtq9kPJAwad%2FuK3DnQzQ2k1KKKKlKj5NsK%2Fimg.png&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;483&quot; data-filename=&quot;Untitled 29.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;8. CloudWatch 경보 및 조정정책 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 단계 조정정책 또는 단순 조정정책을 생성하기 위해 CloudWatch 경보를 생성해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;470&quot; data-filename=&quot;Untitled 30.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnOR7a/btq9lTSA45y/xnTUCQwJbnwNKZyAGu3Lb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnOR7a/btq9lTSA45y/xnTUCQwJbnwNKZyAGu3Lb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnOR7a/btq9lTSA45y/xnTUCQwJbnwNKZyAGu3Lb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnOR7a%2Fbtq9lTSA45y%2FxnTUCQwJbnwNKZyAGu3Lb0%2Fimg.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;470&quot; data-filename=&quot;Untitled 30.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;339&quot; data-filename=&quot;Untitled 31.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u1CxV/btq9iBrCY9D/0xHxMIxhLt1b6nyB06YyP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u1CxV/btq9iBrCY9D/0xHxMIxhLt1b6nyB06YyP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u1CxV/btq9iBrCY9D/0xHxMIxhLt1b6nyB06YyP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu1CxV%2Fbtq9iBrCY9D%2F0xHxMIxhLt1b6nyB06YyP0%2Fimg.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;339&quot; data-filename=&quot;Untitled 31.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;218&quot; data-filename=&quot;Untitled 32.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC2Wrh/btq9jGGbr2Z/TBRWElxUkrTndPItUC3gl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC2Wrh/btq9jGGbr2Z/TBRWElxUkrTndPItUC3gl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC2Wrh/btq9jGGbr2Z/TBRWElxUkrTndPItUC3gl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC2Wrh%2Fbtq9jGGbr2Z%2FTBRWElxUkrTndPItUC3gl1%2Fimg.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;218&quot; data-filename=&quot;Untitled 32.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;466&quot; data-filename=&quot;Untitled 33.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmNAmZ/btq9kQaIbFD/qUEmhdRAsNlA7hUAh42jk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmNAmZ/btq9kQaIbFD/qUEmhdRAsNlA7hUAh42jk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmNAmZ/btq9kQaIbFD/qUEmhdRAsNlA7hUAh42jk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmNAmZ%2Fbtq9kQaIbFD%2FqUEmhdRAsNlA7hUAh42jk0%2Fimg.png&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;466&quot; data-filename=&quot;Untitled 33.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auto Scaling Group의 CPU 사용률이 5% 이하면 경보가 발생하도록 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자고 일어났다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 아무것도 하지 않기 때문에 CPU는 5% 이하가 유지되고 있었으며, 경보가 발생한 상태이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;99&quot; data-filename=&quot;Untitled 34.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceP5qQ/btq9pDOYzfD/a77JZmv5pBW8hSe2Q7fAk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceP5qQ/btq9pDOYzfD/a77JZmv5pBW8hSe2Q7fAk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceP5qQ/btq9pDOYzfD/a77JZmv5pBW8hSe2Q7fAk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceP5qQ%2Fbtq9pDOYzfD%2Fa77JZmv5pBW8hSe2Q7fAk0%2Fimg.png&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;99&quot; data-filename=&quot;Untitled 34.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;504&quot; data-filename=&quot;Untitled 35.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctLO23/btq9gXIE1pL/TMZyyCHbT9IjEGTK04VY90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctLO23/btq9gXIE1pL/TMZyyCHbT9IjEGTK04VY90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctLO23/btq9gXIE1pL/TMZyyCHbT9IjEGTK04VY90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctLO23%2Fbtq9gXIE1pL%2FTMZyyCHbT9IjEGTK04VY90%2Fimg.png&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;504&quot; data-filename=&quot;Untitled 35.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 경보를 이용하여 경보가 발생하면 인스턴스가 1개 Scale-In 되도록 단계 조정정책을 추가하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;416&quot; data-filename=&quot;Untitled 36.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp64yb/btq9pDamiEu/4REx57Wx9fvgZ656mHxqIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp64yb/btq9pDamiEu/4REx57Wx9fvgZ656mHxqIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp64yb/btq9pDamiEu/4REx57Wx9fvgZ656mHxqIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp64yb%2Fbtq9pDamiEu%2F4REx57Wx9fvgZ656mHxqIk%2Fimg.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;416&quot; data-filename=&quot;Untitled 36.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;136&quot; data-filename=&quot;Untitled 37.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cao5CW/btq9ml87qlI/cQNNarBHJGk7PVJtodcC61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cao5CW/btq9ml87qlI/cQNNarBHJGk7PVJtodcC61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cao5CW/btq9ml87qlI/cQNNarBHJGk7PVJtodcC61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcao5CW%2Fbtq9ml87qlI%2FcQNNarBHJGk7PVJtodcC61%2Fimg.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;136&quot; data-filename=&quot;Untitled 37.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;97&quot; data-filename=&quot;Untitled 38.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biP2aQ/btq9mluwnbU/HA04pq1balH1fn5Q2PVr3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biP2aQ/btq9mluwnbU/HA04pq1balH1fn5Q2PVr3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biP2aQ/btq9mluwnbU/HA04pq1balH1fn5Q2PVr3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiP2aQ%2Fbtq9mluwnbU%2FHA04pq1balH1fn5Q2PVr3K%2Fimg.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;97&quot; data-filename=&quot;Untitled 38.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Scale-out 도중 ELB 언바인딩 또한 진행된다는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;116&quot; data-filename=&quot;Untitled 39.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Aacd2/btq9gzgUrsQ/UqPz542wqzjUv204rsltD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Aacd2/btq9gzgUrsQ/UqPz542wqzjUv204rsltD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Aacd2/btq9gzgUrsQ/UqPz542wqzjUv204rsltD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAacd2%2Fbtq9gzgUrsQ%2FUqPz542wqzjUv204rsltD0%2Fimg.png&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;116&quot; data-filename=&quot;Untitled 39.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;116&quot; data-filename=&quot;Untitled 40.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BGN7Q/btq9hzAJnag/Z6bQDbelPK8SJrXDAeTGhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BGN7Q/btq9hzAJnag/Z6bQDbelPK8SJrXDAeTGhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BGN7Q/btq9hzAJnag/Z6bQDbelPK8SJrXDAeTGhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBGN7Q%2Fbtq9hzAJnag%2FZ6bQDbelPK8SJrXDAeTGhk%2Fimg.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;116&quot; data-filename=&quot;Untitled 40.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;또한 예약 작업을 생성하여 원하는 시간대에 Auto Scaling Group 사이즈를 유연하게 조정할 수 있으며, 인스턴스 개수 또한 조정 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;717&quot; data-filename=&quot;Untitled 41.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tVQ4H/btq9gXBQFtT/seKTEnThZ8uysYp2XyGxvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tVQ4H/btq9gXBQFtT/seKTEnThZ8uysYp2XyGxvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tVQ4H/btq9gXBQFtT/seKTEnThZ8uysYp2XyGxvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtVQ4H%2Fbtq9gXBQFtT%2FseKTEnThZ8uysYp2XyGxvK%2Fimg.png&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;717&quot; data-filename=&quot;Untitled 41.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;9. cpu 부하 테스트 (Scale-out)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scale-out 테스트를 위해 CloudWatch에서 CPU 사용률이 60% 이상이 되면 발생하는 경보를 추가하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;467&quot; data-filename=&quot;Untitled 42.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UmOeH/btq9hy9G2Tg/UftKLW3bMJOuo3DUQ2pkz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UmOeH/btq9hy9G2Tg/UftKLW3bMJOuo3DUQ2pkz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UmOeH/btq9hy9G2Tg/UftKLW3bMJOuo3DUQ2pkz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUmOeH%2Fbtq9hy9G2Tg%2FUftKLW3bMJOuo3DUQ2pkz0%2Fimg.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;467&quot; data-filename=&quot;Untitled 42.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;155&quot; data-filename=&quot;Untitled 43.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kxBiI/btq9nCbJhd4/Jm7Nja6BlFJ4Zf1ewa73Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kxBiI/btq9nCbJhd4/Jm7Nja6BlFJ4Zf1ewa73Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kxBiI/btq9nCbJhd4/Jm7Nja6BlFJ4Zf1ewa73Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkxBiI%2Fbtq9nCbJhd4%2FJm7Nja6BlFJ4Zf1ewa73Dk%2Fimg.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;155&quot; data-filename=&quot;Untitled 43.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;CPU 사용률이 60%~80%이면 1개의 인스턴스를 증가시키고, 80% 이상이면 2개의 인스턴스를 증가시킨다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;801&quot; data-filename=&quot;Untitled 44.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGMTrH/btq9pCWO37V/AGxfe4WkeY3ZOrVdHHaXt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGMTrH/btq9pCWO37V/AGxfe4WkeY3ZOrVdHHaXt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGMTrH/btq9pCWO37V/AGxfe4WkeY3ZOrVdHHaXt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGMTrH%2Fbtq9pCWO37V%2FAGxfe4WkeY3ZOrVdHHaXt0%2Fimg.png&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;801&quot; data-filename=&quot;Untitled 44.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;현재 Auto Scaling Group에는 아래와 같이 2개의 조정정책이 설정되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;568&quot; data-filename=&quot;Untitled 45.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XTzBq/btq9lUcQpSe/j0WRfsJTZus9kOOnud2Am0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XTzBq/btq9lUcQpSe/j0WRfsJTZus9kOOnud2Am0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XTzBq/btq9lUcQpSe/j0WRfsJTZus9kOOnud2Am0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXTzBq%2Fbtq9lUcQpSe%2Fj0WRfsJTZus9kOOnud2Am0%2Fimg.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;568&quot; data-filename=&quot;Untitled 45.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;부하 발생을 위해 Auto Scaling Group 내의 인스턴스에서 stress를 주었다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo amazon-linux-extras install epel

$ sudo yum -y install epel-release
$ sudo yum -y install stress

$ stress -c 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;top&lt;/code&gt; 명령어로 확인해보면 CPU 사용률이 100%까지 올라간 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;419&quot; data-filename=&quot;Untitled 46.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTq7S6/btq9mneOxHs/TkB2Dtpuy9dQN0Y8faoVO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTq7S6/btq9mneOxHs/TkB2Dtpuy9dQN0Y8faoVO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTq7S6/btq9mneOxHs/TkB2Dtpuy9dQN0Y8faoVO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTq7S6%2Fbtq9mneOxHs%2FTkB2Dtpuy9dQN0Y8faoVO0%2Fimg.png&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;419&quot; data-filename=&quot;Untitled 46.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CloudWatch에서 5% 이하 경보는 해제되고 60% 이상 경보가 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;153&quot; data-filename=&quot;Untitled 48.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cz01l/btq9nCpfx2l/8ck7nXPGgTMALCuc4Kxhfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cz01l/btq9nCpfx2l/8ck7nXPGgTMALCuc4Kxhfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cz01l/btq9nCpfx2l/8ck7nXPGgTMALCuc4Kxhfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCz01l%2Fbtq9nCpfx2l%2F8ck7nXPGgTMALCuc4Kxhfk%2Fimg.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;153&quot; data-filename=&quot;Untitled 48.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;80%를 넘어 100% 사용률을 나타내고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;504&quot; data-filename=&quot;Untitled 49.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doE7ZL/btq9kQ2QjaN/Ihmu8tkZnnaqzptn4wBaKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doE7ZL/btq9kQ2QjaN/Ihmu8tkZnnaqzptn4wBaKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doE7ZL/btq9kQ2QjaN/Ihmu8tkZnnaqzptn4wBaKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoE7ZL%2Fbtq9kQ2QjaN%2FIhmu8tkZnnaqzptn4wBaKk%2Fimg.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;504&quot; data-filename=&quot;Untitled 49.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;cpu 사용률이 80% 이상이면 인스턴스 2개가 증가하도록 조정정책을 설정해 놓았기 때문에, 2개의 인스턴스가 Scale out 되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1549&quot; data-origin-height=&quot;176&quot; data-filename=&quot;Untitled 50.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bod6dO/btq9hz8yYwU/P2IPvoD0ReOmWBtJHR7OTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bod6dO/btq9hz8yYwU/P2IPvoD0ReOmWBtJHR7OTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bod6dO/btq9hz8yYwU/P2IPvoD0ReOmWBtJHR7OTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbod6dO%2Fbtq9hz8yYwU%2FP2IPvoD0ReOmWBtJHR7OTk%2Fimg.png&quot; data-origin-width=&quot;1549&quot; data-origin-height=&quot;176&quot; data-filename=&quot;Untitled 50.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;185&quot; data-filename=&quot;Untitled 51.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzqsm1/btq9kPW9gCn/EHP3WnRU7Tb7dSBBGWUGSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzqsm1/btq9kPW9gCn/EHP3WnRU7Tb7dSBBGWUGSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzqsm1/btq9kPW9gCn/EHP3WnRU7Tb7dSBBGWUGSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbzqsm1%2Fbtq9kPW9gCn%2FEHP3WnRU7Tb7dSBBGWUGSK%2Fimg.png&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;185&quot; data-filename=&quot;Untitled 51.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;지정해놓은 300초(5분)가 지나자 아직 경보가 해제되지 않았기 때문에 인스턴스가 1대 더 Scale out 되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Auto Scaling Group 내에서 AZ 또한 유연하게 분산배치된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;217&quot; data-filename=&quot;Untitled 52.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N1gDN/btq9iAzo7i1/uYN5l1QwyJ3gyFPJpDFz61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N1gDN/btq9iAzo7i1/uYN5l1QwyJ3gyFPJpDFz61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N1gDN/btq9iAzo7i1/uYN5l1QwyJ3gyFPJpDFz61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN1gDN%2Fbtq9iAzo7i1%2FuYN5l1QwyJ3gyFPJpDFz61%2Fimg.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;217&quot; data-filename=&quot;Untitled 52.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;95&quot; data-filename=&quot;Untitled 53.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba32hG/btq9nCvZYFy/TWrNh51eyVSAZ34H3JwLhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba32hG/btq9nCvZYFy/TWrNh51eyVSAZ34H3JwLhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba32hG/btq9nCvZYFy/TWrNh51eyVSAZ34H3JwLhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba32hG%2Fbtq9nCvZYFy%2FTWrNh51eyVSAZ34H3JwLhk%2Fimg.png&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;95&quot; data-filename=&quot;Untitled 53.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Auto Scaling Group에 총 4대의 인스턴스가 올라가 Cpu 평균 사용률이 감소하고 경보가 해제되는 과정 또한 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;503&quot; data-filename=&quot;Untitled 54.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQ76o/btq9iAlTsf4/Cs9WhZFkIS8gmckFi7XNs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQ76o/btq9iAlTsf4/Cs9WhZFkIS8gmckFi7XNs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQ76o/btq9iAlTsf4/Cs9WhZFkIS8gmckFi7XNs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQ76o%2Fbtq9iAlTsf4%2FCs9WhZFkIS8gmckFi7XNs1%2Fimg.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;503&quot; data-filename=&quot;Untitled 54.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;이것이 Auto Scaling을 사용하는 이유이다.&lt;/p&gt;
&lt;p data-ke-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>IT/Cloud</category>
      <category>autoscaling</category>
      <category>AWS</category>
      <category>CloudWatch</category>
      <category>EC2</category>
      <category>S3</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/46</guid>
      <comments>https://suyeon96.tistory.com/46#entry46comment</comments>
      <pubDate>Mon, 12 Jul 2021 01:35:31 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] VPC 환경 구성 (Public, Private Subnet)</title>
      <link>https://suyeon96.tistory.com/45</link>
      <description>&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;AWS를 사용한다면 Amazon VPC를 활용하여 복잡한 요소를 거치지 않고 Private network를 간단하게 구성하고 보안을 챙길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 자체 VPC를 구성하고, Public subnet과 Private subnet을 생성하는 아주 심플한 시나리오를 진행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;302&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co75yL/btq9gRtpOuX/KREu7beuWOgbjBauHBWuZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co75yL/btq9gRtpOuX/KREu7beuWOgbjBauHBWuZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co75yL/btq9gRtpOuX/KREu7beuWOgbjBauHBWuZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco75yL%2Fbtq9gRtpOuX%2FKREu7beuWOgbjBauHBWuZK%2Fimg.png&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;302&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;1. VPC 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 VPC를 하나 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;499&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zr8Kq/btq9gYsIJHw/KDP6xxlijeydjGF0s1Ask1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zr8Kq/btq9gYsIJHw/KDP6xxlijeydjGF0s1Ask1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zr8Kq/btq9gYsIJHw/KDP6xxlijeydjGF0s1Ask1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZr8Kq%2Fbtq9gYsIJHw%2FKDP6xxlijeydjGF0s1Ask1%2Fimg.png&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;499&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;인스턴스의 public 또는 private IPv4 주소에 해당하는 DNS hostname을 제공하도록 설정할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;427&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lx61b/btq9dFBt3Bx/qbs8a3YDeeFKg6nAYOxKqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lx61b/btq9dFBt3Bx/qbs8a3YDeeFKg6nAYOxKqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lx61b/btq9dFBt3Bx/qbs8a3YDeeFKg6nAYOxKqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLx61b%2Fbtq9dFBt3Bx%2Fqbs8a3YDeeFKg6nAYOxKqk%2Fimg.png&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;427&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;280&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x1t3i/btq9hPISiH8/NFdWVFpdsvIVPRg0dhWJA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x1t3i/btq9hPISiH8/NFdWVFpdsvIVPRg0dhWJA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x1t3i/btq9hPISiH8/NFdWVFpdsvIVPRg0dhWJA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx1t3i%2Fbtq9hPISiH8%2FNFdWVFpdsvIVPRg0dhWJA1%2Fimg.png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;280&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림처럼 &lt;code&gt;Enable&lt;/code&gt;에 체크하면 앞으로 VPC에서 실행된 Amazon EC2 인스턴스에서 DNS hostname을 자동으로 수신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Public DNS hostname은 &lt;code&gt;ec2-public-ipv4-address.region.compute.amazonaws.com&lt;/code&gt; 형식으로 지정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazone Route53을 사용하여 나중에 더 유의미한 DNS hostname을 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Amazon DNS 서버는 Public DNS hostname을 인스턴스 네트워크 외부에서는 인스턴스의 Public IPv4 주소로 변환하고, 인스턴스 네트워크 내부에서는 인스턴스의 Private IPv4 주소로 변환한다. (&lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-instance-addressing.html#concepts-public-addresses&quot;&gt;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-instance-addressing.html#concepts-public-addresses&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Public Subnet 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Subnet 탭으로 가서 Public 용도의 Subnet을 생성하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;626&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tPJQr/btq9jF0h2ZV/tNLvq4bPa6kWYHkvrXwLN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tPJQr/btq9jF0h2ZV/tNLvq4bPa6kWYHkvrXwLN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tPJQr/btq9jF0h2ZV/tNLvq4bPa6kWYHkvrXwLN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtPJQr%2Fbtq9jF0h2ZV%2FtNLvq4bPa6kWYHkvrXwLN1%2Fimg.png&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;626&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(10.0.0.0/24 &amp;rarr; 10.0.0.x 대역의 모든 IP 주소를 포함한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Public Subnet 안에서 시작되는 모든 인스턴스에 Public IP주소가 자동으로 할당되도록 설정해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;573&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c69dyl/btq9fxbm8I1/VHQbdlMvb94ZTDMHOOeB60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c69dyl/btq9fxbm8I1/VHQbdlMvb94ZTDMHOOeB60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c69dyl/btq9fxbm8I1/VHQbdlMvb94ZTDMHOOeB60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc69dyl%2Fbtq9fxbm8I1%2FVHQbdlMvb94ZTDMHOOeB60%2Fimg.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;573&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;454&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nN584/btq9fW9OPnU/67hT7TkckOFYnCYKBmRIZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nN584/btq9fW9OPnU/67hT7TkckOFYnCYKBmRIZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nN584/btq9fW9OPnU/67hT7TkckOFYnCYKBmRIZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnN584%2Fbtq9fW9OPnU%2F67hT7TkckOFYnCYKBmRIZK%2Fimg.png&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;454&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;Public 용으로 생성한 Subnet이지만 아직 Public상태는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼블릭 서브넷에는 인터넷 게이트웨이가 있어야 하며, 이후에 IGW를 생성하고 연결할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Private Subnet 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Public Subnet을 완성하기 전에 Private 용도의 Subnet도 하나 만들어주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;627&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDcODk/btq9eE96LtT/m2ddkBrxiXSqQGSS8qI0S1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDcODk/btq9eE96LtT/m2ddkBrxiXSqQGSS8qI0S1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDcODk/btq9eE96LtT/m2ddkBrxiXSqQGSS8qI0S1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDcODk%2Fbtq9eE96LtT%2Fm2ddkBrxiXSqQGSS8qI0S1%2Fimg.png&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;627&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 똑같지만 Subnetting을 위한 CIDR notation을 &lt;code&gt;/23&lt;/code&gt;으로 지정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(10.0.2.0/23 &amp;rarr; 10.0.2.x 및 10.0.3.x 대역의 모든 IP 주소 포함한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 대부분의 리소스는 Private 환경에 구성해야 하므로, Public Subnet의 2배 크기를 가지도록 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Internet Gateway 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Internet Gateway는 수평적 확장을 통해 이중화를 지원하고 가용성이 높은 VPC 구성 요소로서, VPC와 인터넷 간 통신할 수 있도록 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷 게이트웨이를 사용하는 목적은 2가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터넷 라우팅 가능 트래픽에 대하여 VPC 라우팅 테이블에 연결 대상 제공&lt;/li&gt;
&lt;li&gt;Public IPv4 주소가 할당된 인스턴스에 대해 NAT(Network Address Translation) 수행&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;아까 Step2에서 생성한 Public Subnet에 인터넷 트래픽이 액세스 할 수 있도록 Internet Gateway를 생성하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;278&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nlspR/btq9fd5gZET/q46KaJKPKASBjSknhJH7sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nlspR/btq9fd5gZET/q46KaJKPKASBjSknhJH7sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nlspR/btq9fd5gZET/q46KaJKPKASBjSknhJH7sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnlspR%2Fbtq9fd5gZET%2Fq46KaJKPKASBjSknhJH7sK%2Fimg.png&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;278&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 후 VPC에 attach 해줘야한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;348&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nqOHH/btq9f6x1Pbk/fNx0mHsHwGm0LkWUNNdcuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nqOHH/btq9f6x1Pbk/fNx0mHsHwGm0LkWUNNdcuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nqOHH/btq9f6x1Pbk/fNx0mHsHwGm0LkWUNNdcuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnqOHH%2Fbtq9f6x1Pbk%2FfNx0mHsHwGm0LkWUNNdcuk%2Fimg.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;348&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;5. Route table 구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route table은 Subnet 또는 Gateway의 네트워크 트래픽이 전송되는 위치를 결정하는 데 사용되는 &lt;code&gt;route&lt;/code&gt; 규칙의 집합으로 이루어져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPC에 있는 각 Subnet은 Route table에 연결되어 있어야 하며, 라우팅 테이블이 Subnet에 대한 라우팅을 제어한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route table에 인터넷에 바인딩된 트래픽을 Internet Gateway로 향하도록 지시하는 Route 경로를 포함하고 이를 Subnet에 연결하면 Public Subnet이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기본적으로 위에서 만든 VPC와 연결된 라우팅 테이블이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 라우팅 테이블은 트래픽을 로컬로 라우팅 하기 때문에 Private Routing 테이블이라고 이름을 변경해 주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1657&quot; data-origin-height=&quot;212&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL0cM4/btq9gY7nmgT/onPzd9k07GVLNAdsZYWKqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL0cM4/btq9gY7nmgT/onPzd9k07GVLNAdsZYWKqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL0cM4/btq9gY7nmgT/onPzd9k07GVLNAdsZYWKqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL0cM4%2Fbtq9gY7nmgT%2FonPzd9k07GVLNAdsZYWKqk%2Fimg.png&quot; data-origin-width=&quot;1657&quot; data-origin-height=&quot;212&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;279&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk9y4G/btq9fEV5W74/2prDHgtajZ7sUW6BoJ41kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk9y4G/btq9fEV5W74/2prDHgtajZ7sUW6BoJ41kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk9y4G/btq9fEV5W74/2prDHgtajZ7sUW6BoJ41kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk9y4G%2Fbtq9fEV5W74%2F2prDHgtajZ7sUW6BoJ41kk%2Fimg.png&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;279&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 테이블에서의 Route 경로는 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPC 범위의 IP로 향하는 모든 트래픽이 local로 라우팅 된다는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 VPC 내 모든 서브넷이 서로 통신할 수 있다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Public 트래픽을 Internet Gateway로 전송할 Public Route table 생성하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;365&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqaI9E/btq9eDDqomU/9QmteqVUy6WlynZxPciGr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqaI9E/btq9eDDqomU/9QmteqVUy6WlynZxPciGr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqaI9E/btq9eDDqomU/9QmteqVUy6WlynZxPciGr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqaI9E%2Fbtq9eDDqomU%2F9QmteqVUy6WlynZxPciGr1%2Fimg.png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;365&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;357&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhfqTU/btq9gAMqiRS/iOTvt0XpyZZGRuEzHSWZNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhfqTU/btq9gAMqiRS/iOTvt0XpyZZGRuEzHSWZNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhfqTU/btq9gAMqiRS/iOTvt0XpyZZGRuEzHSWZNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhfqTU%2Fbtq9gAMqiRS%2FiOTvt0XpyZZGRuEzHSWZNK%2Fimg.png&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;357&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷 바운드 트래픽(0.0.0.0/0)을 Internet Gateway로 보내는 Route 경로를 추가하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Route table을 Public 서브넷에 연결하면, 이제서야 진정한 Public Subnet이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;298&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vLN2Q/btq9gRtp0Zq/P3FHzCU9Qt0eGyApInCHC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vLN2Q/btq9gRtp0Zq/P3FHzCU9Qt0eGyApInCHC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vLN2Q/btq9gRtp0Zq/P3FHzCU9Qt0eGyApInCHC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvLN2Q%2Fbtq9gRtp0Zq%2FP3FHzCU9Qt0eGyApInCHC1%2Fimg.png&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;298&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;502&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/esU5xr/btq9dEo0K4u/Y14bqeEml6KgHHSpppk0YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/esU5xr/btq9dEo0K4u/Y14bqeEml6KgHHSpppk0YK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/esU5xr/btq9dEo0K4u/Y14bqeEml6KgHHSpppk0YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FesU5xr%2Fbtq9dEo0K4u%2FY14bqeEml6KgHHSpppk0YK%2Fimg.png&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;502&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;6. Public Subnet에서 EC2 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 생성 시 위에서 만든 VPC와 Public Subnet을 선택할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;125&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKMiQc/btq9gXtOvLY/KC3xWkSKYc6HHyJ2NeYKJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKMiQc/btq9gXtOvLY/KC3xWkSKYc6HHyJ2NeYKJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKMiQc/btq9gXtOvLY/KC3xWkSKYc6HHyJ2NeYKJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKMiQc%2Fbtq9gXtOvLY%2FKC3xWkSKYc6HHyJ2NeYKJ0%2Fimg.png&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;125&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;아까 Public Subnet에서 인스턴스에 자동으로 Public IP가 assign 되도록 설정해주었기 때문에, default setting이 활성화되어있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;464&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CPax9/btq9hN5nCYX/klt3ejV4nDIrFrKGlz1TK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CPax9/btq9hN5nCYX/klt3ejV4nDIrFrKGlz1TK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CPax9/btq9hN5nCYX/klt3ejV4nDIrFrKGlz1TK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCPax9%2Fbtq9hN5nCYX%2Fklt3ejV4nDIrFrKGlz1TK1%2Fimg.png&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;464&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Cloud</category>
      <category>AWS</category>
      <category>Internet Gateway</category>
      <category>network</category>
      <category>Routing</category>
      <category>subnet</category>
      <category>VPC</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/45</guid>
      <comments>https://suyeon96.tistory.com/45#entry45comment</comments>
      <pubDate>Sat, 10 Jul 2021 14:07:41 +0900</pubDate>
    </item>
    <item>
      <title>[Git] CentOS7 GitLab RPM 설치 및 Migration (GitBlit &amp;rarr; GitLab)</title>
      <link>https://suyeon96.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;팀에서 기존에 사용 중이던 Git 서버를 반납해야 했기에, 새로운 Git 서버를 구축해야 하는 미션이 떨어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기존 GitBlit을 버리고 GitLab으로 갈아타기로 마음먹었으며, 이번 기회에 팀 내의 Git Branch 전략을 새롭게 정립하고 CI/CD 또한 개선하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 Internet이 안되는 Intranet 환경에서 GitLab을 설치하는 과정과, Git Repository를 이전하는 방법에 대해 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Install and configure the necessary dependencies&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.1 gitlab 설치에 필요한 패키지 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ sudo yum install -y curl policycoreutils-python openssh-server perl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.2 sshd 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ sudo systemctl enable sshd
$ sudo systemctl start sshd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.3 http, https 방화벽 open&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ sudo firewall-cmd --permanent --add-service=http
$ sudo firewall-cmd --permanent --add-service=https
$ sudo systemctl reload firewalld

# open 확인
$ sudo firewall-cmd --list-all

# 방화벽 재시작
$ sudo systemctl restart firewalld&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;code&gt;iptables -L -vn&lt;/code&gt; 으로 firewall list 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;92&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v9UAX/btq9evXWMpM/siFcY7dfMkzgSdTkA5yob1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v9UAX/btq9evXWMpM/siFcY7dfMkzgSdTkA5yob1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v9UAX/btq9evXWMpM/siFcY7dfMkzgSdTkA5yob1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv9UAX%2Fbtq9evXWMpM%2FsiFcY7dfMkzgSdTkA5yob1%2Fimg.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;92&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.4 메일링을 위한 postfix 메일서버 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ sudo yum install postfix

$ sudo systemctl enable postfix
$ sudo systemctl start postfix&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Add the GitLab package repository and install the package&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷이 된다면 아래 명령어를 이용하여 GitLab package repository를 등록하고 설치하면 된다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ curl -s &amp;lt;https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh&amp;gt; | sudo bash

$ sudo yum install gitlab-ce-13.11.3-ce.0.el7.x86_64&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;우리는 offline 환경이기 때문에 GitLab에서 제공하는 RPM 패키지를 다운받아서 설치하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://packages.gitlab.com/gitlab/gitlab-ce&quot;&gt;https://packages.gitlab.com/gitlab/gitlab-ce&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ rpm -Uvh gitlab-ce-13.11.3-ce.0.el7.x86_64.rpm&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;659&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xmGV9/btq85VLeeKP/TWbVpIZVWlHwBAYHZkJv50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xmGV9/btq85VLeeKP/TWbVpIZVWlHwBAYHZkJv50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xmGV9/btq85VLeeKP/TWbVpIZVWlHwBAYHZkJv50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxmGV9%2Fbtq85VLeeKP%2FTWbVpIZVWlHwBAYHZkJv50%2Fimg.png&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;659&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Configure GitLab&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.1&lt;/b&gt; &lt;code&gt;/etc/gitlab/gitlab.rb&lt;/code&gt; &lt;b&gt;경로의 파일에서 gitlab 설정 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ vi /etc/gitlab/gitlab.rb&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.2 external_url 변경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 이름을 설정하려면 &lt;code&gt;external_url&lt;/code&gt; 변수를 수정하면 된다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;## GitLab URL
##! URL on which GitLab will be reachable.
##! For more details on configuring external_url see:
##! &amp;lt;https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab&amp;gt;
##!
##! Note: During installation/upgrades, the value of the environment variable
##! EXTERNAL_URL will be used to populate/replace this value.
##! On AWS EC2 instances, we also attempt to fetch the public hostname/IP
##! address from AWS. For more details, see:
##! &amp;lt;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html&amp;gt;
external_url &quot;&amp;lt;http://gitlab.example.com&amp;gt;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.3 IP, Port 변경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 IP와 Port를 변경하고 싶은 경우 &lt;code&gt;unicorn['listen']&lt;/code&gt;과 &lt;code&gt;unicorn['port']&lt;/code&gt; 변수를 수정하면 된다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;################################################################################
## GitLab Unicorn
##! Tweak unicorn settings.
##! Docs: &amp;lt;https://docs.gitlab.com/omnibus/settings/unicorn.html&amp;gt;
################################################################################

### Advanced settings
# unicorn['listen'] = 'localhost'
# unicorn['port'] = 8080
# unicorn['socket'] = '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket'
# unicorn['pidfile'] = '/opt/gitlab/var/unicorn/unicorn.pid'
# unicorn['tcp_nopush'] = true
# unicorn['backlog_socket'] = 1024

###! **Make sure somaxconn is equal or higher then backlog_socket**
# unicorn['somaxconn'] = 1024&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.4 data repo 변경&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;git_data_dirs({
    &quot;default&quot; =&amp;gt; {
        &quot;path&quot; =&amp;gt; &quot;/mnt/nfs-01/git-data&quot;
  }
)}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이 부분을 설정하지 않고 &lt;code&gt;#&lt;/code&gt; 주석 처리가 되어 있다면, default 저장소 path는 &lt;code&gt;/var/opt/gitlab/git-data&lt;/code&gt;로 설정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이 부분을 새로 마운트한 /data volume으로 변경해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 directory를 새롭게 추가하였다면, gitlab이 접근할 수 있도록 권한을 확인해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한이 없다면 아래 명령어로 권한을 부여하자&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ chown -R git:git /data/git-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;&lt;b&gt;3.5 SMTP setting&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = &quot;smtp.server&quot;
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = &quot;smtp user&quot;
gitlab_rails['smtp_password'] = &quot;smtp password&quot;
gitlab_rails['smtp_domain'] = &quot;example.com&quot;
gitlab_rails['smtp_authentication'] = &quot;login&quot;
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_openssl_verify_mode'] = 'peer'

# If your SMTP server does not like the default 'From: gitlab@localhost' you
# can change the 'From' with this setting.
gitlab_rails['gitlab_email_from'] = 'gitlab@example.com'
gitlab_rails['gitlab_email_reply_to'] = 'noreply@example.com'

# If your SMTP server is using self signed certificates you can specify a custom ca file
gitlab_rails['smtp_ca_file'] = '/path/to/your/cacert.pem'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 :&amp;nbsp;&lt;a href=&quot;https://docs.gitlab.com/omnibus/settings/smtp.html&quot;&gt;https://docs.gitlab.com/omnibus/settings/smtp.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. GitLab 초기화 및 설치 진행&lt;/h3&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ sudo gitlab-ctl reconfigure&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;463&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QUXVc/btq88LOM8OK/rBMKUauptTD8KI3aK4drN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QUXVc/btq88LOM8OK/rBMKUauptTD8KI3aK4drN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QUXVc/btq88LOM8OK/rBMKUauptTD8KI3aK4drN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQUXVc%2Fbtq88LOM8OK%2FrBMKUauptTD8KI3aK4drN0%2Fimg.png&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;463&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;5. Web 접속 및 로그인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 접속 시 password reset 화면으로 redirect 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;701&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2SzKe/btq89DvZXgh/dunbYy4DmPJhN6m2aDYs51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2SzKe/btq89DvZXgh/dunbYy4DmPJhN6m2aDYs51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2SzKe/btq89DvZXgh/dunbYy4DmPJhN6m2aDYs51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2SzKe%2Fbtq89DvZXgh%2FdunbYy4DmPJhN6m2aDYs51%2Fimg.png&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;701&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;default 계정의 username은 &lt;code&gt;root&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;password 설정 후 root로 로그인하여 사용할 유저를 등록하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Git Repository Migration (GitBlit &amp;rarr; GitLab)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 준비는 끝났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 사용하던 GitBlit의 Repository를 새로 구축한 GitLab으로 이동해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;--mirror&lt;/code&gt; 옵션을 사용하면 원격 저장소(Remote Repository)의 복사본을 만들어 간단하게 Repository를 이전할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 &lt;code&gt;--mirror&lt;/code&gt;는 &lt;code&gt;--bare&lt;/code&gt; 옵션을 포함한다.&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;git clone --mirror {기존 리파지토리 주소}
cd {기존 리파지토리 명}.git
git remote set-url --push origin {신규 리파지토리 주소}
git push --mirror&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;Reference&lt;/h3&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://about.gitlab.com/install/#centos-7&quot;&gt;https://about.gitlab.com/install/#centos-7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.gitlab.com/omnibus/manual_install.html&quot;&gt;https://docs.gitlab.com/omnibus/manual_install.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md&quot;&gt;https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md&lt;/a&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;</description>
      <category>IT/Etc</category>
      <category>Git</category>
      <category>Git Migration</category>
      <category>GitBlit</category>
      <category>GitLab</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/44</guid>
      <comments>https://suyeon96.tistory.com/44#entry44comment</comments>
      <pubDate>Fri, 9 Jul 2021 13:22:13 +0900</pubDate>
    </item>
    <item>
      <title>부동산 투자에서의 레버리지 효과와 위험성</title>
      <link>https://suyeon96.tistory.com/43</link>
      <description>&lt;h2&gt;레버리지 효과란?&lt;/h2&gt;
&lt;p&gt;레버리지 효과(leverage effect)란 타인이나 금융기관으로부터 차입한 자본을 이용하여 자기자본이익률을 높이는 것을 말한다. 빌린 돈을 지렛대(lever) 삼아 이익을 창출한다는 의미에서 지렛대 효과라고도 한다. 지렛대를 사용하면 작은 힘으로 무거운 무게를 들 수 있는데, 차입한 자본이 지렛대가 되고 그 힘이 나의 자본과 합쳐져서 높은 수익을 낼 수 있다는 의미이다. 즉, 자신의 자본을 최소화하고 타인의 자본을 빌려 수익률을 극대화하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들어 A와 B가 동일하게 1년동안 10억원을 투자하여 2억원의 순이익을 얻었다. A는 대출 없이 자신의 돈 10억원을 투자하였으며, B는 은행에서 대출받은 5억원과 자신의 돈 5억원을 합쳐 투자하였다. A와 B 모두 총자본이익률은 20%로 동일하다. 하지만 자기자본이익률을 비교했을 때 A는 20%(2/10)이지만, B는 40%(2/5)이다. 이자율 5%를 계산하더라도 35%(2-(5*0.05)/5)로 자기자본이익률이 A보다 높은 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이처럼 적은 자본으로 많은 수익을 낼 수 있는 것이 투자에 있어서 레버리지의 매력이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;레버리지 위험성&lt;/h2&gt;
&lt;p&gt;레버리지 효과의 전제조건은 투자로 인해 발생하는 수익률이 차입금의 이자율보다 높아야 한다. 이를 정(正)의 지렛대 효과라고 한다. 하지만 수익률이 차입금의 이자율보다 낮은 경우 금리 부담이 커지게 된다. 수익률이 마이너스가 되는 경우 즉 손실이 났을 때는 레버리지가 오히려 더 큰 손해를 야기하게 된다. 이를 부(負)의 지렛대 효과라고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;레버리지 비율이 높을수록 높은 이익을 얻을 수도 있지만, 반대로 위험도 그만큼 증가한다. 여기서 레버리지 비율은 부채 대 자기자본 비율을 말한다. 투자에 있어 지나치게 높은 레버리지 비율은 과도한 부채부담을 의미하고, 이는 원리금 상환부담의 증가와 연결된다. 이러한 경우 단순히 나의 돈을 잃고 끝나는 것을 넘어 채무불이행 등의 위험으로 이어질 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;부동산 투자에서의 레버리지 효과&lt;/h2&gt;
&lt;p&gt;우리나라는 주식이나 금융상품에 대한 투자비중보다 부동산 투자비중이 높은 모습을 보인다. 그 이유로는 &amp;lsquo;높은 수익률&amp;rsquo; 또는 &amp;lsquo;안정성&amp;rsquo; 등이 있을 것이다. 하지만 필자는 위에서 언급한 &amp;lsquo;레버리지&amp;rsquo;가 부동산 투자의 최대 장점이라고 생각한다. 부동산 투자에서의 레버리지 효과를 활용하는 대표적인 방법은 은행으로부터 대출을 받아 부동산을 구매하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;돈이 돈을 번다는 이야기를 한번쯤은 들어봤을 것이다. A와 B라는 아파트가 있다. A아파트는 1억원이고 B아파트는 10억원이다. 시세차익으로 두 아파트에서 동일하게 1억원의 수익을 얻기 위해서는 A아파트는 가격 상승률이 100%여야 하지만, B아파트는 가격 상승률이 10%면 가능하다. B아파트를 소유한 사람이 A아파트를 소유한 사람보다 돈을 많이 벌 수 있는 확률이 더 높다. 사실 너무 당연한 이야기이다. 여기서 중요한 것은 우리가 10억원의 자본이 없더라도 레버리지 효과를 이용하여 B아파트에 투자하고, 그만큼의 이익을 얻을 수 있다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;부동산 투자에 빠질 수 없는 단어가 바로 갭투자이다. 갭투자란 간단하게 매매가와 전세가의 차이(gap)를 이용한 투자이다. 위에서 언급한 10억짜리 B아파트의 전세가가 8억이다. 그럼 B아파트를 사는데 내 자본은 2억원만 있으면 된다. B아파트가 나중에 11억이 된다면 이에 따른 자기자본이익률은 무려 50%이다. 이 또한 레버리지 효과라고 할 수 있다. 또한 시세차익 이외에도 임대수익을 얻을 수도 있다. 은행 대출만이 아니라 타인의 자본을 이용하여 내 자본금에 비해 큰 수익을 얻을 수 있는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 역시나 부동산투자에서도 레버리지의 위험성은 존재한다. 단편적으로 집값이 하락하는 경우 개인에게도 비극이지만 부채를 이용하여 부동산을 구매한 사람들은 신용불량자가 될 것이다. 은행 또한 원리금을 상환받지 못해 수익이 악화되고 금융 위기도 피할 수 없게 된다. 정부는 이를 방지하고자 &lt;a href=&quot;https://suyeon96.tistory.com/42&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;지난 글&lt;/a&gt;에서 언급하였던 주택담보인정비율(LTV), 총부채상환비율(DTI), 총부채원리금상환비율(DSR) 규제 등을 이용하여 과도한 레버리지 효과를 제한한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;결론&lt;/h2&gt;
&lt;p&gt;레버리지 효과는 양날의 검을 가지고 있다. 필자는 아직까지는 부동산 투자가 우리나라에서 레버리지 효과를 이용하여 안정성과 고수익 모두 잡을 수 있는 투자종목이라고 생각한다. 물론 부동산 투자에 있어서 재산세, 종합부동산세, 양도소득세 등 각종 세금과 차입금에 대한 이자율, 위험부담 등을 함께 고려해야 한다. 물론 레버리지 효과로 수익을 얻기 위해서는 레버리지의 위험성 또한 반드시 이해해야 한다. 하지만 이러한 모든 위험성을 사전에 숙지하고 투자에 임한다면 레버리지 효과는 자산을 키우는 데 있어서 큰 도움이 될 것이다. 투자에 있어서 부채를 무조건적으로 부정적인 시선으로 바라볼 필요는 없다는 점을 주장하며 글을 마무리한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;p&gt;이권복(2019). 토익 공부보다 돈 공부. 서울: 한스미디어.&lt;/p&gt;
&lt;p&gt;Zvi Bodie, Alex Kane &amp;amp; Alan J. Marcus(2020). Essentials of Investments (11th ed.). (남상구, 최승두 공역). 서울: 지필미디어.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Life/Economy</category>
      <category>Leverage Effect</category>
      <category>부동산학개론</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/43</guid>
      <comments>https://suyeon96.tistory.com/43#entry43comment</comments>
      <pubDate>Mon, 3 May 2021 15:08:09 +0900</pubDate>
    </item>
    <item>
      <title>DSR 규제에 대하여... (+ LTV, DTI)</title>
      <link>https://suyeon96.tistory.com/42</link>
      <description>&lt;h2&gt;DSR이란?&lt;/h2&gt;
&lt;p&gt;DSR은 총부채원리금상환비율(&lt;i&gt;Debt Service Ratio&lt;/i&gt;)의 약자로 총 금융부채 원리금 상환액을 연간 소득으로 나눈 비율을 뜻한다. 즉, DSR 규제란 대출자의 연소득에 대비하여 1년간 원리금으로 내는 돈이 얼마인지를 따져 대출금액을 제한하는 규제이다. 예를 들어 1년에 1억원을 버는 사람이 당해 전체 대출에 대한 원금과 이자로 4000만원을 내고 있다면 DSR은 40%인 것이다. 대출자의 모든 대출 원금 및 이자 상환액이 얼마나 부담이 되는지를 측정하는 지표이며, 수치가 낮을수록 상환 능력이 높다고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;DSR규제 현황 및 4.29 차주단위 규제 발표&lt;/h2&gt;
&lt;p&gt;현재 금융감독원은 시중은행에 은행 별로 DSR을 40%로 유지하도록 권고하고 있다. 은행 전체 고객들의 평균치로 계산하기 때문에 고객에 따라서는 DSR이 40%가 넘는 대출자도 있다. 일부 고소득자에 대해서는 개인별로 DSR을 적용하기도 한다. 하지만 최근 들어 금융위원회는 DSR 규제를 완전히 개인별로 적용하려는 시도를 하고 있다. 금융위는 지난달 29일 34차 비상경제중앙대책본부회의에서 &amp;lsquo;가계부채 관리 선진화 방안&amp;rsquo;에 따라 2023년 7월부터 DSR 규제를 차주 단위로 적용한다고 발표하였다. 앞으로는 개인별로 1인당 DSR 40%를 일괄 적용하여 개인의 대출 한도를 축소시킨다는 것이다. 이는 지난해 8%까지 치솟은 가계대출 증가세를 낮추고, 최근 유행하는 빛내서 투자하는 행위를 어느 정도 차단하기 위한 조치이다. 또한 고액의 신용대출 한도를 줄여 부동산 시장의 수요를 억제하려는 목적도 존재한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 여기에는 단점도 존재한다. 일반적으로 주택담보대출을 받을 때는 대부분 대출한도를 꽉 채워서 받는 경우가 많은데, DSR 규제를 강화하면 추가 대출이 어렵게 된다. 급하게 자금이 필요한 경우 대출을 받지 못하게 되는 것이다. 또한 저소득자의 경우 더욱 대출이 힘들어질 것이다. 부동산 시장과 가계부채를 안정시키기 위한 조치이지만, 코로나19로 인해 경제가 침체되고 있는 상황에서 돈의 흐름이 더욱 약해지고 불경기가 심해지지 부작용이 생기지 않을까 하는 걱정도 든다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;LTV&amp;middot;DTI&lt;/h2&gt;
&lt;p&gt;주택담보대출의 대출금액을 제한하는 규제에는 DSR이외에도 대표적으로 LTV와 DTI가 있다. LTV는 주택담보인정비율(&lt;i&gt;Loan to Value&lt;/i&gt;)의 약자로 주택의 담보가치에 따른 대출금의 비율을 의미한다. 즉, 주택의 시세에 대비하여 대출 가능한 한도 금액을 나타내는 것이다. DTI는 총부채상환비율(&lt;i&gt;Debt To Income&lt;/i&gt;)의 약자로 금융부채 상환능력을 소득으로 따져서 대출한도를 정하는 비율이다. 연간 소득에서 매년 갚아야 하는 원금 및 이자가 차지하는 비율을 뜻한다. DTI와 DSR 연소득 대비 대출 가능한 한도를 계산하는데 쓰이는 지표로서, 주택담보대출 시 차주의 상환능력을 판단하고 규제하는 용도로 사용된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇다면 DTI와 DSR의 차이는 무엇일까? DTI는 해당 대출 건 이외의 대출은 이자 상환액만을 고려하는 반면, DSR은 기존 대출의 이자는 물론 원금까지 포함하여 계산한다. 따라서 DTI보다 DSR가 더 엄격하다고 볼 수 있으며, DSR 규제에서는 금융부채가 더욱 크게 반영되기 때문에 DTI 규제보다 대출 한도가 줄어든다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Life/Economy</category>
      <category>DSR규제</category>
      <category>부동산학개론</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/42</guid>
      <comments>https://suyeon96.tistory.com/42#entry42comment</comments>
      <pubDate>Mon, 3 May 2021 15:00:34 +0900</pubDate>
    </item>
    <item>
      <title>[ELK Stack] 4. Metricbeat로 System Monitoring</title>
      <link>https://suyeon96.tistory.com/41</link>
      <description>&lt;h3&gt;ELK Stack 목차&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 1. ELK Stack 이란? (Beats, Logstash, Elasticsearch, Kibana)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 2. Elasticsearch, Logstash, Kibana 설치 및 구성&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 3. Filebeat와 ELK Stack으로 Apache log 관리&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;[ELK Stack] 4. Metricbeat로 System Monitoring&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. Metricbeat란?&lt;/h2&gt;
&lt;p&gt;&lt;b&gt;Metricbeat&lt;/b&gt;를 이용하면 시스템과 서비스에서 메트릭 정보를 손쉽게 수집할 수 있다.&lt;/p&gt;
&lt;p&gt;Cpu, Memory, File system, Disk IO, Network IO 등과 시스템에서 실행되는 모든 프로세스에 대한 통계를 수집하여 전송한다.&lt;/p&gt;
&lt;p&gt;또한 기본적으로 내장되어 있는 모듈은 Apache, Jolokia, NGINX, MongoDB, MySQL, PostgreSQL, Prometheus 등의 다양한 서비스로부터 메트릭을 수집하며, 원하는 모듈이 없다면 Go Language로 새로운 모듈을 간단하게 생성할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. Metricbeat 설치&lt;/h2&gt;
&lt;p&gt;이전 게시글에서는 yum으로 ELK stack을 설치했지만 이번에는 download 받아서 설치한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-7.10.2-x86_64.rpm
$ sudo rpm -vi metricbeat-7.10.2-x86_64.rpm&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;0.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;229&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X9s4m/btqW1ssva68/1nDRoaF2K2SbgM1INVN5AK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X9s4m/btqW1ssva68/1nDRoaF2K2SbgM1INVN5AK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X9s4m/btqW1ssva68/1nDRoaF2K2SbgM1INVN5AK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX9s4m%2FbtqW1ssva68%2F1nDRoaF2K2SbgM1INVN5AK%2Fimg.png&quot; data-filename=&quot;0.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;229&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. Metricbeat 설정&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/etc/metricbeat/metricbeat.yml&lt;/code&gt; 파일을 아래와 같이 설정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Kibana&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# =================================== Kibana ===================================

# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API.
# This requires a Kibana endpoint configuration.
setup.kibana:

  # Kibana Host
  # Scheme and port can be left out and will be set to the default (http and 5601)
  # In case you specify and additional path, the scheme is required: http://localhost:5601/path
  # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
  host: &quot;3.35.108.182:5601&quot;

  # Kibana Space ID
  # ID of the Kibana Space into which the dashboards should be loaded. By default,
  # the Default Space will be used.
  #space.id:&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;구축된 Kibana dashboard를 사용하기 위해 Kibana endpoint를 설정해주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Elasticsearch Output&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# ================================== Outputs ===================================

# Configure what output to use when sending the data collected by the beat.

# ---------------------------- Elasticsearch Output ----------------------------
output.elasticsearch:
  # Array of hosts to connect to.
  hosts: [&quot;3.35.108.182:9200&quot;]

  # Protocol - either `http` (default) or `https`.
  #protocol: &quot;https&quot;

  # Authentication credentials - either API key or username/password.
  #api_key: &quot;id:api_key&quot;
  username: &quot;elk-user&quot;
  password: &quot;P@ssword!&quot;

# ------------------------------ Logstash Output -------------------------------
#output.logstash:
  # The Logstash hosts
  #hosts: [&quot;localhost:5044&quot;]

  # Optional SSL. By default is off.
  # List of root certificates for HTTPS server verifications
  #ssl.certificate_authorities: [&quot;/etc/pki/root/ca.pem&quot;]

  # Certificate for SSL client authentication
  #ssl.certificate: &quot;/etc/pki/client/cert.pem&quot;

  # Client Certificate Key
  #ssl.key: &quot;/etc/pki/client/cert.key&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Elasticsearch 또는 Logstash Output을 설정할 수 있다.&lt;/p&gt;
&lt;p&gt;Filebeat 실습 때 Logstash를 통해 Elasticsearch로 보내봤으니, 이번에는 바로 Elasticsearch로 보낸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;4. Kibana Dashboard 설정&lt;/h2&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;$ metricbeat setup --dashboards&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;86&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RNJmk/btqW4hYw4WU/kK5m0vcD9HFBqkSlbRLPKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RNJmk/btqW4hYw4WU/kK5m0vcD9HFBqkSlbRLPKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RNJmk/btqW4hYw4WU/kK5m0vcD9HFBqkSlbRLPKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRNJmk%2FbtqW4hYw4WU%2FkK5m0vcD9HFBqkSlbRLPKk%2Fimg.png&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;86&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;5. Metricbeat 실행&lt;/h2&gt;
&lt;p&gt;기본 초기화 셋팅&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;$ metricbeat setup -e&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;서비스 활성화&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ sudo systemctl enable metricbeat

$ sudo chkconfig --add metricbeat&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;시작&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;$ sudo systemctl start metricbeat&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;6. Elasticsearch Index 확인&lt;/h2&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;$ curl localhost:9200/_cat/indices?v&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1579&quot; data-origin-height=&quot;340&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDE6Fv/btqW4iDbxdz/tKiQBV0sGnXPvQxB0wQ4HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDE6Fv/btqW4iDbxdz/tKiQBV0sGnXPvQxB0wQ4HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDE6Fv/btqW4iDbxdz/tKiQBV0sGnXPvQxB0wQ4HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDE6Fv%2FbtqW4iDbxdz%2FtKiQBV0sGnXPvQxB0wQ4HK%2Fimg.png&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1579&quot; data-origin-height=&quot;340&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;7. Kibana 확인&lt;/h2&gt;
&lt;p&gt;우선 &lt;code&gt;metricbeat-*&lt;/code&gt; Index Pattern을 만들어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;7.1 Kibana Discover&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HCKha/btqWVQVtv0g/aogtHrtqYAjA2OGNzdoXZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HCKha/btqWVQVtv0g/aogtHrtqYAjA2OGNzdoXZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HCKha/btqWVQVtv0g/aogtHrtqYAjA2OGNzdoXZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHCKha%2FbtqWVQVtv0g%2FaogtHrtqYAjA2OGNzdoXZK%2Fimg.png&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;7.2 Kibana Dashboard&lt;/h3&gt;
&lt;p&gt;metricbeat의 kibana dashboard template를 다운받았기 때문에 Dashboard에 들어가면 수많은 템플릿이 기본적으로 제공된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dufQIl/btqWUM67nPI/FL4oeB4KtafxVkAzGKuM01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dufQIl/btqWUM67nPI/FL4oeB4KtafxVkAzGKuM01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dufQIl/btqWUM67nPI/FL4oeB4KtafxVkAzGKuM01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdufQIl%2FbtqWUM67nPI%2FFL4oeB4KtafxVkAzGKuM01%2Fimg.png&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이중 기본적인 [Metricbeat System] Host overview ECS 템플릿이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpdiCL/btqWWteF5VB/KvXlK1ZmEOYekSTlDNW6zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpdiCL/btqWWteF5VB/KvXlK1ZmEOYekSTlDNW6zK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpdiCL/btqWWteF5VB/KvXlK1ZmEOYekSTlDNW6zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpdiCL%2FbtqWWteF5VB%2FKvXlK1ZmEOYekSTlDNW6zK%2Fimg.png&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk3kPz/btqW1sMPXtX/gsjs2s2HrZAVCLSdH3ioKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk3kPz/btqW1sMPXtX/gsjs2s2HrZAVCLSdH3ioKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk3kPz/btqW1sMPXtX/gsjs2s2HrZAVCLSdH3ioKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk3kPz%2FbtqW1sMPXtX%2Fgsjs2s2HrZAVCLSdH3ioKK%2Fimg.png&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기본 템플릿 만으로도 충분히 활용 가능하며, 추가로 커스터마이징 하여 입맛에 맞게 변경할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/kr/beats/metricbeat&quot;&gt;https://www.elastic.co/kr/beats/metricbeat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/beats/metricbeat/current/index.html&quot;&gt;https://www.elastic.co/guide/en/beats/metricbeat/current/index.html&lt;/a&gt; (7.11)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>IT/Etc</category>
      <category>elasticsearch</category>
      <category>elk stack</category>
      <category>kibana</category>
      <category>logstash</category>
      <category>metricbeat</category>
      <category>System Metric</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/41</guid>
      <comments>https://suyeon96.tistory.com/41#entry41comment</comments>
      <pubDate>Sat, 13 Feb 2021 02:53:41 +0900</pubDate>
    </item>
    <item>
      <title>[ELK Stack] 3. Filebeat와 ELK Stack으로 Apache log 관리</title>
      <link>https://suyeon96.tistory.com/40</link>
      <description>&lt;h3&gt;ELK Stack 목차&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 1. ELK Stack 이란? (Beats, Logstash, Elasticsearch, Kibana)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 2. Elasticsearch, Logstash, Kibana 설치 및 구성&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;[ELK Stack] 3. Filebeat와 ELK Stack으로 Apache log 관리&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/41&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 4. Metricbeat로 System Monitoring&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. Filebeat 란?&lt;/h2&gt;
&lt;p&gt;log파일을 Logstash로 수집하지 않고 Filebeat를 사용하는 이유는 경량화되어 data 수집 및 전달에 최적화되어있기 때문이다.&lt;/p&gt;
&lt;p&gt;(Filebeat는 Logstash보다 시스템 공간과 사용 공간이 적기 때문에 더 적은 리소스를 요구한다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;filebeat.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;735&quot; width=&quot;600&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdKCbP/btqW8GwYg53/1ZEzzW0YWEFCeHF82VBJT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdKCbP/btqW8GwYg53/1ZEzzW0YWEFCeHF82VBJT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdKCbP/btqW8GwYg53/1ZEzzW0YWEFCeHF82VBJT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdKCbP%2FbtqW8GwYg53%2F1ZEzzW0YWEFCeHF82VBJT1%2Fimg.png&quot; data-filename=&quot;filebeat.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;735&quot; width=&quot;600&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Filebeat는 크게 &lt;code&gt;Prospector&lt;/code&gt;, &lt;code&gt;Harvester&lt;/code&gt;, &lt;code&gt;Spooler&lt;/code&gt;로 이루어져 있다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Prospector&lt;/b&gt;는 읽어야 할 소스를 식별하고 구분하는 역할을 한다. log, stdin 타입을 지원하며 Prospector 당 2개 이상의 type 선언이 가능하며, 어디까지 읽었는지 meta data를 별도 디스크에 저장한다. (default : &lt;code&gt;/var/lib/filebeat/registry&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Harvester&lt;/b&gt;는 파일을 읽고 출력하는 역할을 한다. 하나의 Harvester가 각각 하나의 파일을 담당하는 구조를 가진다. 파일을 열고 닫는 것까지 담당하기 때문에 Harvester가 실행되고 있다는 것은 file descriptor가 남아 있다는 의미이다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Spooler&lt;/b&gt;는 Harvester가 읽은 데이터를 집계하고 각각 설정한 output으로 전달한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. Apache 설치&lt;/h2&gt;
&lt;h3&gt;2.1 apache 설치&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;$ sudo yum -y install httpd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 apache 설치 확인&lt;/h3&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ httpd -v&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;89&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGvHnj/btqW4iwnmk2/wKulRaWYZKleljrMpL5Pdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGvHnj/btqW4iwnmk2/wKulRaWYZKleljrMpL5Pdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGvHnj/btqW4iwnmk2/wKulRaWYZKleljrMpL5Pdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGvHnj%2FbtqW4iwnmk2%2FwKulRaWYZKleljrMpL5Pdk%2Fimg.png&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;89&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.3 apache 실행&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;$ sudo systemctl start httpd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;560&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLmW9P/btqWVSZ8rkV/pNaVz7J4RpL9i3zkPPoxp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLmW9P/btqWVSZ8rkV/pNaVz7J4RpL9i3zkPPoxp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLmW9P/btqWVSZ8rkV/pNaVz7J4RpL9i3zkPPoxp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLmW9P%2FbtqWVSZ8rkV%2FpNaVz7J4RpL9i3zkPPoxp0%2Fimg.png&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;560&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.4 apache web service 접근&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6Xcsf/btqW4g6pxpW/s2Y6a2JNd32vV7qpC2hIa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6Xcsf/btqW4g6pxpW/s2Y6a2JNd32vV7qpC2hIa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6Xcsf/btqW4g6pxpW/s2Y6a2JNd32vV7qpC2hIa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6Xcsf%2FbtqW4g6pxpW%2Fs2Y6a2JNd32vV7qpC2hIa1%2Fimg.png&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.5 apache access log 확인&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;$ tail -f /var/log/httpd/access_log&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;787&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y4eqT/btqWX5xBL4f/k86hNr97lvMv4pvqa4RzO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y4eqT/btqWX5xBL4f/k86hNr97lvMv4pvqa4RzO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y4eqT/btqWX5xBL4f/k86hNr97lvMv4pvqa4RzO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY4eqT%2FbtqWX5xBL4f%2Fk86hNr97lvMv4pvqa4RzO0%2Fimg.png&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;787&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. Filebeat 설치&lt;/h2&gt;
&lt;h3&gt;3.1. Import the Elasticsearch GPG Key&lt;/h3&gt;
&lt;p&gt;공개 서명키 (public signing key) 설치&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo rpm --import https://packages.elastic.co/GPG-KEY-elasticsearch&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.2 Filebeat 설치&lt;/h3&gt;
&lt;p&gt;/etc/yum.repos.d/ 디렉토리 아래에 Elastic.repo 파일을 만들고 아래와 같이 내용을 입력한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ vi /etc/yum.repos.d/Elastic.repo
    [elastic-7.x]
    name=Elasticsearch repository for 7.x packages
    baseurl=https://artifacts.elastic.co/packages/7.x/yum
    gpgcheck=1
    gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
    enabled=1
    autorefresh=1
    type=rpm-md&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파일을 추가한 후 yum명령을 이용해 filebeat 설치한다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;$ sudo yum -y install filebeat&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.3 Filebeat module list 확인&lt;/h3&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;$ filebeat modules list&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;427&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bowNcj/btqWU74dfRQ/8zPojfPKQrrDm1UUKWsr6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bowNcj/btqWU74dfRQ/8zPojfPKQrrDm1UUKWsr6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bowNcj/btqWU74dfRQ/8zPojfPKQrrDm1UUKWsr6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbowNcj%2FbtqWU74dfRQ%2F8zPojfPKQrrDm1UUKWsr6k%2Fimg.png&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;427&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;4. Filebeat 설정&lt;/h2&gt;
&lt;p&gt;Filebeat를 이용하여 apache log를 수집하고 logstash 또는 elasticsearch에 보내는 방법은 크게 2가지가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.1 Enable apache module&lt;/h3&gt;
&lt;p&gt;첫 번째는 filebeat에서 제공하는 apache 모듈을 이용하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# apache module 활성화
$ filebeat modules enable apache

$ filebeat modules list&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;312&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmyYUw/btqWULmQQZu/DHQoBUrxkQieiNLTOOKNt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmyYUw/btqWULmQQZu/DHQoBUrxkQieiNLTOOKNt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmyYUw/btqWULmQQZu/DHQoBUrxkQieiNLTOOKNt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmyYUw%2FbtqWULmQQZu%2FDHQoBUrxkQieiNLTOOKNt1%2Fimg.png&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;312&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;apache 모듈을 활성화시킨 후 초기 환경을 셋팅한다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ filebeat setup -e  # -e : debug mode&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이후 &lt;code&gt;/etc/filebeat/modules.d/apache.yml&lt;/code&gt; 파일을 아래와 같이 수정한다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;$ vi /etc/filebeat/modules.d/apache.yml&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# Module: apache
# Docs: https://www.elastic.co/guide/en/beats/filebeat/7.10/filebeat-module-apache.html

- module: apache
  # Access logs
  access:
    enabled: true
    input:
      fields:
        server_name: elk-web
        log_type: apache_access
    var.paths: [&quot;/var/log/httpd/access_log&quot;]

    # Set custom paths for the log files. If left empty,
    # Filebeat will choose the paths depending on your OS.
    #var.paths:

  # Error logs
  error:
    enabled: true
    input:
      fields:
        server_name: elk-web
        log_type: apache_error
    var.paths: [&quot;/var/log/httpd/error_log&quot;]

    # Set custom paths for the log files. If left empty,
    # Filebeat will choose the paths depending on your OS.
    #var.paths:&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 우리는 apache module을 사용하지 않고 filebeat.yml 파일을 직접 수정하여 log를 수집할 것이다.&lt;/p&gt;
&lt;p&gt;이를 위해 위에서 활성화한 apache module은 다시 비활성화 해주자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ filebeat modules disable apache&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;56&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8GNgi/btqW1trqhFa/5OTLcBOh2yhsFkJ5fIXdU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8GNgi/btqW1trqhFa/5OTLcBOh2yhsFkJ5fIXdU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8GNgi/btqW1trqhFa/5OTLcBOh2yhsFkJ5fIXdU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8GNgi%2FbtqW1trqhFa%2F5OTLcBOh2yhsFkJ5fIXdU0%2Fimg.png&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;56&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.2 Configuration filebeat.yml&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/etc/filebeat/filebeat.yml&lt;/code&gt; 파일을 아래와 같이 설정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Inputs&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;# ============================== Filebeat inputs ===============================

filebeat.inputs:

# Each - is an input. Most options can be set at the input level, so
# you can use different inputs for various configurations.
# Below are the input specific configurations.

- type: log
  # Change to true to enable this input configuration.
  enabled: true

  # Paths that should be crawled and fetched. Glob based paths.
  paths:
    - /var/log/httpd/access_log
    #- c:\programdata\elasticsearch\logs\*

  # Exclude lines. A list of regular expressions to match. It drops the lines that are
  # matching any regular expression from the list.
  #exclude_lines: ['^DBG']

  # Include lines. A list of regular expressions to match. It exports the lines that are
  # matching any regular expression from the list.
  #include_lines: ['^ERR', '^WARN']

  # Exclude files. A list of regular expressions to match. Filebeat drops the files that
  # are matching any regular expression from the list. By default, no files are dropped.
  #exclude_files: ['.gz$']

  # Optional additional fields. These fields can be freely picked
  # to add additional information to the crawled log files for filtering
  fields:
    server_name: elk-web
    log_type: apache-access
  #fields:
  #  level: debug
  #  review: 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;fields&lt;/code&gt;의 항목은 output으로 보낼 때 추가한 필드를 포함하여 보낸다.&lt;/p&gt;
&lt;p&gt;나중에 Kibana에서 해당 필드 키워드를 이용하여 data를 컨트롤할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Outputs&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# ================================== Outputs ===================================

# Configure what output to use when sending the data collected by the beat.

# ---------------------------- Elasticsearch Output ----------------------------
#output.elasticsearch:
  # Array of hosts to connect to.
  #hosts: [&quot;localhost:9200&quot;]

  # Protocol - either `http` (default) or `https`.
  #protocol: &quot;https&quot;

  # Authentication credentials - either API key or username/password.
  #api_key: &quot;id:api_key&quot;
  #username: &quot;elastic&quot;
  #password: &quot;changeme&quot;

# ------------------------------ Logstash Output -------------------------------
output.logstash:
  # The Logstash hosts
  hosts: [&quot;3.35.108.182:5044&quot;]

  # Optional SSL. By default is off.
  # List of root certificates for HTTPS server verifications
  #ssl.certificate_authorities: [&quot;/etc/pki/root/ca.pem&quot;]

  # Certificate for SSL client authentication
  #ssl.certificate: &quot;/etc/pki/client/cert.pem&quot;

  # Client Certificate Key
  #ssl.key: &quot;/etc/pki/client/cert.key&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;elasticsearch output을 주석 처리하고 logstash output을 활성화한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hosts&lt;/code&gt;는 elk-master 서버의 IP로 설정한다.&lt;/p&gt;
&lt;p&gt;hosts에는 여러 노드를 입력할 수 있으며, 각각의 host에서 data를 균등하게 보내고 싶다면 &lt;code&gt;loadbalance: true&lt;/code&gt; 설정을 넣어주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;5. Run Filebeat&lt;/h2&gt;
&lt;p&gt;systemd 기준&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;$ sudo systemctl start filebeat&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Filebeat 데몬 로그 확인&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;$ tail -f /var/log/filebeat/filebeat&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;6. Logstash 파이프라인 설정&lt;/h2&gt;
&lt;p&gt;/etc/logstash/conf.d/filebeats.conf를 아래와 같이 설정한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;$ vi /etc/logstash/conf.d/filebeats.conf&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;puppet&quot;&gt;&lt;code&gt;input {
  beats {
    port =&amp;gt; 5044
  }
}
filter {
  grok {
    match =&amp;gt; { &quot;message&quot; =&amp;gt; &quot;%{TIMESTAMP_ISO8601:time}\t%{DATA:tag}\t{ % {DATA:data} }&quot; }
  }
  mutate {
    add_field =&amp;gt; { &quot;json_data&quot; =&amp;gt; &quot;{ %{data}}&quot; }
  }
  json {
    source =&amp;gt; &quot;json_data&quot;
  }
  alter {
    remove_field =&amp;gt; [ &quot;data&quot;, &quot;json_data&quot; ]
  }
  date {
    match =&amp;gt; [ &quot;time&quot;, &quot;yyyy-MM-dd'T'HH:mm:ssZZ&quot; ]
  }
}
output {
  elasticsearch {
    hosts =&amp;gt; &quot;localhost:9200&quot;
    index =&amp;gt; &quot;%{[fields][log_type]}-%{+YYYY.MM.dd}&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;filter에서 grok 등의 패턴을 이용하여 메시지를 가공할 수 있지만 일단 test를 위해 filter 없이 bypass 시켜보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;7. Logstash 실행&lt;/h2&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;$ sh /usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/filebeats.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;8. Elasticsearch index 생성 확인&lt;/h2&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;$ curl localhost:9200/_cat/indices?v&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;253&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlLAsQ/btqW1seZ64u/sG6hf70odJKNNOQvfL3HbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlLAsQ/btqW1seZ64u/sG6hf70odJKNNOQvfL3HbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlLAsQ/btqW1seZ64u/sG6hf70odJKNNOQvfL3HbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlLAsQ%2FbtqW1seZ64u%2FsG6hf70odJKNNOQvfL3HbK%2Fimg.png&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;253&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;9. Kibana 확인&lt;/h2&gt;
&lt;h3&gt;9.1 Kibana index pattern 생성&lt;/h3&gt;
&lt;p&gt;index pattern을 검색하면 방금 생성한 apache-access-2021.02.08이 확인된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GGlrH/btqWXbkypa5/KqABhk3RzKAluvNl4V60f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GGlrH/btqWXbkypa5/KqABhk3RzKAluvNl4V60f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GGlrH/btqWXbkypa5/KqABhk3RzKAluvNl4V60f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGGlrH%2FbtqWXbkypa5%2FKqABhk3RzKAluvNl4V60f0%2Fimg.png&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;@timestamp 필드를 설정하고 최종 생성을 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyJMNw/btqW1r8dDTq/xcqVelzgK7Fv1TP1FjK8TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyJMNw/btqW1r8dDTq/xcqVelzgK7Fv1TP1FjK8TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyJMNw/btqW1r8dDTq/xcqVelzgK7Fv1TP1FjK8TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyJMNw%2FbtqW1r8dDTq%2FxcqVelzgK7Fv1TP1FjK8TK%2Fimg.png&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만들어진 Index Patterns에 아까 filebeat.yml에서 설정한 2개의 fields값이 포함되어 있는 것을 확인할 수 있다. (log_type, server_name)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OkVIb/btqW4gL7KIL/950zkyK31kM7s96F0j9fbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OkVIb/btqW4gL7KIL/950zkyK31kM7s96F0j9fbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OkVIb/btqW4gL7KIL/950zkyK31kM7s96F0j9fbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOkVIb%2FbtqW4gL7KIL%2F950zkyK31kM7s96F0j9fbk%2Fimg.png&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;9.2 Kibana 데이터 탐색 및 조회&lt;/h3&gt;
&lt;p&gt;Discover 메뉴로 진입하면 elk-web 서버의 apache access_log를 수집한 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OUCdx/btqWX5j30ab/TQjGIcCrHDzOEqhNRHOO60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OUCdx/btqWX5j30ab/TQjGIcCrHDzOEqhNRHOO60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OUCdx/btqWX5j30ab/TQjGIcCrHDzOEqhNRHOO60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOUCdx%2FbtqWX5j30ab%2FTQjGIcCrHDzOEqhNRHOO60%2Fimg.png&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;message field를 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kSutj/btqWVQ2lB75/Pds9mHrGib30sXJka9nTnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kSutj/btqWVQ2lB75/Pds9mHrGib30sXJka9nTnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kSutj/btqWVQ2lB75/Pds9mHrGib30sXJka9nTnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkSutj%2FbtqWVQ2lB75%2FPds9mHrGib30sXJka9nTnk%2Fimg.png&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/beats/filebeat/7.10/filebeat-overview.html&quot;&gt;https://www.elastic.co/guide/en/beats/filebeat/7.10/filebeat-overview.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/beats/filebeat/current/setup-repositories.html&quot;&gt;https://www.elastic.co/guide/en/beats/filebeat/current/setup-repositories.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/beats/filebeat/current/logstash-output.html&quot;&gt;https://www.elastic.co/guide/en/beats/filebeat/current/logstash-output.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>IT/Etc</category>
      <category>apache</category>
      <category>elasticsearch</category>
      <category>elk stack</category>
      <category>Filebeats</category>
      <category>kibana</category>
      <category>log</category>
      <category>logstash</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/40</guid>
      <comments>https://suyeon96.tistory.com/40#entry40comment</comments>
      <pubDate>Sat, 13 Feb 2021 02:47:06 +0900</pubDate>
    </item>
    <item>
      <title>[ELK Stack] 2. Elasticsearch, Logstash, Kibana 설치 및 구성</title>
      <link>https://suyeon96.tistory.com/39</link>
      <description>&lt;h3&gt;ELK Stack 목차&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 1. ELK Stack 이란? (Beats, Logstash, Elasticsearch, Kibana)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;[ELK Stack] 2. Elasticsearch, Logstash, Kibana 설치 및 구성&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 3. Filebeat와 ELK Stack으로 Apache log 관리&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/41&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 4. Metricbeat로 System Monitoring&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. 환경 구성&lt;/h2&gt;
&lt;p&gt;ELK Stack으로 web 서버의 Apache log와 metric data를 분석하고 관리하는 환경을 구성해보려 한다.&lt;/p&gt;
&lt;p&gt;우선 AWS EC2로 web 서버와 master 서버를 준비하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;0.jpg&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;123&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MAPjI/btqXb2T3dcC/oIbZtN7wwu7RABkk1FdJek/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MAPjI/btqXb2T3dcC/oIbZtN7wwu7RABkk1FdJek/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MAPjI/btqXb2T3dcC/oIbZtN7wwu7RABkk1FdJek/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMAPjI%2FbtqXb2T3dcC%2FoIbZtN7wwu7RABkk1FdJek%2Fimg.jpg&quot; data-filename=&quot;0.jpg&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;123&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;web 서버에는 Filebeat와 Metricbeat를 설치하여 Apache log와 시스템 메트릭 data를 수집한다.&lt;/p&gt;
&lt;p&gt;master 서버에는 Elasticsearch, Logstash, Kibana를 설치하여 web서버의 data를 집계한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. JAVA 환경 설정&lt;/h2&gt;
&lt;p&gt;Elasticsearch 및 Logstash 실행을 위해 자바 1.8 이상의 버전을 설치해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.1 JAVA 설치&lt;/h3&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ yum list java*jdk-devel
  Loaded plugins: fastestmirror, langpacks
  Available Packages
  java-1.8.0-openjdk-devel.i686 1:1.8.0.275.b01-0.el7_9 updates
  java-1.8.0-openjdk-devel.x86_64 1:1.8.0.275.b01-0.el7_9 updates
  java-11-openjdk-devel.i686 1:11.0.9.11-2.el7_9 updates
  java-11-openjdk-devel.x86_64 1:11.0.9.11-2.el7_9 updates

$ yum -y install java-1.8.0-openjdk-devel.x86_64

$ java -version
  openjdk version &quot;1.8.0_275&quot;
  OpenJDK Runtime Environment (build 1.8.0_275-b01)
  OpenJDK 64-Bit Server VM (build 25.275-b01, mixed mode)

$ readlink -f /usr/bin/java
  /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/bin/java&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 JAVA 환경변수 설정&lt;/h3&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ vi /etc/profile
  # setting java
  JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64
  PATH=$PATH:$JAVA_HOME/bin
  CLASSPATH=$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
  export JAVA_HOME PATH CLASSPATH

$ source /etc/profile
$ echo $JAVA_HOME
  /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. ElasticSearch / logstash / kibana 설치&lt;/h2&gt;
&lt;p&gt;RPM(yum)으로 설치하며 systemd를 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;87&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7Z2ry/btqW1tdSTXO/AbShdCklHT0oQNP8nmkj60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7Z2ry/btqW1tdSTXO/AbShdCklHT0oQNP8nmkj60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7Z2ry/btqW1tdSTXO/AbShdCklHT0oQNP8nmkj60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7Z2ry%2FbtqW1tdSTXO%2FAbShdCklHT0oQNP8nmkj60%2Fimg.png&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;87&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.1 Import the Elasticsearch GPG Key&lt;/h3&gt;
&lt;p&gt;공개 서명키 (public signing key) 설치&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.2 Installing from the RPM repository&lt;/h3&gt;
&lt;p&gt;먼저 최신 버전의 elasticsearch를 yum으로 설치하기 위해 /etc/yum.repos.d/ 디렉토리 아래에 elasticsearch.repo 파일을 만들고 아래와 같이 내용을 입력한다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;$ vi /etc/yum.repos.d/Elastic.repo&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1613151307172&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[elasticsearch-7.x]
name=Elasticsearch repository for 7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;236&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rcobW/btqW4hjWkJT/gBTnqgGKWcBZkwO4pLx18k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rcobW/btqW4hjWkJT/gBTnqgGKWcBZkwO4pLx18k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rcobW/btqW4hjWkJT/gBTnqgGKWcBZkwO4pLx18k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrcobW%2FbtqW4hjWkJT%2FgBTnqgGKWcBZkwO4pLx18k%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;236&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파일을 추가한 후 yum명령을 이용해 Elasticsearch, Logstash, Kibana를 설치한다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;$ sudo yum -y install elasticsearch logstash kibana

$ yum list elasticsearch logstash kibana&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;255&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1SSFk/btqXb1HBRcS/CjcxDaSjPkbAOarKpJkhWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1SSFk/btqXb1HBRcS/CjcxDaSjPkbAOarKpJkhWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1SSFk/btqXb1HBRcS/CjcxDaSjPkbAOarKpJkhWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1SSFk%2FbtqXb1HBRcS%2FCjcxDaSjPkbAOarKpJkhWk%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;255&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.3 ELK 환경 설정&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;3.3.1 jvm.options&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Elasticsearch과 Logstash는 Java Runtime 가상머신 위에서 실행되는데 1gb의 힙 메모리가 기본으로 설정되어 있다. (7.0 기준) 원활한 환경을 위해 jvm.options 파일에서 아래 내용들을 수정한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# elasticsearch
$ vi /etc/elasticsearch/jvm.options
  # -Xms1g
  # -Xmx1g
  -Xms2g
  -Xmx2g

# logstash
$ vi /etc/logstash/jvm.options
  # -Xms1g
  # -Xmx1g
  -Xms2g
  -Xmx2g&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;253&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GLxoq/btqWWtyVyI4/09MPfk4UlGZi13laLpJh21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GLxoq/btqWWtyVyI4/09MPfk4UlGZi13laLpJh21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GLxoq/btqWWtyVyI4/09MPfk4UlGZi13laLpJh21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGLxoq%2FbtqWWtyVyI4%2F09MPfk4UlGZi13laLpJh21%2Fimg.png&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;253&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;(테스트용 master서버는 2Core/4GB라 logstash는 1g로 설정함)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3.3.2 elasticsearch.yml&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;elasticsearch 실행 환경에 대한 설정들은 대부분 elasticsearch.yml 파일에서 설정한다. YAML 파일이기 때문에 옵션을 설정할 때는 들여쓰기에 유의해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;vi /etc/elasticsearch/elasticsearch.yml
  ...
  # -------------------- Cluster --------------------
  cluster.name: suyeon-app
  # -------------------- Node --------------------
  node.name: suyeon.elktest.com
  # -------------------- Paths --------------------
  path.data: /ELK/elasticsearch        # 별도 disk
  path.logs: /ELK/log/elasticsearch    # 별도 disk
  # -------------------- Network --------------------
  network.host: 0.0.0.0    # ANY
  http.port: 9200
  # -------------------- Discovery --------------------
  discovery.seed_hosts: [&quot;192.168.56.x&quot;, &quot;127.0.0.1&quot;, &quot;[::1]&quot;]    # my IP
  cluster.initial_master_nodes: [&quot;suyeon.elktest.com&quot;]

  node.master: true&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cluster.name&lt;/code&gt; : 클러스터 명 설정 (default는 elasticsearch이며 충돌을 방지하기 위해 반드시 고유한 이름으로 설정 필요)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;node.name&lt;/code&gt; : elasticsearch 노드 명 설정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;path.data&lt;/code&gt; : 색인된 데이터를 저장하는 경로 지정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;path.logs&lt;/code&gt; : elasticsearch 실행 로그를 저장하는 경로 지정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bootstrap.memory_lock&lt;/code&gt; : Elasticsearch가 사용중인 힙메모리 영역을 다른 자바 프로그램이 간섭 못하도록 미리 점유하는 설정 (항상 true로 사용하는 것을 권장)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;network.host&lt;/code&gt; : Elasticsearch가 실행되는 서버의 ip 주소 (default는 127.0.0.1(loopback)이며 0.0.0.0으로 설정 시 ANY로 통신이 가능)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http.port&lt;/code&gt; : Elasticsearch가 클라이언트와 통신하기 위한 http 포트를 설정 (default는 9200이며, 포트가 이미 사용 중인 경우 9200 ~ 9299 사이 값을 차례대로 사용)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;transport.port&lt;/code&gt; : Elasticsearch 노드들 끼리 서로 통신하기 위한 tcp 포트를 설정 (default는 9300이며, 포트가 이미 사용 중인 경우 9300 ~ 9399 사이 값을 차례대로 사용)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cluster.initial_master_nodes&lt;/code&gt; : 클러스터가 최초 실행 될 때 명시된 노드들을 대상으로 마스터 노드를 선출&lt;/li&gt;
&lt;li&gt;&lt;code&gt;node.master&lt;/code&gt; : 마스터 후보(master eligible) 노드 여부를 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;디스크 용량 및 성능 관리차원에서 로그파일 및 데이터파일을 별도로 분리 저장하여 관리하는 것이 좋다. 이를 위해 EBS를 새로 생성하여 마운트하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;3-1.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;143&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N8tJD/btqWUL1uunf/9MLfzL5cr6yATxc2LkL4F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N8tJD/btqWUL1uunf/9MLfzL5cr6yATxc2LkL4F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N8tJD/btqWUL1uunf/9MLfzL5cr6yATxc2LkL4F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN8tJD%2FbtqWUL1uunf%2F9MLfzL5cr6yATxc2LkL4F1%2Fimg.png&quot; data-filename=&quot;3-1.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;143&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;yml 파일에 설정한 데로 data와 log 파일을 담을 디렉토리를 생성하고 권한을 설정해야 한다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$ mkdir -p /ELK/elasticsearch
$ mkdir -p /ELK/log/elasticsearch
$ chown -R elasticsearch.elasticsearch /ELK&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3.3.3 logstash.yml&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;logstash에 대한 설정 또한 logstash.yml 파일에서 설정한다.&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;$ vi /etc/logstash/logstash.yml
  # -------------------- Data path --------------------
  #path.data: /var/lib/logstash
  path.data: /ELK/logstash
  # -------------------- Data path --------------------
  #path.logs: /var/log/logstash
  path.logs: /ELK/log/logstash&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pipeline.batch.size&lt;/code&gt; : 개별 worker 스레드가 input에서 수집할 최대 이벤트 개수 설정. 일반적으로 배치크기가 클수록 효율적이지만 메모리 오버해드가 증가함. (default : 125)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pipeline.batch.delay&lt;/code&gt; : 크기가 작은 배치를 pipeline worker에게 발송하기 전 각 이벤트를 기다리는 시간 (default : 50ms)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;logstash 또한 data와 log 디렉토리를 따로 관리하자.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$ mkdir -p /ELK/logstash
$ mkdir -p /ELK/log/logstash
$ chown -R logstash.logstash /ELK&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3.3.4 kibana.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ vi /etc/kibana/kibana.yml
  #server.port: 5601
  server.port: 5601
  #server.host: &quot;localhost&quot;
  server.host: &quot;0.0.0.0&quot;
  #server.name: &quot;your-hostname&quot;
  server.name: &quot;ktds.elklab.com&quot;
  #elasticsearch.hosts: [&quot;http://localhost:9200&quot;]
  elasticsearch.hosts: [&quot;http://localhost:9200&quot;]    # or server IP&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;server.port&lt;/code&gt; : kibana가 통신할 포트 설정 (Default : 5601)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server.host&lt;/code&gt; : 백엔드 서버의 host IP 지정 (Default는 &quot;localhost&quot;이며, 원격 사용자의 연결을 허용하려면 kibana 서버의 IP주소 또는 DNS 이름으로 설정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server.name&lt;/code&gt; : kibana 인스턴스 명 설정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;elasticsearch.hosts&lt;/code&gt; : 모든 쿼리에 사용할 Elasticsearch 인스턴스의 url 설정 (설정된 모든 노드는 동일한 클러스터에 있어야 함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.4 Run ELK Service&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Elasticsearch 서비스&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;$ sudo /bin/systemctl daemon-reload

# To configure Elasticsearch to start automatically when the system boots up, run the following commands
$ sudo chkconfig --add elasticsearch
$ sudo systemctl enable elasticsearch.service

# Elasticsearch can be started and stopped as follows
$ sudo systemctl start elasticsearch.service
$ sudo systemctl stop elasticsearch.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;logstash 서비스&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ sudo chkconfig --add logstash
$ sudo systemctl enable logstash.service

$ sudo systemctl start logstash.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;kibana 서비스&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ sudo chkconfig --add kibana
$ sudo systemctl enable kibana.service

$ sudo systemctl start kibana.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.5 상태 확인&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;elasticsearch&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo systemctl status elasticsearch
$ ps-ef | grep elasticsearch&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;481&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be4CjS/btqWX5YIxHs/ULyUIRH6rbkBTK2ey1saSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be4CjS/btqWX5YIxHs/ULyUIRH6rbkBTK2ey1saSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be4CjS/btqWX5YIxHs/ULyUIRH6rbkBTK2ey1saSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe4CjS%2FbtqWX5YIxHs%2FULyUIRH6rbkBTK2ey1saSk%2Fimg.png&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;481&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;logstash&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ systemctl status logstash.service
$ ps -ef | grep logstash&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;453&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/20obY/btqWVQVtjyS/jBgBiPNkRGvxChjCSG8q61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/20obY/btqWVQVtjyS/jBgBiPNkRGvxChjCSG8q61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/20obY/btqWVQVtjyS/jBgBiPNkRGvxChjCSG8q61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F20obY%2FbtqWVQVtjyS%2FjBgBiPNkRGvxChjCSG8q61%2Fimg.png&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;453&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;kibana&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ systemctl status kibana.service
$ ps -ef | grep kibana&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;674&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8uN8V/btqXb18FlDx/eTWfJ7nUaGqiv0VrBmqw21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8uN8V/btqXb18FlDx/eTWfJ7nUaGqiv0VrBmqw21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8uN8V/btqXb18FlDx/eTWfJ7nUaGqiv0VrBmqw21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8uN8V%2FbtqXb18FlDx%2FeTWfJ7nUaGqiv0VrBmqw21%2Fimg.png&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;674&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;네트워크 상태 (port) 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;$ netstat -nlp | grep 9200
$ netstat -nlp | grep 5601&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;18-1.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;116&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bspnaf/btqW8FLBC2f/dyDsbLuZGcKKqWSirZm8t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bspnaf/btqW8FLBC2f/dyDsbLuZGcKKqWSirZm8t0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bspnaf/btqW8FLBC2f/dyDsbLuZGcKKqWSirZm8t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbspnaf%2FbtqW8FLBC2f%2FdyDsbLuZGcKKqWSirZm8t0%2Fimg.png&quot; data-filename=&quot;18-1.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;116&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.6 방화벽 open&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ firewall-cmd --add-port=9200/tcp --permanent --zone=public
$ firewall-cmd --add-port=9300/tcp --permanent --zone=public

$ firewall-cmd --add-port=5443/udp --permanent --zone=public
$ firewall-cmd --add-port=5443/tcp --permanent --zone=public

$ firewall-cmd --add-port=5601/tcp --permanent --zone=public

$ firewall-cmd --reload
$ firewall-cmd --list-all
  ...
  ports: 9200/tcp 9300/tcp 5443/udp 5443/tcp 5601/tcp
  ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;master 서버의 인바운드 규칙을 아래와 같이 설정하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;18-2.jpg&quot; data-origin-width=&quot;1575&quot; data-origin-height=&quot;343&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zb2FU/btqW1tkDTbw/LFEumdlpER1ODLCmuEnZGk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zb2FU/btqW1tkDTbw/LFEumdlpER1ODLCmuEnZGk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zb2FU/btqW1tkDTbw/LFEumdlpER1ODLCmuEnZGk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZb2FU%2FbtqW1tkDTbw%2FLFEumdlpER1ODLCmuEnZGk%2Fimg.jpg&quot; data-filename=&quot;18-2.jpg&quot; data-origin-width=&quot;1575&quot; data-origin-height=&quot;343&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;4. Checking ELK stack&lt;/h2&gt;
&lt;h3&gt;4.1 Elasticsearch running 테스트&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ curl -X GET &quot;localhost:9200/?pretty&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot; : &quot;Cp8oag6&quot;,
  &quot;cluster_name&quot; : &quot;elasticsearch&quot;,
  &quot;cluster_uuid&quot; : &quot;AT69_T_DTp-1qgIJlatQqA&quot;,
  &quot;version&quot; : {
    &quot;number&quot; : &quot;7.10.2&quot;,
    &quot;build_flavor&quot; : &quot;default&quot;,
    &quot;build_type&quot; : &quot;tar&quot;,
    &quot;build_hash&quot; : &quot;f27399d&quot;,
    &quot;build_date&quot; : &quot;2016-03-30T09:51:41.449Z&quot;,
    &quot;build_snapshot&quot; : false,
    &quot;lucene_version&quot; : &quot;8.7.0&quot;,
    &quot;minimum_wire_compatibility_version&quot; : &quot;1.2.3&quot;,
    &quot;minimum_index_compatibility_version&quot; : &quot;1.2.3&quot;
  },
  &quot;tagline&quot; : &quot;You Know, for Search&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;505&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPna9w/btqWU8IUdeu/YmtMnUXZvYBLsnWY5fObPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPna9w/btqWU8IUdeu/YmtMnUXZvYBLsnWY5fObPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPna9w/btqWU8IUdeu/YmtMnUXZvYBLsnWY5fObPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPna9w%2FbtqWU8IUdeu%2FYmtMnUXZvYBLsnWY5fObPK%2Fimg.png&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;505&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.2 Elasticsearch web 접속 테스트&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KW4Yj/btqW4g6ptTV/NsNRlnAkPNdWxlX0XrI2U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KW4Yj/btqW4g6ptTV/NsNRlnAkPNdWxlX0XrI2U0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KW4Yj/btqW4g6ptTV/NsNRlnAkPNdWxlX0XrI2U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKW4Yj%2FbtqW4g6ptTV%2FNsNRlnAkPNdWxlX0XrI2U0%2Fimg.png&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.3 Kibana 접속 테스트&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6NCo9/btqW8G4MWoT/QJblUykfLn9Cenjf3ataKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6NCo9/btqW8G4MWoT/QJblUykfLn9Cenjf3ataKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6NCo9/btqW8G4MWoT/QJblUykfLn9Cenjf3ataKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6NCo9%2FbtqW8G4MWoT%2FQJblUykfLn9Cenjf3ataKK%2Fimg.png&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAdm8b/btqWWtFKo5S/fZyntSfM9A0uXh9TWjgcbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAdm8b/btqWWtFKo5S/fZyntSfM9A0uXh9TWjgcbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAdm8b/btqWWtFKo5S/fZyntSfM9A0uXh9TWjgcbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAdm8b%2FbtqWWtFKo5S%2FfZyntSfM9A0uXh9TWjgcbK%2Fimg.png&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;elasticsearch 참고 : &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/setup.html&quot;&gt;https://www.elastic.co/guide/en/elasticsearch/reference/current/setup.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;logstash 참고 : &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/current/setup-logstash.html&quot;&gt;https://www.elastic.co/guide/en/logstash/current/setup-logstash.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;kibana 참고 : &lt;a href=&quot;https://www.elastic.co/guide/en/kibana/current/setup.html&quot;&gt;https://www.elastic.co/guide/en/kibana/current/setup.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>IT/Etc</category>
      <category>AWS ec2</category>
      <category>elasticsearch</category>
      <category>elk stack</category>
      <category>kibana</category>
      <category>logstash</category>
      <category>setting</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/39</guid>
      <comments>https://suyeon96.tistory.com/39#entry39comment</comments>
      <pubDate>Sat, 13 Feb 2021 02:37:14 +0900</pubDate>
    </item>
    <item>
      <title>[ELK Stack] 1. ELK Stack 이란? (Beats, Logstash, Elasticsearch, Kibana)</title>
      <link>https://suyeon96.tistory.com/38</link>
      <description>&lt;h3&gt;ELK Stack 목차&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;[ELK Stack] 1. ELK Stack 이란? (Beats, Logstash, &lt;span style=&quot;color: #333333;&quot;&gt;Elasticsearch,&lt;/span&gt; Kibana)&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 2. Elasticsearch, Logstash, Kibana 설치 및 구성&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 3. Filebeat와 ELK Stack으로 Apache log 관리&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://suyeon96.tistory.com/41&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[ELK Stack] 4. Metricbeat로 System Monitoring&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;ELK Stack 이란?&lt;/h2&gt;
&lt;p&gt;&lt;b&gt;ELK&lt;/b&gt;는 &lt;code&gt;Elasticsearch&lt;/code&gt;, &lt;code&gt;Logstash&lt;/code&gt;, &lt;code&gt;Kibana&lt;/code&gt; 세 가지 오픈소스 프로젝트의 이니셜이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;elk-stack-3-elks-stacked.png&quot; data-origin-width=&quot;555&quot; data-origin-height=&quot;300&quot; width=&quot;500&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/26yA3/btqW1slLJvC/l92A9sVpzF4UY0vyK60hjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/26yA3/btqW1slLJvC/l92A9sVpzF4UY0vyK60hjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/26yA3/btqW1slLJvC/l92A9sVpzF4UY0vyK60hjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F26yA3%2FbtqW1slLJvC%2Fl92A9sVpzF4UY0vyK60hjK%2Fimg.png&quot; data-filename=&quot;elk-stack-3-elks-stacked.png&quot; data-origin-width=&quot;555&quot; data-origin-height=&quot;300&quot; width=&quot;500&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Elasticsearch&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Elasticsearch&lt;/b&gt;는 JSON 기반의 분산형 RESTful 검색 및 분석 엔진이다.&lt;/p&gt;
&lt;p&gt;Apache Lucene 기반으로 구축되었으며 분산형 및 개방형을 특징으로 한다.&lt;/p&gt;
&lt;p&gt;Lucene이 JAVA로 만들어졌기 때문에 Elasticsearch 또한 JAVA로 개발되어 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Elasticsearch의 가장 큰 장점은 &lt;b&gt;실시간&lt;/b&gt;(Real-time) 분석 시스템이라는 것이다.&lt;/p&gt;
&lt;p&gt;현재 대용량 데이터 분석에 가장 널리 사용되고 있는 하둡(Hadoop)은 배치 기반으로 &lt;code&gt;데이터수집 &amp;rarr; 분석 &amp;rarr; 결과도출&lt;/code&gt; 루틴으로 실행된다.&lt;/p&gt;
&lt;p&gt;반면 Elasticsearch는 클러스터가 실행되고 있는 동안 계속해서 데이터가 입력(Indexing)되고, 그와 동시에 실시간에 가까운 속도로 색인된 데이터의 검색과 집계가 가능하다.&lt;/p&gt;
&lt;p&gt;이가 가능한 이유는 &lt;b&gt;역인덱스&lt;/b&gt;(Inverted file index) 데이터 구조를 사용하여 &lt;b&gt;풀텍스트&lt;/b&gt;(Full text) 검색을 할 수 있도록 설계되었기 때문이다.&lt;/p&gt;
&lt;p&gt;역색인이란 전공서적 맨 뒷편의 색인된 키워드를 이용해 역으로 본문을 찾는 방식을 생각하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로 Elasicserach의 핵심 기능들은 Apache 2.0 라이센스로 배포되고 있으며, 6.3 버전부터는 Elastic 라이센스와 Apache 라이센스가 섞여있다.&lt;/p&gt;
&lt;p&gt;X-Pack 디렉토리 아래의 파일들이 Elastic EULA 라이센스가 적용되고 그 외의 파일은 Apache 라이센스를 따른다.&lt;/p&gt;
&lt;p&gt;최근 이를 두고 벌어지는 AWS와 Elastic사의 분쟁이 굉장한 이슈이다.&lt;/p&gt;
&lt;p&gt;이에 대해 이야기하려면 따로 포스팅을 해야할 정도라 간단하게 요약하자면...&lt;/p&gt;
&lt;p&gt;Elastic사에서 AWS를 막기 위해 라이센스를 변경했고, AWS는 Apache 2.0 라이센스의 Elasticsearch를 fork하여 직접 운영하겠다고 발표하였다.&lt;/p&gt;
&lt;p&gt;앞으로 Elastic과 AWS의 Elasticsearch(상표권은 사용 못하겠지만)가 어떤 행보를 보일지, 개발자 커뮤니티는 어떤 버전을 선택할지가 초미의 관심사이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Logstash&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Logstash&lt;/b&gt;는 실시간 파이프라인 기능을 가진 오픈소스 데이터 수집 엔진이다.&lt;/p&gt;
&lt;p&gt;JRuby로 되어있으며, Ruby로 개발되어 JAVA Runtime 가상머신 위에서 돌아간다.&lt;/p&gt;
&lt;p&gt;원래는 Elasticsearch와 별개로 시작된 프로젝트였으나, Logstash에서 출력 API로 Elasticsearch를 지원하기 시작하면서 많은 사용자들이 Elasticsearch의 입력 수단으로 Logstash를 사용하며 ELK로 통합되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Logstash는 Server-side 데이터 처리 파이프라인으로 다양한 소스에서 동시에 데이터를 수집하고 통합한다.&lt;/p&gt;
&lt;p&gt;또한 수집된 데이터를 정규화하여 Elasticsearch 등의 목적지로 전송하는 역할을 한다.&lt;/p&gt;
&lt;p&gt;거의 대부분의 이벤트를 수집하여 변호나할 수 있으며 기본으로 제공되는 여러 코덱을 이용하여 수집(Ingestion) 프로세스를 한층 더 간소화할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Kibana&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Kibana&lt;/b&gt;는 Elasticsearch에 색인된 데이터를 검색하고 시각화하는 오픈소스 프론트엔드 서비스이다.&lt;/p&gt;
&lt;p&gt;Elasticsearch로부터 document, aggregation 집계 결과 등을 불러와 웹으로 시각화 한다.&lt;/p&gt;
&lt;p&gt;기본적으로 Discover, Visualize, Dashboard 메뉴와 다양한 App으로 구성되어 있으며, 플러그인을 통해 확장 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Discover&lt;/b&gt;는 Elasticsearch에 색인된 소스 데이터들의 검색을 위한 메뉴이다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Dashboard&lt;/b&gt;는 Visualize 메뉴에서 만들어진 시각화 도구(수집된 차트, 그래프, 메트릭, 검색 및 지도 등)들을 조합하여 단일 페이지에 모아 놓고 다양한 관점에서 데이터에 요약된 인사이트를 제공한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;ELK Stack 구성&lt;/h3&gt;
&lt;p&gt;기존에는 세 가지 오픈소스 제품을 통합하여 ELK라고 하였으나, 2015년 Beats가 도입되며 ELK Stack 또는 Elasic Stack이라는 서비스로 부르기 시작하였다.&lt;/p&gt;
&lt;p&gt;Beats란 서버에 에이전트로 설치하여 다양한 유형의 데이터를 Elasticsearch 또는 Logstash로 전송하는 오픈소스이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;503&quot; width=&quot;550&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deo7bt/btqWXaFV3Il/YMQ3LS0ZH9KMVqv4k4gEAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deo7bt/btqWXaFV3Il/YMQ3LS0ZH9KMVqv4k4gEAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deo7bt/btqWXaFV3Il/YMQ3LS0ZH9KMVqv4k4gEAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdeo7bt%2FbtqWXaFV3Il%2FYMQ3LS0ZH9KMVqv4k4gEAk%2Fimg.png&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;503&quot; width=&quot;550&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;경량 에이전트 Beats가 서버에 설치되어 데이터를 Logstash 또는 Elasticsearch로 전송한다.&lt;/p&gt;
&lt;p&gt;Logstash는 다양한 소스에서 데이터를 수집하고 가공, 변환하여 Elasticsearch에 인덱싱하여 전송한다.&lt;/p&gt;
&lt;p&gt;Elasticsearch는 수신된 데이터를 저장소에 저장하고, 탐색하는 역할을 수행한다.&lt;/p&gt;
&lt;p&gt;마지막으로 사용자는 Kibana를 통해 Elasicsearch에 저장된 데이터를 시각화하여 실시간 분석 등을 할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로 Beats 제품군은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;352&quot; width=&quot;599&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Enols/btqW4iwnaeC/PcAEWsKK9NU0Cp2IAJyYU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Enols/btqW4iwnaeC/PcAEWsKK9NU0Cp2IAJyYU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Enols/btqW4iwnaeC/PcAEWsKK9NU0Cp2IAJyYU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEnols%2FbtqW4iwnaeC%2FPcAEWsKK9NU0Cp2IAJyYU0%2Fimg.png&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;352&quot; width=&quot;599&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/index.html&quot;&gt;https://www.elastic.co/guide/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://esbook.kimjmin.net/&quot;&gt;https://esbook.kimjmin.net/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>IT/Etc</category>
      <category>beats</category>
      <category>elasticsearch</category>
      <category>elk stack</category>
      <category>kibana</category>
      <category>logstash</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/38</guid>
      <comments>https://suyeon96.tistory.com/38#entry38comment</comments>
      <pubDate>Sat, 13 Feb 2021 02:24:20 +0900</pubDate>
    </item>
    <item>
      <title>[DevOps] Jenkins Scaling (master + slave 구성)</title>
      <link>https://suyeon96.tistory.com/37</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://suyeon96.tistory.com/36&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 글&lt;/a&gt;에서 master 노드에 Jenkins를 설치하고 정적 web site와 docker 이미지를 각각 AWS S3와 AWS ECR에 배포하고 배포된 이미지를 AWS ECS에 올리는 작업을 하였다.&lt;/p&gt;
&lt;p&gt;하지만 작업이 많아지고 무거워지는 경우 서버 한대에서 이를 처리하기에 버거워질 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;오늘은 slave 서버를 추가하여 master 노드가 slave 노드와 작업을 분산할 수 있도록 구성을 해보자.&lt;/p&gt;
&lt;p&gt;slave 노드에는 별도로 Jenkins를 설치하지 않고 ssh 통신으로 master 노드가 slave 노드에 작업을 지시하도록 할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1. AWS EC2 slave node 생성&lt;/h3&gt;
&lt;p&gt;우선 master와 동일하게 AWS EC2로 Slave 서버를 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;0.png&quot; data-origin-width=&quot;1341&quot; data-origin-height=&quot;257&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n8EWb/btqVkLz6lwj/rsIpsVkpQ3QdFWQestje61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n8EWb/btqVkLz6lwj/rsIpsVkpQ3QdFWQestje61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n8EWb/btqVkLz6lwj/rsIpsVkpQ3QdFWQestje61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn8EWb%2FbtqVkLz6lwj%2FrsIpsVkpQ3QdFWQestje61%2Fimg.png&quot; data-filename=&quot;0.png&quot; data-origin-width=&quot;1341&quot; data-origin-height=&quot;257&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2. slave 환경설정&lt;/h3&gt;
&lt;p&gt;master 서버와 동일하게 java, git, docker를 설치한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ sudo yum install -y java-1.8.0-openjdk git docker&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3. ssh connection&lt;/h3&gt;
&lt;p&gt;master에 ssh-keygen을 생성한 후&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ ssh-keygen&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;838&quot; width=&quot;601&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MHBJC/btqVe65SgHm/eJsKnWHCkIlnB52X7krfO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MHBJC/btqVe65SgHm/eJsKnWHCkIlnB52X7krfO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MHBJC/btqVe65SgHm/eJsKnWHCkIlnB52X7krfO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMHBJC%2FbtqVe65SgHm%2FeJsKnWHCkIlnB52X7krfO0%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;838&quot; width=&quot;601&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;slave의 authorized_keys에 master의 public key를 추가한다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ vi ~/.ssh/authorized_keys&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;master &amp;rarr; slave ssh 연결 확인!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;838&quot; width=&quot;601&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qPPCe/btqVctgtw5x/JYj5NPat8rCNJADrGgg7YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qPPCe/btqVctgtw5x/JYj5NPat8rCNJADrGgg7YK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qPPCe/btqVctgtw5x/JYj5NPat8rCNJADrGgg7YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqPPCe%2FbtqVctgtw5x%2FJYj5NPat8rCNJADrGgg7YK%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;838&quot; width=&quot;601&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4. Credential 추가&lt;/h3&gt;
&lt;p&gt;Jenkins에서 slave와의 ssh 연결을 위해 Credential을 추가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;886&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGkDbR/btqVbWDaXcw/mK3iboEf3LR86Tsg9XRvy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGkDbR/btqVbWDaXcw/mK3iboEf3LR86Tsg9XRvy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGkDbR/btqVbWDaXcw/mK3iboEf3LR86Tsg9XRvy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGkDbR%2FbtqVbWDaXcw%2FmK3iboEf3LR86Tsg9XRvy1%2Fimg.png&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;886&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Private Key 항목에는 master node의 private key를 입력한다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ cat ~/.ssh/id_rsa&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;922&quot; width=&quot;601&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UufOy/btqVh7cnu0u/lHic25quSO3IDTsKzW0Wek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UufOy/btqVh7cnu0u/lHic25quSO3IDTsKzW0Wek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UufOy/btqVh7cnu0u/lHic25quSO3IDTsKzW0Wek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUufOy%2FbtqVh7cnu0u%2FlHic25quSO3IDTsKzW0Wek%2Fimg.png&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;922&quot; width=&quot;601&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;생성된 Credentials 목록이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;302&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btrSVY/btqVbXISbXp/wWG5AEuNKpkvqQK0gms0Pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btrSVY/btqVbXISbXp/wWG5AEuNKpkvqQK0gms0Pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btrSVY/btqVbXISbXp/wWG5AEuNKpkvqQK0gms0Pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtrSVY%2FbtqVbXISbXp%2FwWG5AEuNKpkvqQK0gms0Pk%2Fimg.png&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;302&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;5. Node 추가&lt;/h3&gt;
&lt;p&gt;slave node를 추가할 차례이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1588&quot; data-origin-height=&quot;1264&quot; data-filename=&quot;3.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLyehJ/btqVnYzaU6g/lhU6XT8Ob69FAwYlvL1Ny1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLyehJ/btqVnYzaU6g/lhU6XT8Ob69FAwYlvL1Ny1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLyehJ/btqVnYzaU6g/lhU6XT8Ob69FAwYlvL1Ny1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLyehJ%2FbtqVnYzaU6g%2FlhU6XT8Ob69FAwYlvL1Ny1%2Fimg.png&quot; data-origin-width=&quot;1588&quot; data-origin-height=&quot;1264&quot; data-filename=&quot;3.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;테스트 용이므로 slave node에 일을 시켰을 때 동작되는 것을 확인하고자 Usage 항목에서 'Only build jobs with label expressions matching this node'를 선택한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;slave node가 정상적으로 추가되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;335&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC1LUL/btqVbvePs5x/K8kdQBNvkJUAa0qt5Za081/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC1LUL/btqVbvePs5x/K8kdQBNvkJUAa0qt5Za081/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC1LUL/btqVbvePs5x/K8kdQBNvkJUAa0qt5Za081/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC1LUL%2FbtqVbvePs5x%2FK8kdQBNvkJUAa0qt5Za081%2Fimg.png&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;335&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;node 목록에서 master와 slave를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;342&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xF375/btqVddLdpuX/thbGsD4cD4YfarhnQRNaok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xF375/btqVddLdpuX/thbGsD4cD4YfarhnQRNaok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xF375/btqVddLdpuX/thbGsD4cD4YfarhnQRNaok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxF375%2FbtqVddLdpuX%2FthbGsD4cD4YfarhnQRNaok%2Fimg.png&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;342&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;6. Jenkinsfile 수정&lt;/h3&gt;
&lt;p&gt;default로 master node에서 빌드될 수 있도록 상단에서 &lt;code&gt;agent&lt;/code&gt; label을 'master'로 설정하고, 'Deploy Server' Stage는 slave node에서 빌드될 수 있도록 설정하였다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;def DEPLOY_TO

pipeline {
  //agent any
  agent {
    label 'master'
  }

  parameters {
    ...
  }

  stages {
    stage('Decide Deploy To') {
      ...
    }

    stage('Check deploy parameter') {
      ...
    }

    stage('Build website') {
      ...
    }

    stage('Deploy website') {
      ...
    }

    stage('Build Server') {
      ...
    }

    stage('Deploy Server') {
      agent {
        label 'slave'
      }
      steps {
        script {
          withAWS(region:'ap-northeast-2', credentials:'jenkinsaws') {
            sh &quot;&quot;&quot;
              aws ecs update-service \
                --region ap-northeast-2 \
                --cluster ${DEPLOY_TO}-app \
                --service ${DEPLOY_TO}-webserver \
                --force-new-deployment \
                --desired-count 2
              aws ecs wait services-stable \
                --cluster ${DEPLOY_TO}-app \
                --services ${DEPLOY_TO}-webserver
            &quot;&quot;&quot;
          }
        }
      }
    }

  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7. 배포 테스트&lt;/h3&gt;
&lt;p&gt;master 노드와 slave 노드에서 동시에 빌드되는 중이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;333&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJKkIT/btqVe83Ix2M/xpBQfrHWAzmnUx7aFgAkG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJKkIT/btqVe83Ix2M/xpBQfrHWAzmnUx7aFgAkG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJKkIT/btqVe83Ix2M/xpBQfrHWAzmnUx7aFgAkG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJKkIT%2FbtqVe83Ix2M%2FxpBQfrHWAzmnUx7aFgAkG1%2Fimg.png&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;333&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정상적으로 빌드도 완료되었으며,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;411&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/22Xtc/btqVdNlaebD/knbwxaLUQEukbowocSx15k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/22Xtc/btqVdNlaebD/knbwxaLUQEukbowocSx15k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/22Xtc/btqVdNlaebD/knbwxaLUQEukbowocSx15k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F22Xtc%2FbtqVdNlaebD%2FknbwxaLUQEukbowocSx15k%2Fimg.png&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;411&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;log를 봐도 'Deploy Server' Stage가 slvae 노드에서 빌드되었음을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;193&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ymRuF/btqVddxDS8l/k4mNxEGPdFqsYBN7xYbxbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ymRuF/btqVddxDS8l/k4mNxEGPdFqsYBN7xYbxbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ymRuF/btqVddxDS8l/k4mNxEGPdFqsYBN7xYbxbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FymRuF%2FbtqVddxDS8l%2Fk4mNxEGPdFqsYBN7xYbxbK%2Fimg.png&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;193&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;ps. 이렇게 활용할 수도...&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;stage('Some Parallel Stage') {
  parallel {
    stage('frontend test') {
      agent { label 'slave1' }
      ...
    }
    stage('backend test') {
      agent { label 'slave2' }
      ...
    }
    stage('master job') {
      agent { label 'master' }
      ...
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 Stage 처럼 Parallel 하게 3개의 Stage를 각각 다른 node에서 돌아가도록 설정할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Etc</category>
      <category>jenkins</category>
      <category>master</category>
      <category>Scaling</category>
      <category>slave</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/37</guid>
      <comments>https://suyeon96.tistory.com/37#entry37comment</comments>
      <pubDate>Sun, 31 Jan 2021 23:39:06 +0900</pubDate>
    </item>
    <item>
      <title>[DevOps] Jenkins 설치 및 Static web page, Docker 배포 (AWS S3, ECR, ECS)</title>
      <link>https://suyeon96.tistory.com/36</link>
      <description>&lt;h2&gt;Jenkins&lt;/h2&gt;
&lt;p&gt;&lt;b&gt;Jenkins&lt;/b&gt;란 오픈소스 자동화 서버로 구축, 테스트, 배포와 관련된 소프트웨어 개발 프로세스를 자동화하고 CI/CD 제공하는 툴이다.&lt;/p&gt;
&lt;p&gt;Java Runtime 위에서 작동되며 다양한 플러그인을 지원하여 인프라 운영 상황에 맞게 입맛대로 파이프라인을 구성할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Jenkins Pipeline&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Jenkins Pipeline&lt;/b&gt;이란 CI/CD를 Jenkins에 구현하고 통합하기 위한 플러그인의 모음이다.&lt;/p&gt;
&lt;p&gt;코드가 커밋된 후부터 실환경에 배포되기까지 릴리즈 과정은 꽤나 복잡하다.&lt;/p&gt;
&lt;p&gt;코드를 빌드하고 여러 단계의 테스트를 거쳐 각각의 환경에 배포해야 한다.&lt;/p&gt;
&lt;p&gt;우리는 Pipeline을 미리 정의하여, 이러한 프로세스를 자동화할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Jenkins Pipeline은 Pipeline DSL 구문을 통해 코드 형태로 복잡한 파이프라인을 모델링할 수 있으며, &lt;code&gt;Jenkinsfile&lt;/code&gt;이라는 text 파일로 정의하여 프로젝트에 포함시켜 활용할 수 있다.&lt;/p&gt;
&lt;p&gt;Jenkinsfile로 Pipeline을 관리하는 경우 프로젝트의 여러 구성원이 쉽게 공유하고 리뷰/수정할 수 있으며, Pipeline audit에 대한 추적이 가능하다.&lt;/p&gt;
&lt;p&gt;또한 Pipeline 빌드 프로세스를 자동으로 생성해주며, Jenkins 노드가 재시작되어도 안전하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Pipeline의 2가지 주요 개념에 대해 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Stage&lt;/code&gt; : 전체 파이프라인에서 수행되는 작업을 개념적 구분해놓은 단계이다. (스텝들의 모음)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Step&lt;/code&gt; : 젠킨스가 특정 시점에서 수행해야 하는 단순한 하나의 task를 의미한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 Jenkins Pipeline 시나리오 예시이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;realworld-pipeline-flow.png&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;499&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baQ5rc/btqVh7iSKQa/MfaCRV0kJzR3CR4Us1ohQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baQ5rc/btqVh7iSKQa/MfaCRV0kJzR3CR4Us1ohQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baQ5rc/btqVh7iSKQa/MfaCRV0kJzR3CR4Us1ohQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaQ5rc%2FbtqVh7iSKQa%2FMfaCRV0kJzR3CR4Us1ohQK%2Fimg.png&quot; data-filename=&quot;realworld-pipeline-flow.png&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;499&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;오늘은 Jenkins를 통해 AWS S3 배포와 Docker 이미지/컨테이너 배포를 해 볼 계획이다.&lt;/p&gt;
&lt;p&gt;또한 Git과 연동하여 master 브랜치는 Prod 환경에, develop 브랜치는 Dev 환경에 배포되도록 Pipeline을 구성할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. 환경 설정&lt;/h2&gt;
&lt;h3&gt;1.1. Jenkins Node 준비&lt;/h3&gt;
&lt;p&gt;AWS EC2를 하나 준비하였다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AMI : Amazon Linux 2 AMI&lt;/li&gt;
&lt;li&gt;인스턴스 유형 : t2.small (1Core/2GB)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;474&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sbwWo/btqVdfaW9oS/X44le2YCgfIbhZwJRkMTfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sbwWo/btqVdfaW9oS/X44le2YCgfIbhZwJRkMTfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sbwWo/btqVdfaW9oS/X44le2YCgfIbhZwJRkMTfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsbwWo%2FbtqVdfaW9oS%2FX44le2YCgfIbhZwJRkMTfk%2Fimg.png&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;474&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.2. Jenkins 설치&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;$ sudo yum update -y

# Jenkins 패키지 추가
$ sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins.io/redhat/jenkins.repo &amp;amp;&amp;amp;
$ sudo rpm --import https://pkg.jenkins.io/redhat/jenkins.io.key

# Install java, docker, git
$ sudo yum install -y java-1.8.0-openjdk jenkins git docker

# 자바 버전 8 로 설정
$ sudo alternatives --config java
$ sudo service jenkins start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.3. docker 그룹에 Jenkins 추가&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# docker 그룹에 jenkins 추가
$ sudo usermod -aG docker jenkins
$ sudo systemctl start docker
$ sudo service docker start
$ sudo chmod 666 /var/run/docker.sock
$ sudo -su jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.4. Jenkins Dashboard 접속&lt;/h3&gt;
&lt;p&gt;Jenkins Dashboard에 접속하기 위해서는 우선 기본 port인 8080을 열어줘야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2021-01-28__1.52.01.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgH6ii/btqVbXu3Tmy/lsc6DYHFb3hDAuumQfstbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgH6ii/btqVbXu3Tmy/lsc6DYHFb3hDAuumQfstbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgH6ii/btqVbXu3Tmy/lsc6DYHFb3hDAuumQfstbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgH6ii%2FbtqVbXu3Tmy%2Flsc6DYHFb3hDAuumQfstbK%2Fimg.png&quot; data-filename=&quot;_2021-01-28__1.52.01.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;최초 접속 시 Password를 입력해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1208&quot; data-filename=&quot;2.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uoOx3/btqVe8PTbAF/2T6tVW5tlgvoVxEANhKgyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uoOx3/btqVe8PTbAF/2T6tVW5tlgvoVxEANhKgyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uoOx3/btqVe8PTbAF/2T6tVW5tlgvoVxEANhKgyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuoOx3%2FbtqVe8PTbAF%2F2T6tVW5tlgvoVxEANhKgyK%2Fimg.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1208&quot; data-filename=&quot;2.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;초기 Password는 화면에 적혀있듯이 아래 경로에서 확인 가능하다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;# 초기 비밀번호 확인
sudo cat /var/lib/jenkins/secrets/initialAdminPassword&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;98&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGO5kI/btqVcrWWz3W/2n0HIRmokAcJwIjnT5Vfdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGO5kI/btqVcrWWz3W/2n0HIRmokAcJwIjnT5Vfdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGO5kI/btqVcrWWz3W/2n0HIRmokAcJwIjnT5Vfdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGO5kI%2FbtqVcrWWz3W%2F2n0HIRmokAcJwIjnT5Vfdk%2Fimg.png&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;98&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.5. 시작&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1208&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mnj3m/btqU7c7Acdm/WtsVoWp0pefWtOCeNKgKT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mnj3m/btqU7c7Acdm/WtsVoWp0pefWtOCeNKgKT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mnj3m/btqU7c7Acdm/WtsVoWp0pefWtOCeNKgKT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMnj3m%2FbtqU7c7Acdm%2FWtsVoWp0pefWtOCeNKgKT0%2Fimg.png&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1208&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.6. 추가 Pulg-in 설치&lt;/h3&gt;
&lt;p&gt;Pipeline에서 사용하기 위한 plugin을 추가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;700&quot; data-filename=&quot;6.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J4dxM/btqU92Q9380/mhsUly74PytVme8xYVZ4M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J4dxM/btqU92Q9380/mhsUly74PytVme8xYVZ4M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J4dxM/btqU92Q9380/mhsUly74PytVme8xYVZ4M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ4dxM%2FbtqU92Q9380%2FmhsUly74PytVme8xYVZ4M0%2Fimg.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;700&quot; data-filename=&quot;6.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.7. Git Credentials 추가&lt;/h3&gt;
&lt;p&gt;Jenkins와 Git의 연결을 위해 Credentials을 추가해줘야 한다.&lt;/p&gt;
&lt;p&gt;우선 Git Access Token을 발급받는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;712&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZX86n/btqU94BtHFb/aBE6qhJ1nAhePtyq5I37v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZX86n/btqU94BtHFb/aBE6qhJ1nAhePtyq5I37v1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZX86n/btqU94BtHFb/aBE6qhJ1nAhePtyq5I37v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZX86n%2FbtqU94BtHFb%2FaBE6qhJ1nAhePtyq5I37v1%2Fimg.png&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;712&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Jenkins관리 &amp;gt; Manage Credentials 메뉴에서 Credentials를 추가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1208&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sXGio/btqVde38YUT/gYL5UypRY4RDbAyJnf6mxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sXGio/btqVde38YUT/gYL5UypRY4RDbAyJnf6mxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sXGio/btqVde38YUT/gYL5UypRY4RDbAyJnf6mxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsXGio%2FbtqVde38YUT%2FgYL5UypRY4RDbAyJnf6mxk%2Fimg.png&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1208&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.8. AWS Credentials 추가&lt;/h3&gt;
&lt;p&gt;동일하게 AWS와의 연결을 위해 우선 AWS에서 jenkins 사용자를 만들고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1397&quot; data-origin-height=&quot;557&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nnEvH/btqVh6ROxOD/f8HkUtypEkQDyHa5FcyRqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nnEvH/btqVh6ROxOD/f8HkUtypEkQDyHa5FcyRqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nnEvH/btqVh6ROxOD/f8HkUtypEkQDyHa5FcyRqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnnEvH%2FbtqVh6ROxOD%2Ff8HkUtypEkQDyHa5FcyRqk%2Fimg.png&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1397&quot; data-origin-height=&quot;557&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Jenkins Credentials를 추가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1208&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N2fw5/btqVbu7JNvB/raDeroVwKmcjTLhBcREkYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N2fw5/btqVbu7JNvB/raDeroVwKmcjTLhBcREkYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N2fw5/btqVbu7JNvB/raDeroVwKmcjTLhBcREkYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN2fw5%2FbtqVbu7JNvB%2FraDeroVwKmcjTLhBcREkYK%2Fimg.png&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1208&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;방금 만든 2개의 Credentials이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;484&quot; data-filename=&quot;11.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p5bmn/btqU94BtHRz/mmKZRIUhgIKPxusvo6nKk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p5bmn/btqU94BtHRz/mmKZRIUhgIKPxusvo6nKk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p5bmn/btqU94BtHRz/mmKZRIUhgIKPxusvo6nKk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp5bmn%2FbtqU94BtHRz%2FmmKZRIUhgIKPxusvo6nKk0%2Fimg.png&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;484&quot; data-filename=&quot;11.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.9. Pipeline 생성&lt;/h3&gt;
&lt;p&gt;git을 붙이기 위해 &lt;code&gt;Multibranch Pipeline&lt;/code&gt; item을 만들었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이름과 설명을 간단하게 입력하고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;810&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvuiK3/btqVh7b7Wjv/aI6QjkUMKKZoUEOFlLlh6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvuiK3/btqVh7b7Wjv/aI6QjkUMKKZoUEOFlLlh6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvuiK3/btqVh7b7Wjv/aI6QjkUMKKZoUEOFlLlh6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvuiK3%2FbtqVh7b7Wjv%2FaI6QjkUMKKZoUEOFlLlh6K%2Fimg.png&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;810&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Git Project Repository를 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;1020&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BibrI/btqVcrbCPWy/HuffcGggwYNUUKmToOWJEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BibrI/btqVcrbCPWy/HuffcGggwYNUUKmToOWJEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BibrI/btqVcrbCPWy/HuffcGggwYNUUKmToOWJEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBibrI%2FbtqVcrbCPWy%2FHuffcGggwYNUUKmToOWJEK%2Fimg.png&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;1020&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;마지막으로 Jenkinsfile의 위치를 설정하고, Trigger 배치 간격을 정해준다.&lt;br /&gt;(test를 원활하게 진행하기 위해 1분으로 설정하였다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bABFNS/btqVdL8tCpu/YslsjjbEqM5w0qRq87Cz51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bABFNS/btqVdL8tCpu/YslsjjbEqM5w0qRq87Cz51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bABFNS/btqVdL8tCpu/YslsjjbEqM5w0qRq87Cz51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbABFNS%2FbtqVdL8tCpu%2FYslsjjbEqM5w0qRq87Cz51%2Fimg.png&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. Parameter 관리 (AWS Parameter Store)&lt;/h2&gt;
&lt;p&gt;본격적으로 배포를 하기 전에 Parameter를 받아오는 방법에 대해 알아보고 가자.&lt;/p&gt;
&lt;p&gt;인프라 운영에서 Parameter 관리는 매우 중요하다.&lt;/p&gt;
&lt;p&gt;자체적으로 Parameter 서버를 따로 운영하기도 하고, 심지어는 DB나 File로 관리하기도 한다.&lt;/p&gt;
&lt;p&gt;이번에는 AWS Parameter Store에 파라미터를 설정하고 Jenkinsfile에서 이를 받아오도록 구현해보려 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.1 AWS parameter store에 환경 별 파라미터 저장&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;924&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQaEt8/btqVe7DrMLT/L3EUk9cLiLGAgHhyQvMRwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQaEt8/btqVe7DrMLT/L3EUk9cLiLGAgHhyQvMRwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQaEt8/btqVe7DrMLT/L3EUk9cLiLGAgHhyQvMRwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQaEt8%2FbtqVe7DrMLT%2FL3EUk9cLiLGAgHhyQvMRwk%2Fimg.png&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;924&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 jq install&lt;/h3&gt;
&lt;p&gt;Jenkins node에서 jq 명령을 수행하기 위해 yum 설치&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo yum jq
$ sudo yum install jq&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.3 Jenkinsfile 설정&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;def DEPLOY_TO

pipeline {
  agent any

  parameters {
    string(name:'MYSQL_HOST', defaultValue: 'devMysql', description: 'dev mysql host')
    string(name:'MYSQL_PASSWORD', defaultValue: 'new1234!', description: 'dev mysql password')
  }

  stages {
    // branch명을 기준으로 배포할 환경 설정
    stage('Decide Deploy To') {
      steps {
        script {
          if (env.BRANCH_NAME == 'master'){
            DEPLOY_TO = 'prod'
          } else if (env.BRANCH_NAME == 'develop'){
            DEPLOY_TO = 'dev'
          } else if (env.BRANCH_NAME == 'qa'){
            DEPLOY_TO = 'qa'
          }
        }
        echo &quot;DEPLOY_TO: ${DEPLOY_TO}&quot;
      }
    }

    // aws Parameter Store의 파라미터 정보 가져오기
    stage('Check deploy parameter') {
      steps {
        script {
          withAWS(region:'ap-northeast-2', credentials:'jenkinsaws') {
            def mysql_host = sh(script: &quot;aws ssm get-parameters --name /${DEPLOY_TO}/MYSQL_HOST | jq '.Parameters[0].Value'&quot;, returnStdout: true).trim()
            def mysql_password = sh(script: &quot;aws ssm get-parameters --name /${DEPLOY_TO}/MYSQL_PASSWORD | jq '.Parameters[0].Value'&quot;, returnStdout: true).trim()
            echo &quot;${mysql_host}&quot;
          }
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.4 log 확인&lt;/h3&gt;
&lt;p&gt;master 브랜치에 push 후 log이다.&lt;/p&gt;
&lt;p&gt;prod 환경도 정상적으로 설정되며, aws Parameter Store의 &lt;code&gt;/prod/MYSQL_HOST&lt;/code&gt; 파라미터도 잘 가져온 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;707&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IEXrA/btqVkLT9sLj/tDxAGOKrFGolzE6LgLAzA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IEXrA/btqVkLT9sLj/tDxAGOKrFGolzE6LgLAzA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IEXrA/btqVkLT9sLj/tDxAGOKrFGolzE6LgLAzA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIEXrA%2FbtqVkLT9sLj%2FtDxAGOKrFGolzE6LgLAzA1%2Fimg.png&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;707&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. Front-end 배포 (AWS S3)&lt;/h2&gt;
&lt;h3&gt;3.1. Jenkinsfile 설정&lt;/h3&gt;
&lt;p&gt;front-end를 배포하기 위한 'Deploy website' stage 추가&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;stage('Deploy website') {
  steps{
    dir ('./website') {
      script {
        withAWS(region:'ap-northeast-2', credentials:'jenkinsaws') {
          sh &quot;&quot;&quot;
          aws s3 sync ./ s3://${DEPLOY_TO}-suyeon-website
          &quot;&quot;&quot;
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.2 AWS S3 버킷 생성&lt;/h3&gt;
&lt;p&gt;정적 page를 배포하기 위해 환경별로 AWS S3 버킷을 생성하고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;320&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEA20v/btqVdeXmipN/ezrIVdOAIb4nrxDJSo3uM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEA20v/btqVdeXmipN/ezrIVdOAIb4nrxDJSo3uM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEA20v/btqVdeXmipN/ezrIVdOAIb4nrxDJSo3uM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEA20v%2FbtqVdeXmipN%2FezrIVdOAIb4nrxDJSo3uM1%2Fimg.png&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;320&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.3 코드 준비&lt;/h3&gt;
&lt;p&gt;간단하게 배포할 코드를 준비하였다.&lt;/p&gt;
&lt;p&gt;./website/index.html&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;

&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.4 Prod 배포&lt;/h3&gt;
&lt;p&gt;Prod 환경에 배포하기 위해 &lt;code&gt;master&lt;/code&gt; 브랜치에 새로 추가한 index.html을 push 하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Jenkins Dashboard에서 정상적으로 Pipeline이 동작한 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;550&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boD4uA/btqVcrvRnkk/o8AMFQbxxk6GYB9cDn82h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boD4uA/btqVcrvRnkk/o8AMFQbxxk6GYB9cDn82h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boD4uA/btqVcrvRnkk/o8AMFQbxxk6GYB9cDn82h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboD4uA%2FbtqVcrvRnkk%2Fo8AMFQbxxk6GYB9cDn82h1%2Fimg.png&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;550&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;'Deploy website' Stage의 Log를 확인해보니 prod용으로 만들어 놓은 s3 버킷에 index.html이 잘 배포되었다고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;354&quot; data-filename=&quot;19.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRGr7c/btqVe9ahuDD/hkadjFpmabq7vBo8mZSouK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRGr7c/btqVe9ahuDD/hkadjFpmabq7vBo8mZSouK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRGr7c/btqVe9ahuDD/hkadjFpmabq7vBo8mZSouK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRGr7c%2FbtqVe9ahuDD%2FhkadjFpmabq7vBo8mZSouK%2Fimg.png&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;354&quot; data-filename=&quot;19.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;확인사살 1&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;20.png&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;928&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu3MAS/btqVdepz5h6/6ZkzbdlRDjleGSwxKjUmB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu3MAS/btqVdepz5h6/6ZkzbdlRDjleGSwxKjUmB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu3MAS/btqVdepz5h6/6ZkzbdlRDjleGSwxKjUmB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu3MAS%2FbtqVdepz5h6%2F6ZkzbdlRDjleGSwxKjUmB1%2Fimg.png&quot; data-filename=&quot;20.png&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;928&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;확인사살 2&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;979&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK4SBV/btqVkKOt5kL/3JfhqKrYWBMPuHD1yP26hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK4SBV/btqVkKOt5kL/3JfhqKrYWBMPuHD1yP26hK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK4SBV/btqVkKOt5kL/3JfhqKrYWBMPuHD1yP26hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK4SBV%2FbtqVkKOt5kL%2F3JfhqKrYWBMPuHD1yP26hK%2Fimg.png&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;979&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.5 Dev 배포&lt;/h3&gt;
&lt;p&gt;Dev 환경에도 배포해보자.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;develop&lt;/code&gt; branch에 push를 하자 Trigger에 의해 develop Pipeline이 동작한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;880&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DdEg0/btqU7dMcrN0/QWJExRcAodfy838p6jpVkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DdEg0/btqU7dMcrN0/QWJExRcAodfy838p6jpVkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DdEg0/btqU7dMcrN0/QWJExRcAodfy838p6jpVkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDdEg0%2FbtqU7dMcrN0%2FQWJExRcAodfy838p6jpVkK%2Fimg.png&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;880&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;dev 환경에도 정상적으로 배포가 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;23.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;874&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLe6Ik/btqU94BtMDy/GIjlZcGelzh4Rzk3HkXM5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLe6Ik/btqU94BtMDy/GIjlZcGelzh4Rzk3HkXM5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLe6Ik/btqU94BtMDy/GIjlZcGelzh4Rzk3HkXM5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLe6Ik%2FbtqU94BtMDy%2FGIjlZcGelzh4Rzk3HkXM5k%2Fimg.png&quot; data-filename=&quot;23.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;874&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;4. Docker Image 배포 (AWS ECR)&lt;/h2&gt;
&lt;p&gt;이번에는 docker를 빌드하여 이미지 생성 후 이미지를 AWS ECR(Elastic Container Registry)에 배포해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.1 Jenkins node docker 설치 확인&lt;/h3&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ docker ps&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.2 Jenkinsfile 설정&lt;/h3&gt;
&lt;p&gt;docker 이미지를 배포하기 위한 'Build Server' stage 추가&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;stage('Build Server') {
  steps {
    dir('./server') {
      script {
        withAWS(region:'ap-northeast-2', credentials:'jenkinsaws') {
          def login = ecrLogin()
          def mysql_host = sh(script: &quot;aws ssm get-parameters --name /${DEPLOY_TO}/MYSQL_HOST | jq '.Parameters[0].Value'&quot;, returnStdout: true).trim()
          def mysql_password = sh(script: &quot;aws ssm get-parameters --name /${DEPLOY_TO}/MYSQL_PASSWORD | jq '.Parameters[0].Value'&quot;, returnStdout: true).trim()
          echo &quot;${login}&quot;
          // 실제 로그인
          sh &quot;${login}&quot;
          sh &quot;&quot;&quot;
          docker build --build-arg env=${DEPLOY_TO} --build-arg mysqlHost=${mysql_host} --build-arg mysqlPassword=${mysql_password} -t 000000000000.dkr.ecr.ap-northeast-2.amazonaws.com/${DEPLOY_TO}-webserver .
          docker push 000000000000.dkr.ecr.ap-northeast-2.amazonaws.com/${DEPLOY_TO}-webserver
          &quot;&quot;&quot;
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;000000000000 대신 AWS 계정 ID를 넣어줘야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.3 AWS ECR Repositories 생성&lt;/h3&gt;
&lt;p&gt;docker 이미지를 배포할 AWS ECR Repository를 환경별로 생성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;24.png&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;397&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ugnJL/btqVe8CroUF/uEu9fX5LqRQjRilR8fB5kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ugnJL/btqVe8CroUF/uEu9fX5LqRQjRilR8fB5kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ugnJL/btqVe8CroUF/uEu9fX5LqRQjRilR8fB5kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FugnJL%2FbtqVe8CroUF%2FuEu9fX5LqRQjRilR8fB5kK%2Fimg.png&quot; data-filename=&quot;24.png&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;397&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.4 Docker 빌드 환경 준비&lt;/h3&gt;
&lt;p&gt;./server/package.json&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;server&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;tsc&quot;,
    &quot;start&quot;: &quot;node ./dist/index.js&quot;,
    &quot;test&quot;: &quot;node_modules/.bin/jest .&quot;,
    &quot;lint&quot;: &quot;npx eslint index.ts&quot;
  },
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;dependencies&quot;: {
    &quot;@types/jest&quot;: &quot;^26.0.20&quot;,
    &quot;express&quot;: &quot;^4.17.1&quot;,
    &quot;typescript&quot;: &quot;^4.1.3&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@typescript-eslint/eslint-plugin&quot;: &quot;^4.14.1&quot;,
    &quot;@typescript-eslint/parser&quot;: &quot;^4.14.1&quot;,
    &quot;eslint&quot;: &quot;^7.18.0&quot;,
    &quot;jest&quot;: &quot;^26.6.3&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;./server/Dockerfile&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;FROM node:11.13.0
LABEL name=&quot;server&quot;

RUN mkdir app

WORKDIR /app
COPY ./package.json /app/package.json
COPY ./package-lock.json /app/package-lock.json

RUN npm install

ARG env
ARG mysqlHost
ARG mysqlPassword
ENV APP_ENV=$env
ENV MYSQL_HOST=$mysqlHost
ENV MYSQL_PASSWORD=$mysqlPassword

COPY . /app

EXPOSE 80

RUN echo 'Build Completed'

# The first parameter of docker CMD is executable and second parameter is file or parameters.
# Docker cmd internally uses sh -C
CMD [&quot;npm&quot;,&quot;start&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.5 Prod 배포&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;25.png&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;864&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CIeRy/btqU7drUqW6/5aWvbLN67AVe65QM2dxWhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CIeRy/btqU7drUqW6/5aWvbLN67AVe65QM2dxWhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CIeRy/btqU7drUqW6/5aWvbLN67AVe65QM2dxWhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCIeRy%2FbtqU7drUqW6%2F5aWvbLN67AVe65QM2dxWhK%2Fimg.png&quot; data-filename=&quot;25.png&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;864&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;26.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;433&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rEn1B/btqU7bU7iTC/warEwOX5UFitKxWuoyncMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rEn1B/btqU7bU7iTC/warEwOX5UFitKxWuoyncMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rEn1B/btqU7bU7iTC/warEwOX5UFitKxWuoyncMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrEn1B%2FbtqU7bU7iTC%2FwarEwOX5UFitKxWuoyncMk%2Fimg.png&quot; data-filename=&quot;26.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;433&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;prod 용 AWS ECR Repository에 이미지가 잘 들어갔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;27.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;625&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6KQfe/btqVbvyMAK6/hn31vguEIpf0I7JnQaSOLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6KQfe/btqVbvyMAK6/hn31vguEIpf0I7JnQaSOLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6KQfe/btqVbvyMAK6/hn31vguEIpf0I7JnQaSOLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6KQfe%2FbtqVbvyMAK6%2Fhn31vguEIpf0I7JnQaSOLK%2Fimg.png&quot; data-filename=&quot;27.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;625&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;5. Docker Container 배포 (AWS ECS)&lt;/h2&gt;
&lt;p&gt;AWS ECR에 push 된 이미지를 AWS ECS(Elastic Container Service)에 배포해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;5.1 Jenkinsfile 설정&lt;/h3&gt;
&lt;p&gt;docker 컨테이너를 배포하기 위한 'Deploy Server' stage 추가&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;stage('Deploy Server') {
  steps {
    script {
      withAWS(region:'ap-northeast-2', credentials:'jenkinsaws') {
        sh &quot;&quot;&quot;
          aws ecs update-service \
            --region ap-northeast-2 \
            --cluster ${DEPLOY_TO}-app \
            --service ${DEPLOY_TO}-webserver \
            --force-new-deployment \
            --desired-count 2
          aws ecs wait services-stable \
            --cluster ${DEPLOY_TO}-app \
            --services ${DEPLOY_TO}-webserverD
        &quot;&quot;&quot;
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;5.2 AWS ECS 클러스터 생성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;28.png&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;649&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb1JGS/btqVbWQshHM/U9MpeVN7AgpNpPImf1NIXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb1JGS/btqVbWQshHM/U9MpeVN7AgpNpPImf1NIXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb1JGS/btqVbWQshHM/U9MpeVN7AgpNpPImf1NIXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb1JGS%2FbtqVbWQshHM%2FU9MpeVN7AgpNpPImf1NIXk%2Fimg.png&quot; data-filename=&quot;28.png&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;649&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;5.3 Prod 배포&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;29.png&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;864&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TFp4W/btqVde387wO/hRNUjsApFmp05doiUljLh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TFp4W/btqVde387wO/hRNUjsApFmp05doiUljLh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TFp4W/btqVde387wO/hRNUjsApFmp05doiUljLh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTFp4W%2FbtqVde387wO%2FhRNUjsApFmp05doiUljLh0%2Fimg.png&quot; data-filename=&quot;29.png&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;864&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;AWS ECS 클러스터 작업 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;30.png&quot; data-origin-width=&quot;1655&quot; data-origin-height=&quot;643&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8OtoI/btqVbXBQhYy/yzUIdrb0oWkGqWQbiECaRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8OtoI/btqVbXBQhYy/yzUIdrb0oWkGqWQbiECaRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8OtoI/btqVbXBQhYy/yzUIdrb0oWkGqWQbiECaRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8OtoI%2FbtqVbXBQhYy%2FyzUIdrb0oWkGqWQbiECaRk%2Fimg.png&quot; data-filename=&quot;30.png&quot; data-origin-width=&quot;1655&quot; data-origin-height=&quot;643&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;31.png&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;860&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdIOBK/btqVdMfhof5/zwBliKLj9MDiXpeDyzeLc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdIOBK/btqVdMfhof5/zwBliKLj9MDiXpeDyzeLc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdIOBK/btqVdMfhof5/zwBliKLj9MDiXpeDyzeLc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdIOBK%2FbtqVdMfhof5%2FzwBliKLj9MDiXpeDyzeLc0%2Fimg.png&quot; data-filename=&quot;31.png&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;860&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jenkins.io/doc/book/pipeline/&quot;&gt;https://www.jenkins.io/doc/book/pipeline/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>IT/Etc</category>
      <category>AWS</category>
      <category>container</category>
      <category>Docker</category>
      <category>EC2</category>
      <category>ECR</category>
      <category>ECS</category>
      <category>jenkins</category>
      <category>jenkins pipeline</category>
      <category>S3</category>
      <category>Static web page</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/36</guid>
      <comments>https://suyeon96.tistory.com/36#entry36comment</comments>
      <pubDate>Sun, 31 Jan 2021 14:13:01 +0900</pubDate>
    </item>
    <item>
      <title>아마존 Fire TV Stick 구매 및 NETFLIX(넷플릭스) 연결</title>
      <link>https://suyeon96.tistory.com/34</link>
      <description>&lt;p&gt;코로나19가 지속되며 집에 머무는 시간이 많아졌고 TV로 편하게 NETFLIX를 보면 어떨까 생각했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이참에 IPTV를 설치할까 고민하다 TV를 많이 보는 편도 아니고, 가끔 NETFILX와 유튜브만 볼 것 같아 보다 가성비 좋은 &lt;b&gt;아마존 Fire Tv Stick&lt;/b&gt;을 구매하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; width=&quot;500&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFkQPk/btqS1VmaoSE/NPM0En8wuNkDsvFWfWT7h0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFkQPk/btqS1VmaoSE/NPM0En8wuNkDsvFWfWT7h0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFkQPk/btqS1VmaoSE/NPM0En8wuNkDsvFWfWT7h0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFkQPk%2FbtqS1VmaoSE%2FNPM0En8wuNkDsvFWfWT7h0%2Fimg.jpg&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; width=&quot;500&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;셋탑박스 중 샤오미 TV Stick도 잠깐 구매 선상에 올랐지만, 아마존을 애정 하는 관계로다가 Fire TV Stick을 선택하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1719&quot; data-origin-height=&quot;968&quot; data-filename=&quot;0.png&quot; width=&quot;599&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TeEnC/btqS90fpdva/fxKpbhB5fzvJL1MiJQ3tjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TeEnC/btqS90fpdva/fxKpbhB5fzvJL1MiJQ3tjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TeEnC/btqS90fpdva/fxKpbhB5fzvJL1MiJQ3tjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTeEnC%2FbtqS90fpdva%2FfxKpbhB5fzvJL1MiJQ3tjK%2Fimg.png&quot; data-origin-width=&quot;1719&quot; data-origin-height=&quot;968&quot; data-filename=&quot;0.png&quot; width=&quot;599&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로 현재 Amazon Fire TV Stick은 3가지 모델로 출시되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;fire-tv-stick-2020-family.jpg&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;217&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csKhdI/btqS6YhQO5K/hsFo0rD0gO5k7lTXc9ncm1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csKhdI/btqS6YhQO5K/hsFo0rD0gO5k7lTXc9ncm1/img.jpg&quot; data-alt=&quot;Left to right: Fire TV Stick Lite, Fire TV Stick, Fire TV Stick 4K&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csKhdI/btqS6YhQO5K/hsFo0rD0gO5k7lTXc9ncm1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsKhdI%2FbtqS6YhQO5K%2FhsFo0rD0gO5k7lTXc9ncm1%2Fimg.jpg&quot; data-filename=&quot;fire-tv-stick-2020-family.jpg&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;217&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Left to right: Fire TV Stick Lite, Fire TV Stick, Fire TV Stick 4K&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하드웨어 성능은 거의 비슷하나 리모컨 기능과 해상도 지원이 조금씩 다르니 자신에게 잘 맞는 제품을 선택하면 된다.&lt;/p&gt;
&lt;p&gt;(참고 : &lt;a href=&quot;https://liliputing.com/2020/09/amazon-fire-tv-stick-stick-lite-and-stick-4k-specs-compared.html?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A+Liliputing+(Liliputing)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Amazon Fire TV Stick, Stick Lite, and Stick 4K specs compared&lt;/a&gt;)&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;&lt;a href=&quot;https://amzn.to/2EvLkqN&quot;&gt;&lt;b&gt;Fire TV Stick Lite&lt;/b&gt;&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href=&quot;https://amzn.to/305TfCR&quot;&gt;&lt;b&gt;Fire TV Stick&lt;/b&gt;&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href=&quot;https://amzn.to/3kNOcim&quot;&gt;&lt;b&gt;Fire TV Stick 4K&lt;/b&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Video&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1080p @ 60 fps HDR10+&lt;/td&gt;
&lt;td&gt;1080p @ 60 fps HDR10+&lt;/td&gt;
&lt;td&gt;4K @ 60 fps HDR10+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CPU&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;MT8695D 1.7 GHz quad-core 32-bit (software limitation)&lt;/td&gt;
&lt;td&gt;MT8695D 1.7 GHz quad-core 32-bit (software limitation)&lt;/td&gt;
&lt;td&gt;MTK8695+MT7668 1.7 GHz quad-core 32-bit (software limitation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GPU&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;IMG GE8300&lt;/td&gt;
&lt;td&gt;IMG GE8300&lt;/td&gt;
&lt;td&gt;IMG GE8300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;RAM&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1GB DDR4&lt;/td&gt;
&lt;td&gt;1GB DDR4&lt;/td&gt;
&lt;td&gt;1.5 GB DDR4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Storage&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;WiFi&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;WiFi 5 2 x 2 MIMO&lt;/td&gt;
&lt;td&gt;WiFi 5 2 x 2 MIMO&lt;/td&gt;
&lt;td&gt;WiFi 5 2 x 2 MIMO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Bluetooth&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;BT 5.0&lt;/td&gt;
&lt;td&gt;BT 5.0&lt;/td&gt;
&lt;td&gt;BT 5.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Ethernet&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;10/100 (with dongle)&lt;/td&gt;
&lt;td&gt;10/100 (with dongle)&lt;/td&gt;
&lt;td&gt;10/100 (with dongle)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;OS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Fire OS 7 (Android 9)&lt;/td&gt;
&lt;td&gt;Fire OS 7 (Android 9)&lt;/td&gt;
&lt;td&gt;Fire OS 6 (Android 7.1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Remote&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Alexa voice remote Lite&lt;/td&gt;
&lt;td&gt;Alexa voice remote&lt;/td&gt;
&lt;td&gt;Alexa voice remote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Price&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://amzn.to/2EvLkqN&quot;&gt;$30&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://amzn.to/305TfCR&quot;&gt;$40&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://amzn.to/3kNOcim&quot;&gt;$50&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;구성품은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; width=&quot;500&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX77V4/btqS1UU4PdF/q22VuKFM19MO4IjbDvRvc0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX77V4/btqS1UU4PdF/q22VuKFM19MO4IjbDvRvc0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX77V4/btqS1UU4PdF/q22VuKFM19MO4IjbDvRvc0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX77V4%2FbtqS1UU4PdF%2Fq22VuKFM19MO4IjbDvRvc0%2Fimg.jpg&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; width=&quot;500&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;설치방법은 매우매우 간단하다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;미국에서 직구해서 110v 어답터가 포함되어 있으니, 이것만 220v 어답터로 변경해서 사용하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;3.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;2268&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWBjZq/btqSZwfZKgI/jMx3lRUGinpKh77erUaot0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWBjZq/btqSZwfZKgI/jMx3lRUGinpKh77erUaot0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWBjZq/btqSZwfZKgI/jMx3lRUGinpKh77erUaot0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWBjZq%2FbtqSZwfZKgI%2FjMx3lRUGinpKh77erUaot0%2Fimg.jpg&quot; data-filename=&quot;3.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;2268&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;TV HDMI 단자에 연결하고&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;2268&quot; data-filename=&quot;4.jpg&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oo7rK/btqTfCZj8sb/hmMO6D0WnNm93oKv7WOtgK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oo7rK/btqTfCZj8sb/hmMO6D0WnNm93oKv7WOtgK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oo7rK/btqTfCZj8sb/hmMO6D0WnNm93oKv7WOtgK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foo7rK%2FbtqTfCZj8sb%2FhmMO6D0WnNm93oKv7WOtgK%2Fimg.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;2268&quot; data-filename=&quot;4.jpg&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Wifi 연결하고 아마존 로그인까지 완료하면 설정은 끝이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;6.jpg&quot; data-origin-width=&quot;2844&quot; data-origin-height=&quot;1600&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLNPRV/btqS4LC6l7F/i3zKJsEHz5Vyvzbd8dDWXk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLNPRV/btqS4LC6l7F/i3zKJsEHz5Vyvzbd8dDWXk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLNPRV/btqS4LC6l7F/i3zKJsEHz5Vyvzbd8dDWXk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLNPRV%2FbtqS4LC6l7F%2Fi3zKJsEHz5Vyvzbd8dDWXk%2Fimg.jpg&quot; data-filename=&quot;6.jpg&quot; data-origin-width=&quot;2844&quot; data-origin-height=&quot;1600&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;NETFLIX, 유튜브, 아마존 FRIME Video 등 다양한 OTT 서비스를 이용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;3300&quot; data-origin-height=&quot;1856&quot; data-filename=&quot;10.jpg&quot; width=&quot;501&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MsjYt/btqSZwGYRAQ/tZM9xRRsYyn2jaKxXIfhS1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MsjYt/btqSZwGYRAQ/tZM9xRRsYyn2jaKxXIfhS1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MsjYt/btqSZwGYRAQ/tZM9xRRsYyn2jaKxXIfhS1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMsjYt%2FbtqSZwGYRAQ%2FtZM9xRRsYyn2jaKxXIfhS1%2Fimg.jpg&quot; data-origin-width=&quot;3300&quot; data-origin-height=&quot;1856&quot; data-filename=&quot;10.jpg&quot; width=&quot;501&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나의 목적은 NETFLIX였으니, NETFLIX 앱을 선택하여 로그인하면 PC 또는 태블릿과 동일하게 이용 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;3370&quot; data-origin-height=&quot;1896&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qkXqE/btqS6YWqwoo/YpHb2BoCkD4xRBvHlpaD4k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qkXqE/btqS6YWqwoo/YpHb2BoCkD4xRBvHlpaD4k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qkXqE/btqS6YWqwoo/YpHb2BoCkD4xRBvHlpaD4k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqkXqE%2FbtqS6YWqwoo%2FYpHb2BoCkD4xRBvHlpaD4k%2Fimg.jpg&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;3370&quot; data-origin-height=&quot;1896&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;끝!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Life/Tech</category>
      <category>Amazon</category>
      <category>Fire TV Stick</category>
      <category>Netflix</category>
      <category>TV set-top box</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/34</guid>
      <comments>https://suyeon96.tistory.com/34#entry34comment</comments>
      <pubDate>Mon, 11 Jan 2021 03:42:18 +0900</pubDate>
    </item>
    <item>
      <title>[VSCode] EditorConfig - 확장자 별 tab size (Indentation) 설정하기</title>
      <link>https://suyeon96.tistory.com/33</link>
      <description>&lt;p&gt;코드의 가독성과 일관성 유지를 위해 표준화된 Code Style과 Rule에 따라 코드를 작성하는 것이 중요하다.&lt;/p&gt;
&lt;p&gt;그중 &lt;b&gt;Indentation Rule (들여쓰기 규칙)&lt;/b&gt;을 &lt;code&gt;Visual Studio Code&lt;/code&gt;에서 File Format 별로 설정하는 방법을 알아보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://google.github.io/styleguide/htmlcssguide.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Google HTML/CSS Style Guide&lt;/a&gt;와 &lt;a href=&quot;https://standardjs.com/rules-en.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JavaScript Standard Style&lt;/a&gt;에 따라 &lt;code&gt;.html&lt;/code&gt;, &lt;code&gt;.css&lt;/code&gt;, &lt;code&gt;.js&lt;/code&gt;의 경우 하나의 &lt;code&gt;tab&lt;/code&gt;을 &lt;code&gt;2 spaces&lt;/code&gt;로 설정하고, 나머지 파일에 대해서는 default인 &lt;code&gt;4 spaces&lt;/code&gt;로 유지한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;369&quot; width=&quot;699&quot; height=&quot;368&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqP1g8/btqSGjlBvcN/zgDrohP9hRFofVP5aadK60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqP1g8/btqSGjlBvcN/zgDrohP9hRFofVP5aadK60/img.png&quot; data-alt=&quot;Google HTML/CSS Style Guide&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqP1g8/btqSGjlBvcN/zgDrohP9hRFofVP5aadK60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqP1g8%2FbtqSGjlBvcN%2FzgDrohP9hRFofVP5aadK60%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;369&quot; width=&quot;699&quot; height=&quot;368&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Google HTML/CSS Style Guide&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;256&quot; data-filename=&quot;Untitled 1.png&quot; width=&quot;700&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmMDqH/btqSgNbGhxx/vRuH0LZ946Ct15jHLZ2bDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmMDqH/btqSgNbGhxx/vRuH0LZ946Ct15jHLZ2bDk/img.png&quot; data-alt=&quot;JavaScript Standard Style&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmMDqH/btqSgNbGhxx/vRuH0LZ946Ct15jHLZ2bDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmMDqH%2FbtqSgNbGhxx%2FvRuH0LZ946Ct15jHLZ2bDk%2Fimg.png&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;256&quot; data-filename=&quot;Untitled 1.png&quot; width=&quot;700&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JavaScript Standard Style&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;EditorConfig for VS Code&lt;/h2&gt;
&lt;h3&gt;1. Default Setting&lt;/h3&gt;
&lt;p&gt;1.1 Preferences &amp;gt; Settings (&lt;code&gt;⌘ + ,&lt;/code&gt;)에서 Text Editor &amp;gt; Tab Size를 4로 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2021-01-04__7.12.09.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bigI3h/btqSELWZQcx/h54mGquZ3myNcProgLpmM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bigI3h/btqSELWZQcx/h54mGquZ3myNcProgLpmM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bigI3h/btqSELWZQcx/h54mGquZ3myNcProgLpmM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbigI3h%2FbtqSELWZQcx%2Fh54mGquZ3myNcProgLpmM1%2Fimg.png&quot; data-filename=&quot;_2021-01-04__7.12.09.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1.2 Preferences &amp;gt; Settings (&lt;code&gt;⌘ + ,&lt;/code&gt;)에서 Text Editor &amp;gt; Detect Indentation 체크&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2021-01-04__7.15.46.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ng30y/btqSjCgBTt8/xuIhkPwWWIkHJOhuZVPKDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ng30y/btqSjCgBTt8/xuIhkPwWWIkHJOhuZVPKDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ng30y/btqSjCgBTt8/xuIhkPwWWIkHJOhuZVPKDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fng30y%2FbtqSjCgBTt8%2FxuIhkPwWWIkHJOhuZVPKDk%2Fimg.png&quot; data-filename=&quot;_2021-01-04__7.15.46.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2. VS Code Extension : EditorConfig for VS Code 설치&lt;/h3&gt;
&lt;p&gt;2.1 Installation&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2021-01-04__7.17.42.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nKGnF/btqSxB1U0C5/Z6nTDun8O5Li61Bh9bx2g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nKGnF/btqSxB1U0C5/Z6nTDun8O5Li61Bh9bx2g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nKGnF/btqSxB1U0C5/Z6nTDun8O5Li61Bh9bx2g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnKGnF%2FbtqSxB1U0C5%2FZ6nTDun8O5Li61Bh9bx2g1%2Fimg.png&quot; data-filename=&quot;_2021-01-04__7.17.42.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2.2 폴더 우클릭 후 Generate .editorconfig&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2021-01-04__7.29.57.png&quot; data-origin-width=&quot;2548&quot; data-origin-height=&quot;1455&quot; width=&quot;790&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyu0OS/btqSxBnkPmS/EpejGLMqOXXQkryk5XVk9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyu0OS/btqSxBnkPmS/EpejGLMqOXXQkryk5XVk9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyu0OS/btqSxBnkPmS/EpejGLMqOXXQkryk5XVk9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdyu0OS%2FbtqSxBnkPmS%2FEpejGLMqOXXQkryk5XVk9K%2Fimg.png&quot; data-filename=&quot;_2021-01-04__7.29.57.png&quot; data-origin-width=&quot;2548&quot; data-origin-height=&quot;1455&quot; width=&quot;790&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2.3 .editorconfig 파일 수정&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.html&lt;/code&gt; &lt;code&gt;.css&lt;/code&gt; &lt;code&gt;.js&lt;/code&gt; 파일의 경우 indent_size = 2로 설정&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;[*.{html, css, js}]
indent_size = 2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2021-01-04__7.53.43.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/naseT/btqSsRRxfh7/c7eKYBxL72e9ihQdkCmHeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/naseT/btqSsRRxfh7/c7eKYBxL72e9ihQdkCmHeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/naseT/btqSsRRxfh7/c7eKYBxL72e9ihQdkCmHeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnaseT%2FbtqSsRRxfh7%2Fc7eKYBxL72e9ihQdkCmHeK%2Fimg.png&quot; data-filename=&quot;_2021-01-04__7.53.43.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1682&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Supported Properties&lt;/b&gt; (&lt;a href=&quot;https://editorconfig.org/#supported-properties&quot;&gt;https://editorconfig.org/#supported-properties&lt;/a&gt;)&lt;/p&gt;
&lt;table style=&quot;height: 141px;&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;th style=&quot;width: 197px; height: 21px;&quot;&gt;Properties&lt;/th&gt;
&lt;th style=&quot;width: 601px; height: 21px;&quot;&gt;Comment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 181px; height: 20px;&quot;&gt;indent_style&lt;/td&gt;
&lt;td style=&quot;width: 585px; height: 20px;&quot;&gt;&lt;code&gt;tab&lt;/code&gt; 또는 &lt;code&gt;space&lt;/code&gt; 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 181px; height: 20px;&quot;&gt;indent_size&lt;/td&gt;
&lt;td style=&quot;width: 585px; height: 20px;&quot;&gt;들여쓰기 수준에 사용되는 너비의 size 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 181px; height: 20px;&quot;&gt;end_of_line&lt;/td&gt;
&lt;td style=&quot;width: 585px; height: 20px;&quot;&gt;개행 표시 방법 제어 (&lt;code&gt;lf&lt;/code&gt;, &lt;code&gt;cr&lt;/code&gt;, &lt;code&gt;crlf&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 181px; height: 20px;&quot;&gt;charset&lt;/td&gt;
&lt;td style=&quot;width: 585px; height: 20px;&quot;&gt;인코딩 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 181px; height: 20px;&quot;&gt;trim_trailing_whitespace&lt;/td&gt;
&lt;td style=&quot;width: 585px; height: 20px;&quot;&gt;newline 앞의 공백 문자를 제거하려면 &lt;code&gt;true&lt;/code&gt;로 그렇지 않으면 &lt;code&gt;false&lt;/code&gt;로 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 181px; height: 20px;&quot;&gt;insert_final_newline&lt;/td&gt;
&lt;td style=&quot;width: 585px; height: 20px;&quot;&gt;저장 시 파일 마지막 line을 줄바꿈 처리 하려면 &lt;code&gt;true&lt;/code&gt;로 그렇지 않으면 &lt;code&gt;false&lt;/code&gt;로 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.html&lt;/code&gt; 파일은 2 spaces로 &lt;code&gt;.java&lt;/code&gt; 파일은 4 spaces로 설정된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m4KRO/btqSpF4VoZU/tKlEohJ9XLFIZx7nVas1x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m4KRO/btqSpF4VoZU/tKlEohJ9XLFIZx7nVas1x0/img.png&quot; data-alt=&quot;.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m4KRO/btqSpF4VoZU/tKlEohJ9XLFIZx7nVas1x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm4KRO%2FbtqSpF4VoZU%2FtKlEohJ9XLFIZx7nVas1x0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKW1P0/btqSsQLTApH/XCi9TFUD3mHKVM6kiQTt51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKW1P0/btqSsQLTApH/XCi9TFUD3mHKVM6kiQTt51/img.png&quot; data-alt=&quot;.java&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKW1P0/btqSsQLTApH/XCi9TFUD3mHKVM6kiQTt51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKW1P0%2FbtqSsQLTApH%2FXCi9TFUD3mHKVM6kiQTt51%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;.java&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Etc</category>
      <category>.editorconfig</category>
      <category>EditorConfig</category>
      <category>extension</category>
      <category>indentation</category>
      <category>Tab Size</category>
      <category>vsCode</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/33</guid>
      <comments>https://suyeon96.tistory.com/33#entry33comment</comments>
      <pubDate>Mon, 4 Jan 2021 20:55:11 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 그래프 (Graph) - 인접행렬 vs 인접리스트, DFS, BFS, Connected Component, Spanning Tree</title>
      <link>https://suyeon96.tistory.com/32</link>
      <description>&lt;h2&gt;1. 그래프&lt;/h2&gt;
&lt;h3&gt;1.1 그래프란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;그래프(Graph)&lt;/b&gt;란 요소들이 서로 복잡하게 연결되어 있는 관계를 표현하는 자료구조이다.&lt;/p&gt;
&lt;p&gt;그래프는 &lt;b&gt;정점(vertex)과&lt;/b&gt; &lt;b&gt;간선(edge)&lt;/b&gt;들의 집합으로 구성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;179&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coHi34/btqSjAPXa41/Dwji6MLkkGCfd09dsTxnzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coHi34/btqSjAPXa41/Dwji6MLkkGCfd09dsTxnzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coHi34/btqSjAPXa41/Dwji6MLkkGCfd09dsTxnzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoHi34%2FbtqSjAPXa41%2FDwji6MLkkGCfd09dsTxnzk%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;179&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;G = (V, E)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;수학적으로 그래프를 표시하는 방법이다.&lt;/p&gt;
&lt;p&gt;V(G)는 그래프 G의 정점들의 집합을, E(G)는 그래프 G의 간선들의 집합을 의미한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래프는 수학자 &lt;b&gt;오일러(Euler)&lt;/b&gt;에 의해 창안되어 수학의 한 분야로서 수백 년 간 연구되어 왔지만, 컴퓨터 과학 분야에서 그래프 알고리즘을 다루기 시작한 것은 오래되지 않았다.&lt;/p&gt;
&lt;p&gt;지하철 노선도, 도심의 도로 등 실생활의 다양한 예를 그래프로 표현하고 활용할 수 있으며, 특히 알고리즘에서 굉장히 많이 사용되므로 그래프에 대해 반드시 공부해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;※ 오일러 경로 (Eulerian Tour)&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;그래프에 존재하는 모든 간선을 한번만 통과하면서 처음 정점으로 되돌아오는 경로&lt;/li&gt;
&lt;li&gt;그래프의 모든 정점에 연결된 간선의 개수가 짝수일 때만 오일러 경로가 존재 (오일러의 정리)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.2 그래프 용어&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;198&quot; data-origin-height=&quot;183&quot; width=&quot;179&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l11Sw/btqSASn7X6A/n1edQ5ZSOfPN0YtsT5qB00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l11Sw/btqSASn7X6A/n1edQ5ZSOfPN0YtsT5qB00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l11Sw/btqSASn7X6A/n1edQ5ZSOfPN0YtsT5qB00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl11Sw%2FbtqSASn7X6A%2Fn1edQ5ZSOfPN0YtsT5qB00%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;198&quot; data-origin-height=&quot;183&quot; width=&quot;179&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;정점 (vertex)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;노드(node)라고도 하며 데이터가 저장되는 그래프의 기본 원소이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;간선 (edge)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;링크(link)라고도 하며 정점 간의 관계를 나타낸다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;인접 정점 (adjacent vertex)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;하나의 정점에서 간선에 의해 직접 연결되어 있는 정점을 뜻한다.&lt;br /&gt;위 그래프에서 정점 &lt;code&gt;C&lt;/code&gt;의 인접 정점은 &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;D&lt;/code&gt; 이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;차수(degree)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;정점에 연결된 간선의 수를 말한다.&lt;br /&gt;위 그래프에서 정점 &lt;code&gt;A&lt;/code&gt;의 차수는 3이고, 모든 정점의 차수를 합하면 8이다.&lt;br /&gt;무방향 그래프에서 하나의 간선은 두 개의 정점에 인접하기 때문에 간선 수에 2배를 해주면 된다.&lt;/p&gt;
&lt;p&gt;방향 그래프의 경우 외부에서 오는 간선의 수를 &lt;b&gt;진입 차수(in-degree)&lt;/b&gt;라고 하며, 외부로 향하는 간선의 수를 &lt;b&gt;진출 차수(out-degree)&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;경로 (path)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;간선을 따라갈 수 있는 길을 말하며, 정점을 나열하여 표시한다.&lt;br /&gt;정점 &lt;code&gt;s&lt;/code&gt;로부터 정점 &lt;code&gt;e&lt;/code&gt;까지의 경로는 &lt;code&gt;s, v₁, v₂ ... e&lt;/code&gt;로 표현하며, 반드시 정점들 간의 간선이 존재해야 한다.&lt;br /&gt;너무나도 당연하게도 간선이 존재하지 않는 길은 경로가 될 수 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;경로의 길이 (length)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;경로를 구성하는 데 사용된 간선의 수를 뜻한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;단순 경로 (simple path)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;경로 중에서 반복되는 간선이 없는 경로이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;사이클 (cycle)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;시작 정점과 종료 정점이 같은 단순 경로를 뜻한다.&lt;br /&gt;위 그래프에서 &lt;code&gt;A, C, D, A&lt;/code&gt; 경로를 사이클이라고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;추가로 &lt;b&gt;그래프를 표현하는 방법&lt;/b&gt;에 대해서도 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;259&quot; width=&quot;496&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAsGGY/btqSmBnwzDm/vzlZE0dyk1u4uswIvHZbjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAsGGY/btqSmBnwzDm/vzlZE0dyk1u4uswIvHZbjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAsGGY/btqSmBnwzDm/vzlZE0dyk1u4uswIvHZbjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAsGGY%2FbtqSmBnwzDm%2FvzlZE0dyk1u4uswIvHZbjk%2Fimg.png&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;259&quot; width=&quot;496&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;V(G1)&lt;/b&gt; = {0, 1, 2, 3}, &lt;b&gt;E(G1)&lt;/b&gt; = {(0, 1), (0, 2), (0, 3), (1, 2), (2, 3)}&lt;/li&gt;
&lt;li&gt;&lt;b&gt;V(G2)&lt;/b&gt; = {0, 1, 2, 3}, &lt;b&gt;E(G2)&lt;/b&gt; = {(0, 1), (0, 2)}&lt;/li&gt;
&lt;li&gt;&lt;b&gt;V(G3)&lt;/b&gt; = (0, 1, 3}, &lt;b&gt;E(G3)&lt;/b&gt; = {&amp;lt;0, 1&amp;gt;, &amp;lt;1, 0&amp;gt;, &amp;lt;1, 2&amp;gt;}&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.3 그래프의 종류&lt;/h3&gt;
&lt;p&gt;그래프는 간선(edge)의 종류에 따라 구분된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;무방향 그래프 (Undirected Graph)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;198&quot; data-origin-height=&quot;47&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUThU4/btqSmzQORTu/1u4Ox6FgsxOFgU1w2glJNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUThU4/btqSmzQORTu/1u4Ox6FgsxOFgU1w2glJNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUThU4/btqSmzQORTu/1u4Ox6FgsxOFgU1w2glJNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUThU4%2FbtqSmzQORTu%2F1u4Ox6FgsxOFgU1w2glJNk%2Fimg.png&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;198&quot; data-origin-height=&quot;47&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;두 정점을 연결하는 간선에 방향이 없는 그래프이며, &lt;b&gt;양 방향으로 이동&lt;/b&gt;이 가능하다.&lt;/p&gt;
&lt;p&gt;위 그래프의 경우 &lt;code&gt;(A, B)&lt;/code&gt;로 표현한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;방향 그래프 (Directed Graph)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;44&quot; data-filename=&quot;Untitled 4.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf7r8g/btqSjAWJ7ak/jSdP6Vgulk3OKGKFIOSvyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf7r8g/btqSjAWJ7ak/jSdP6Vgulk3OKGKFIOSvyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf7r8g/btqSjAWJ7ak/jSdP6Vgulk3OKGKFIOSvyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf7r8g%2FbtqSjAWJ7ak%2FjSdP6Vgulk3OKGKFIOSvyk%2Fimg.png&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;44&quot; data-filename=&quot;Untitled 4.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;두 정점을 연결하는 간선에 방향이 존재하는 그래프이며, &lt;b&gt;간선의 방향으로만 이동&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p&gt;위 그래프의 경우 &lt;code&gt;&amp;lt;A, B&amp;gt;&lt;/code&gt;로 표현한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;가중치 그래프 (Weighted Graph)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;44&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKxtMJ/btqSgLEcnLe/lvjTpb30PrkpATPdon1NAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKxtMJ/btqSgLEcnLe/lvjTpb30PrkpATPdon1NAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKxtMJ/btqSgLEcnLe/lvjTpb30PrkpATPdon1NAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKxtMJ%2FbtqSgLEcnLe%2FlvjTpb30PrkpATPdon1NAK%2Fimg.png&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;44&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;간선에 &lt;b&gt;비용(cost)&lt;/b&gt; 또는 &lt;b&gt;가중치(weight)&lt;/b&gt;가 할당된 그래프이다. &lt;b&gt;네트워크(network)&lt;/b&gt;라고 불리기도 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;완전 그래프 (Complete Graph)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;185&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtlpYK/btqSbaEBdgX/UldMBA9Va8rNKQ00nKu4F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtlpYK/btqSbaEBdgX/UldMBA9Va8rNKQ00nKu4F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtlpYK/btqSbaEBdgX/UldMBA9Va8rNKQ00nKu4F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtlpYK%2FbtqSbaEBdgX%2FUldMBA9Va8rNKQ00nKu4F1%2Fimg.png&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;185&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;모든 정점 간에 간선이 존재하는 그래프이다.&lt;/p&gt;
&lt;p&gt;무방향 완전 그래프의 정점의 수가 &lt;code&gt;n&lt;/code&gt;이라면, 전체 간선의 수는 &lt;code&gt;n * (n-1) / 2&lt;/code&gt; 가 된다.&lt;/p&gt;
&lt;p&gt;위 그래프의 경우 n=4이며 간선의 수는 (4*3)/2=6 이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.4 그래프 ADT&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;객체&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정점의 집합과 간선의 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;연산&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create() : 그래프 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;insertVertex(v)&lt;/b&gt; : 그래프에 정점 v 삽입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;insertEdge(u, v)&lt;/b&gt; : 그래프에 u정점과 v정점을 연결하는 간선 삽입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;deleteVertex(v)&lt;/b&gt; : 그래프에서 정점 v 삭제 (v에 연결된 모든 간선도 함께 삭제)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;deleteEdge(u, v)&lt;/b&gt; : 그래프에서 u정점과 v정점을 연결하는 간선 삭제&lt;/li&gt;
&lt;li&gt;adjacent(v) : 정점 v에 인접한 모든 정점을 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.5 그래프 구현 - 인접 행렬 (&lt;b&gt;Adjacency Materix)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;컴퓨터에서 그래프를 구현하는 방법에는 &lt;b&gt;배열(Array)&lt;/b&gt;을 사용하는 방법과 &lt;b&gt;연결리스트(Linked List)&lt;/b&gt;를 사용하는 방법이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;인접 행렬을 이용한 그래프 구현&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;309&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AX0fJ/btqSsQxDVL3/UMyk36B9WbAqVOzNkLpO8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AX0fJ/btqSsQxDVL3/UMyk36B9WbAqVOzNkLpO8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AX0fJ/btqSsQxDVL3/UMyk36B9WbAqVOzNkLpO8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAX0fJ%2FbtqSsQxDVL3%2FUMyk36B9WbAqVOzNkLpO8k%2Fimg.png&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;309&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그래프의 정점을 &lt;b&gt;2차원 배열&lt;/b&gt;로 만든 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정점의 개수가 &lt;code&gt;n&lt;/code&gt;이라면 &lt;code&gt;n*n&lt;/code&gt; 형태의 2차원 배열이 인접 행렬로 사용된다.&lt;/p&gt;
&lt;p&gt;인접 행렬에서 행과 열은 정점을 의미하며, 각각의 원소들은 정점 간의 간선을 나타낸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;무방향 그래프&lt;/b&gt;는 (a), (b)에서 볼 수 있듯이 인접 행렬이 대칭적 구조를 가진다.&lt;br /&gt;(두 개의 정점에서 간선이 동시에 연결되어 있기 때문)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;가중치 그래프&lt;/b&gt;의 경우 행렬에서 0과 1이 아니라 각 간선의 가중치 값이 저장된다.&lt;br /&gt;(이 경우 가중치가 0인 것과 간선이 없는 것이 구별돼야 함)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;2차원 배열에 모든 정점들의 간선 정보가 있기 때문에, &lt;b&gt;두 정점을 연결하는 간선을 조회할 때&lt;/b&gt; &lt;code&gt;O(1)&lt;/code&gt; 시간복잡도로 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;정점(i)의 차수를 구할 때&lt;/b&gt;는 다음과 같이 인접행렬(M)의 i번째 행의 값을 모두 더하면 되므로 &lt;code&gt;O(n)&lt;/code&gt;의 시간복잡도를 가진다.&lt;/p&gt;
&lt;p&gt;( $degree(i) = \sum_{k=0}^{n-1}{M[i][k]}$ )&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;구현이 비교적 간단하다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;간선의 수와 무관하게 항상 n&amp;sup2; 크기의 2차원 배열이 필요하므로 &lt;b&gt;메모리 공간이 낭비&lt;/b&gt;된다.&lt;/li&gt;
&lt;li&gt;그래프의 &lt;b&gt;모든 간선의 수&lt;/b&gt;를 알아내려면 인접행렬 전체를 확인해야 하므로 &lt;code&gt;O(n&amp;sup2;)&lt;/code&gt;의 시간이 소요된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;C++ 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/*
 * 9.1
 * 인접 행렬(Adjacency Matrix)를 이용한 Graph 구현
 */
#include &amp;lt;iostream&amp;gt;
using namespace std;

#define MAX_VTXS 256    // 최대 정점 개수

class AdjMatGraph{
private:
    int size;                       // 정점의 개수
    char vertices[MAX_VTXS];        // 정점의 이름
    int adjMat[MAX_VTXS][MAX_VTXS];    // 인접 행렬

public:
    AdjMatGraph(){
        reset();
    }
    ~AdjMatGraph(){}

    char getVertex(int i){
        return vertices[i];
    }
    int getEdge(int i, int j){
        return adjMat[i][j];
    }
    void setEdge(int i, int j, int val){
        adjMat[i][j] = val;
    }

    // 그래프 초기화
    void reset(){
        for(int i=0; i&amp;lt;MAX_VTXS; i++){
            for(int j=0; j&amp;lt;MAX_VTXS; j++){
                setEdge(i, j, 0);
            }
        }
        size = 0;
    }

    // 정점 삽입
    void insertVertex(char name){
        if(isFull()){
            cout &amp;lt;&amp;lt; &quot;Graph vertex full error&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        vertices[size++] = name;
    }

    // 간선 삽입 (무방향 그래프)
    void insertEdge(int u, int v){
        setEdge(u, v, 1);       // 가중치 그래프에서는 1이 아닌 가중치 삽입
        setEdge(v, u, 1);       // 방향 그래프에서는 삭제 (&amp;lt;u,v&amp;gt;만 존재)
    }

    // 그래프 정보 출력
    void display(){
        cout &amp;lt;&amp;lt; &quot;vertex size : &quot; &amp;lt;&amp;lt; size &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; &quot;    &quot;;
        for(int i=0; i&amp;lt;size; i++){
            cout &amp;lt;&amp;lt; getVertex(i) &amp;lt;&amp;lt; &quot; &quot;;
        }
        cout &amp;lt;&amp;lt; endl;

        for(int i=0; i&amp;lt;size; i++){
            cout &amp;lt;&amp;lt; getVertex(i) &amp;lt;&amp;lt; &quot; : &quot;;
            for(int j=0; j&amp;lt;size; j++){
                cout &amp;lt;&amp;lt; getEdge(i, j) &amp;lt;&amp;lt; &quot; &quot;;
            }
            cout &amp;lt;&amp;lt; endl;
        }
    }

    bool isEmpty(){
        return size == 0;
    }
    bool isFull(){
        return size &amp;gt;= MAX_VTXS;
    }
};

int main(){
    AdjMatGraph graph;

    // 정점 삽입 (A, B, C, D)
    graph.insertVertex('A');    // 0
    graph.insertVertex('B');    // 1
    graph.insertVertex('C');    // 2
    graph.insertVertex('D');    // 3

    // 간선 삽입
    graph.insertEdge(0, 1);     // A-&amp;gt;B
    graph.insertEdge(0, 2);     // A-&amp;gt;C
    graph.insertEdge(0, 3);     // A-&amp;gt;D
    graph.insertEdge(2, 3);     // C-&amp;gt;D

    graph.display();

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-30__10.41.09.png&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;448&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdTBYF/btqSsQYLqvP/g3OUn6AL6MFuApHnkVK5H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdTBYF/btqSsQYLqvP/g3OUn6AL6MFuApHnkVK5H1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdTBYF/btqSsQYLqvP/g3OUn6AL6MFuApHnkVK5H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdTBYF%2FbtqSsQYLqvP%2Fg3OUn6AL6MFuApHnkVK5H1%2Fimg.png&quot; data-filename=&quot;_2020-11-30__10.41.09.png&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;448&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;인접행렬을 이용하여 아래와 같은 그래프를 구현하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;145&quot; data-origin-height=&quot;144&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3cRGR/btqSa9ZW0Xu/te6GcfFBnyZsdyNbNUAPJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3cRGR/btqSa9ZW0Xu/te6GcfFBnyZsdyNbNUAPJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3cRGR/btqSa9ZW0Xu/te6GcfFBnyZsdyNbNUAPJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3cRGR%2FbtqSa9ZW0Xu%2Fte6GcfFBnyZsdyNbNUAPJk%2Fimg.png&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;145&quot; data-origin-height=&quot;144&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.6 그래프 구현 - 인접 리스트 (&lt;b&gt;Adjacency List)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;인접 리스트를 이용한 그래프 구현&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;417&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FT7FQ/btqSASBFP80/Ij5cIGUZ3Dk7YyoxvaFiZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FT7FQ/btqSASBFP80/Ij5cIGUZ3Dk7YyoxvaFiZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FT7FQ/btqSASBFP80/Ij5cIGUZ3Dk7YyoxvaFiZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFT7FQ%2FbtqSASBFP80%2FIj5cIGUZ3Dk7YyoxvaFiZK%2Fimg.png&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;417&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그래프의 각 정점에 인접한 정점들을 &lt;b&gt;연결리스트(Linked List)&lt;/b&gt;로 표현하는 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉 정점의 개수만큼 인접리스트가 존재하며, 각각의 인접리스트에는 인접한 정점 정보가 저장되는 것이다.&lt;/p&gt;
&lt;p&gt;그래프는 각 인접리스트에 대한 헤드포인터를 배열로 갖는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;무방향 그래프&lt;/b&gt;의 경우 간선이 추가되면 각각의 정점의 인접리스트에 반대편 정점의 노드를 추가해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;존재하는 간선만 관리하면 되므로 &lt;b&gt;메모리 사용 측면에서 보다 효율적&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;그래프의 &lt;b&gt;모든 간선의 수&lt;/b&gt;를 알아내려면 각 정점의 헤더 노드부터 모든 인접리스트를 탐색해야 하므로 O(n+e)의 시간이 소요된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;두 정점을 연결하는 간선을 조회&lt;/b&gt;하거나 &lt;b&gt;정점의 차수&lt;/b&gt;를 알기 위해서는 정점의 인접 리스트를 탐색해야 하므로 정점의 차수만큼의 시간이 필요하다. &lt;code&gt;O(degree(v))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;구현이 비교적 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;C++ 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/*
 * 9.2
 * 인접 리스트(Adjacency List)를 이용한 Graph 구현 (무방향 그래프)
 */
#include &amp;lt;iostream&amp;gt;
using namespace std;

#define MAX_VTXS 256    // 최대 정점 개수

struct Node{
private:
    int id;         // vertex id
    Node* link;     // next node's pointer

public:
    Node(int _id, Node* _link){
        id = _id;
        link = _link;
    }
    ~Node(){};

    // getter/setter
    int getId(){
        return id;
    }
    void setId(int _id){
        id = _id;
    }
    Node* getLink(){
        return link;
    }
    void setLink(Node* _link){
        link = _link;
    }
};

class AdjListGraph{
private:
    int size;                   // 정점의 개수
    char vertices[MAX_VTXS];    // 정점의 이름
    Node* adjList[MAX_VTXS];    // 인접 리스트

public:
    AdjListGraph(){
        size = 0;
    }
    ~AdjListGraph(){}

    char getVertex(int i){
        return vertices[i];
    }

    // 그래프 초기화
    void reset(){
        for(int i=0; i&amp;lt;size; i++){
            if(adjList[i] != NULL){
                delete adjList[i];
            }
        }
        size = 0;
    }

    // 정점 삽입
    void insertVertex(char name){
        if(isFull()){
            cout &amp;lt;&amp;lt; &quot;Graph vertex full error&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        vertices[size] = name;
        adjList[size++] = NULL;
    }

    // 간선 삽입 (무방향 그래프)
    void insertEdge(int u, int v){
        // 인접리스트에 추가
        adjList[u] = new Node(v, adjList[u]);   // 새로운 노드로 head pointer가 바뀜 (새로운 node는 기존의 head pointer를 link로 함)
        adjList[v] = new Node(u, adjList[v]);   // 방향 그래프에서는 삭제 (&amp;lt;u,v&amp;gt;만 존재)
    }

    // 그래프 정보 출력
    void display(){
        cout &amp;lt;&amp;lt; &quot;vertex size : &quot; &amp;lt;&amp;lt; size &amp;lt;&amp;lt; endl;
        for(int i=0; i&amp;lt;size; i++){
            cout &amp;lt;&amp;lt; getVertex(i) &amp;lt;&amp;lt; &quot; : &quot;;
            Node* head = adjList[i];
            while(head != NULL){
                cout &amp;lt;&amp;lt; getVertex(head-&amp;gt;getId()) &amp;lt;&amp;lt; &quot; &quot;;
                head = head-&amp;gt;getLink();
            }
            cout &amp;lt;&amp;lt; endl;
        }
    }

    // 'v'번째 정점의 인접 정점 리스트 반환
    Node* adjacent(int v){
        return adjList[v];
    }

    bool isEmpty(){
        return size == 0;
    }
    bool isFull(){
        return size &amp;gt;= MAX_VTXS;
    }
};

int main(){
    AdjListGraph graph;

    // 정점 삽입 (A, B, C, D)
    graph.insertVertex('A');    // 0
    graph.insertVertex('B');    // 1
    graph.insertVertex('C');    // 2
    graph.insertVertex('D');    // 3

    // 간선 삽입
    graph.insertEdge(0, 1);     // A-&amp;gt;B
    graph.insertEdge(0, 2);     // A-&amp;gt;C
    graph.insertEdge(0, 3);     // A-&amp;gt;D
    graph.insertEdge(2, 3);     // C-&amp;gt;D

    graph.display();

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-30__11.40.35.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;402&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OhRp8/btqSAShlUF8/xQFLiqveiQ42e5u2pditnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OhRp8/btqSAShlUF8/xQFLiqveiQ42e5u2pditnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OhRp8/btqSAShlUF8/xQFLiqveiQ42e5u2pditnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOhRp8%2FbtqSAShlUF8%2FxQFLiqveiQ42e5u2pditnk%2Fimg.png&quot; data-filename=&quot;_2020-11-30__11.40.35.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;402&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;동일하게 아래의 그래프를 인접 리스트로 구현한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;145&quot; data-origin-height=&quot;144&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LEkZf/btqSAT8qdet/XCGkVJiqXoSH8XkaU7PYZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LEkZf/btqSAT8qdet/XCGkVJiqXoSH8XkaU7PYZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LEkZf/btqSAT8qdet/XCGkVJiqXoSH8XkaU7PYZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLEkZf%2FbtqSAT8qdet%2FXCGkVJiqXoSH8XkaU7PYZK%2Fimg.png&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;145&quot; data-origin-height=&quot;144&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.7 인접 행렬 vs 인접 리스트&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 10.png&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;309&quot; width=&quot;720&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s9cNv/btqSdtcL9I9/WGH8cw8sqPuRDW1jygiwkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s9cNv/btqSdtcL9I9/WGH8cw8sqPuRDW1jygiwkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s9cNv/btqSdtcL9I9/WGH8cw8sqPuRDW1jygiwkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs9cNv%2FbtqSdtcL9I9%2FWGH8cw8sqPuRDW1jygiwkk%2Fimg.png&quot; data-filename=&quot;Untitled 10.png&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;309&quot; width=&quot;720&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;마치 어떻게 활용될지에 따라 ArrayList와 LinkedList를 고민하는 것처럼, 이 또한 상황에 따라 알맞은 방법을 선택해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 10억 개의 노드가 있고 각 노드가 2개씩의 간선만 있는 상황이다. 인접행렬로 구현한 그래프에서는 한 정점의 차수를 구할 때 10억번의 연산을 수행할 것이다. 반면, 인접리스트로 구현한 그래프에서는 2번의 연산만 수행하면 된다.&lt;/p&gt;
&lt;p&gt;정점의 개수에 비해 간선의 개수가 매우 적은 희소 그래프(sparse graph)에서는 인접리스트가 유리할 수 있고, 모든 정점간에 간선이 존재하는 완전 그래프(Complete Graph)에서는 인접행렬이 유리할 수 있다.&lt;/p&gt;
&lt;p&gt;그래프에서 주로 어떤 연산이 수행되는지도 매우 중요하게 고려되어야 할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;답은 없다. 상황에 따라 최선의 방법을 선택하는 것이 프로그래머의 덕목이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. 그래프 활용&lt;/h2&gt;
&lt;h3&gt;2.1 깊이우선 탐색 (DFS)&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;깊이우선 탐색(DFS: Depth First Search)&lt;/b&gt;이란 특정 노드에서 시작하여 다음 분기(branch)로 넘어가기 전에 해당 분기를 완벽하게(끝까지) 탐색하는 방법을 뜻한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 그래프를 깊이우선 탐색해보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 11.png&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;146&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RIvNY/btqR9lTU8zf/wbWOmlwKkKtEgGqp6ljKbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RIvNY/btqR9lTU8zf/wbWOmlwKkKtEgGqp6ljKbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RIvNY/btqR9lTU8zf/wbWOmlwKkKtEgGqp6ljKbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRIvNY%2FbtqR9lTU8zf%2FwbWOmlwKkKtEgGqp6ljKbk%2Fimg.png&quot; data-filename=&quot;Untitled 11.png&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;146&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;인접행렬을 이용한 DFS 구현 (C++)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// DFS 탐색 기능이 추가된 그래프
class SearchAdjMatGraph : public AdjMatGraph{
private:
    bool visited[MAX_VTXS];     // 방문 기록

public:
    // 모든 정점의 방문기록을 false로 초기화
    void resetVisited(){
        for(int i=0; i&amp;lt;size; i++){
            visited[i] = false;
        }
    }

    // 깊이우선탐색 (순환)
    void dfs(int v){
        resetVisited();
        dfsRecur(v);
    }
    void dfsRecur(int v){
        visited[v] = true;      // 현재 vertex 방문처리
        cout &amp;lt;&amp;lt; getVertex(v) &amp;lt;&amp;lt; &quot; &quot;;

        for(int n=0; n&amp;lt;size; n++){
            if(isLinked(v, n) &amp;amp;&amp;amp; visited[n]==false){
                dfsRecur(n);
            }
        }
    }
};

int main(){
    SearchAdjMatGraph graph;

    // 정점 삽입 (A, B, C, D)
    graph.insertVertex('A');    // 0
    graph.insertVertex('B');    // 1
    graph.insertVertex('C');    // 2
    graph.insertVertex('D');    // 3
    graph.insertVertex('E');    // 4
    graph.insertVertex('F');    // 5
    graph.insertVertex('G');    // 6
    graph.insertVertex('H');    // 7
    // 간선 삽입
    graph.insertEdge(0, 1);     // A-&amp;gt;B
    graph.insertEdge(0, 2);     // A-&amp;gt;C
    graph.insertEdge(1, 3);     // B-&amp;gt;D
    graph.insertEdge(2, 3);     // C-&amp;gt;D
    graph.insertEdge(2, 4);     // C-&amp;gt;E
    graph.insertEdge(3, 5);     // D-&amp;gt;F
    graph.insertEdge(4, 6);     // E-&amp;gt;G
    graph.insertEdge(4, 7);     // E-&amp;gt;H
    graph.insertEdge(6, 7);     // G-&amp;gt;H

    cout &amp;lt;&amp;lt; &quot;== Display Graph == &quot; &amp;lt;&amp;lt; endl;
    graph.display();

    cout &amp;lt;&amp;lt; &quot;-DFS =&amp;gt; &quot;;
    graph.dfs(0);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-12-01__12.47.08.png&quot; data-origin-width=&quot;1772&quot; data-origin-height=&quot;656&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPBu0R/btqSgMb3rNm/WlOQ8Koy4NhBWgxhTEwDLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPBu0R/btqSgMb3rNm/WlOQ8Koy4NhBWgxhTEwDLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPBu0R/btqSgMb3rNm/WlOQ8Koy4NhBWgxhTEwDLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPBu0R%2FbtqSgMb3rNm%2FWlOQ8Koy4NhBWgxhTEwDLK%2Fimg.png&quot; data-filename=&quot;_2020-12-01__12.47.08.png&quot; data-origin-width=&quot;1772&quot; data-origin-height=&quot;656&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;인접리스트를 이용한 DFS 구현 (C++)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// DFS 탐색 기능이 추가된 그래프
class SearchAdjListGraph : public AdjListGraph{
private:
    bool visited[MAX_VTXS];     // 방문 기록

public:
    // 모든 정점의 방문기록을 false로 초기화
    void resetVisited(){
        for(int i=0; i&amp;lt;size; i++){
            visited[i] = false;
        }
    }

    // 깊이우선탐색 (순환)
    void dfs(int v){
        resetVisited();
        dfsRecur(v);
    }
    void dfsRecur(int v){
        visited[v] = true;      // 현재 vertex 방문처리
        cout &amp;lt;&amp;lt; getVertex(v) &amp;lt;&amp;lt; &quot; &quot;;

        // v 정점의 인접리스트를 모두 방문하며 탐색
        for(Node *p=adjList[v]; p!=NULL; p=p-&amp;gt;getLink()){
            if(visited[p-&amp;gt;getId()] == false){
                dfsRecur(p-&amp;gt;getId());
            }
        }
    }
};

int main(){
    SearchAdjListGraph graph;

    // 정점 삽입 (A, B, C, D)
    graph.insertVertex('A');    // 0
    graph.insertVertex('B');    // 1
    graph.insertVertex('C');    // 2
    graph.insertVertex('D');    // 3
    graph.insertVertex('E');    // 4
    graph.insertVertex('F');    // 5
    graph.insertVertex('G');    // 6
    graph.insertVertex('H');    // 7
    // 간선 삽입
    graph.insertEdge(0, 1);     // A-&amp;gt;B
    graph.insertEdge(0, 2);     // A-&amp;gt;C
    graph.insertEdge(1, 3);     // B-&amp;gt;D
    graph.insertEdge(2, 3);     // C-&amp;gt;D
    graph.insertEdge(2, 4);     // C-&amp;gt;E
    graph.insertEdge(3, 5);     // D-&amp;gt;F
    graph.insertEdge(4, 6);     // E-&amp;gt;G
    graph.insertEdge(4, 7);     // E-&amp;gt;H
    graph.insertEdge(6, 7);     // G-&amp;gt;H

    cout &amp;lt;&amp;lt; &quot;== Display Graph == &quot; &amp;lt;&amp;lt; endl;
    graph.display();

    cout &amp;lt;&amp;lt; &quot;-DFS =&amp;gt; &quot;;
    graph.dfs(0);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-12-01__1.10.45.png&quot; data-origin-width=&quot;1770&quot; data-origin-height=&quot;606&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDCWxo/btqSa802JrC/b7dcz7uWNUS37FbP1rmynK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDCWxo/btqSa802JrC/b7dcz7uWNUS37FbP1rmynK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDCWxo/btqSa802JrC/b7dcz7uWNUS37FbP1rmynK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDCWxo%2FbtqSa802JrC%2Fb7dcz7uWNUS37FbP1rmynK%2Fimg.png&quot; data-filename=&quot;_2020-12-01__1.10.45.png&quot; data-origin-width=&quot;1770&quot; data-origin-height=&quot;606&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;DFS 결과 순서가 다른 이유는 인접 리스트의 노드 순서가 역순으로 들어있기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;b&gt;※&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;시간복잡도&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;정점의 수가 n이고 간선의 수가 e인 그래프를 &lt;b&gt;인접 행렬로 구현하는 경우 DFS 시간복잡도&lt;/b&gt;는 &lt;code&gt;O(n&amp;sup2;)&lt;/code&gt; 이며, &lt;b&gt;인접 리스트로 구현하는 경우 DFS 시간복잡도&lt;/b&gt;는 &lt;code&gt;O(n+e)&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 너비우선 탐색&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;너비우선 탐색(BFS: Breadth First Search)&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이란 특정 노드에서 시작하여 인접한 노드를 먼저 탐색해나가는 방법을 뜻한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이번에는 아래 그래프를 너비우선 탐색해보자&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 11.png&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;146&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUymZA/btqSsQYLuv0/89zCICeU41dhNBiv6kuw90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUymZA/btqSsQYLuv0/89zCICeU41dhNBiv6kuw90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUymZA/btqSsQYLuv0/89zCICeU41dhNBiv6kuw90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUymZA%2FbtqSsQYLuv0%2F89zCICeU41dhNBiv6kuw90%2Fimg.png&quot; data-filename=&quot;Untitled 11.png&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;146&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;인접행렬을 이용한 BFS 구현 (C++)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// BFS 탐색 기능이 추가된 그래프
class SearchAdjMatGraph : public AdjMatGraph{
private:
    bool visited[MAX_VTXS];     // 방문 기록

public:
    // 모든 정점의 방문기록을 false로 초기화
    void resetVisited(){
        for(int i=0; i&amp;lt;size; i++){
            visited[i] = false;
        }
    }

    // 너비우선탐색 (큐 이용)
    void bfs(int v){
        resetVisited();

        visited[v] = true;      // 현재 vertex 방문처리
        cout &amp;lt;&amp;lt; getVertex(v) &amp;lt;&amp;lt; &quot; &quot;;

        queue&amp;lt;int&amp;gt; queue;
        queue.push(v);          // 시작 vertex enqueue

        while(!queue.empty()){
            v = queue.front();
            queue.pop();        // v = queue.dequeue()

            // 인접 정점 탐색
            for(int n=0; n&amp;lt;size; n++){
                if(isLinked(v, n) &amp;amp;&amp;amp; visited[n]==false){
                    cout &amp;lt;&amp;lt; getVertex(n) &amp;lt;&amp;lt; &quot; &quot;;
                    visited[n] = true;  // 방문 처리
                    queue.push(n);
                }
            }
        }

    }
};

int main(){
    SearchAdjMatGraph graph;

    // 정점 삽입 (A, B, C, D)
    graph.insertVertex('A');    // 0
    graph.insertVertex('B');    // 1
    graph.insertVertex('C');    // 2
    graph.insertVertex('D');    // 3
    graph.insertVertex('E');    // 4
    graph.insertVertex('F');    // 5
    graph.insertVertex('G');    // 6
    graph.insertVertex('H');    // 7
    // 간선 삽입
    graph.insertEdge(0, 1);     // A-&amp;gt;B
    graph.insertEdge(0, 2);     // A-&amp;gt;C
    graph.insertEdge(1, 3);     // B-&amp;gt;D
    graph.insertEdge(2, 3);     // C-&amp;gt;D
    graph.insertEdge(2, 4);     // C-&amp;gt;E
    graph.insertEdge(3, 5);     // D-&amp;gt;F
    graph.insertEdge(4, 6);     // E-&amp;gt;G
    graph.insertEdge(4, 7);     // E-&amp;gt;H
    graph.insertEdge(6, 7);     // G-&amp;gt;H

    cout &amp;lt;&amp;lt; &quot;== Display Graph == &quot; &amp;lt;&amp;lt; endl;
    graph.display();

    cout &amp;lt;&amp;lt; &quot;-BFS =&amp;gt; &quot;;
    graph.bfs(0);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-12-01__2.57.58.png&quot; data-origin-width=&quot;1766&quot; data-origin-height=&quot;654&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5mhtB/btqSmzJ1qLJ/ZRxZAI9fq2N3qcxpEuKcUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5mhtB/btqSmzJ1qLJ/ZRxZAI9fq2N3qcxpEuKcUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5mhtB/btqSmzJ1qLJ/ZRxZAI9fq2N3qcxpEuKcUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5mhtB%2FbtqSmzJ1qLJ%2FZRxZAI9fq2N3qcxpEuKcUK%2Fimg.png&quot; data-filename=&quot;_2020-12-01__2.57.58.png&quot; data-origin-width=&quot;1766&quot; data-origin-height=&quot;654&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;인접리스트를 이용한 BFS 구현 (C++)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// BFS 탐색 기능이 추가된 그래프
class SearchAdjListGraph : public AdjListGraph{
private:
    bool visited[MAX_VTXS];     // 방문 기록

public:
    // 모든 정점의 방문기록을 false로 초기화
    void resetVisited(){
        for(int i=0; i&amp;lt;size; i++){
            visited[i] = false;
        }
    }

    // 너비우선탐색 (큐 사용)
    void bfs(int v){
        resetVisited();

        visited[v] = true;      // 현재 vertex 방문처리
        cout &amp;lt;&amp;lt; getVertex(v) &amp;lt;&amp;lt; &quot; &quot;;

        queue&amp;lt;int&amp;gt; queue;
        queue.push(v);          // 시작 vertex enqueue

        while(!queue.empty()){
            v = queue.front();
            queue.pop();        // v = queue.dequeue()

            // 인접 정점 탐색
            for(Node *n=adjList[v]; n!=NULL; n=n-&amp;gt;getLink()){
                int nId = n-&amp;gt;getId();       // 인접 노드의 정점 id
                if(!visited[nId]){
                    cout &amp;lt;&amp;lt; getVertex(nId) &amp;lt;&amp;lt; &quot; &quot;;
                    visited[nId] = true;  // 방문 처리
                    queue.push(nId);
                }
            }
        }
    }
};

int main(){
    SearchAdjListGraph graph;

    // 정점 삽입 (A, B, C, D)
    graph.insertVertex('A');    // 0
    graph.insertVertex('B');    // 1
    graph.insertVertex('C');    // 2
    graph.insertVertex('D');    // 3
    graph.insertVertex('E');    // 4
    graph.insertVertex('F');    // 5
    graph.insertVertex('G');    // 6
    graph.insertVertex('H');    // 7
    // 간선 삽입
    graph.insertEdge(0, 1);     // A-&amp;gt;B
    graph.insertEdge(0, 2);     // A-&amp;gt;C
    graph.insertEdge(1, 3);     // B-&amp;gt;D
    graph.insertEdge(2, 3);     // C-&amp;gt;D
    graph.insertEdge(2, 4);     // C-&amp;gt;E
    graph.insertEdge(3, 5);     // D-&amp;gt;F
    graph.insertEdge(4, 6);     // E-&amp;gt;G
    graph.insertEdge(4, 7);     // E-&amp;gt;H
    graph.insertEdge(6, 7);     // G-&amp;gt;H

    cout &amp;lt;&amp;lt; &quot;== Display Graph == &quot; &amp;lt;&amp;lt; endl;
    graph.display();

    cout &amp;lt;&amp;lt; &quot;-BFS =&amp;gt; &quot;;
    graph.bfs(0);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-12-01__3.03.07.png&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;618&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wtLeR/btqSgLEcww5/PIsCQ2k1MctAUfYHCQzUcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wtLeR/btqSgLEcww5/PIsCQ2k1MctAUfYHCQzUcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wtLeR/btqSgLEcww5/PIsCQ2k1MctAUfYHCQzUcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwtLeR%2FbtqSgLEcww5%2FPIsCQ2k1MctAUfYHCQzUcK%2Fimg.png&quot; data-filename=&quot;_2020-12-01__3.03.07.png&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;618&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;※ 시간복잡도&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;정점의 수가 n이고 간선의 수가 e인 그래프를 &lt;b&gt;인접 행렬로 구현하는 경우 BFS 시간복잡도&lt;/b&gt;는 &lt;code&gt;O(n&amp;sup2;)&lt;/code&gt; 이며, &lt;b&gt;인접 리스트로 구현하는 경우 BFS 시간복잡도&lt;/b&gt;는 &lt;code&gt;O(n+e)&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.3 Connected Component&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Connected Component(연결 성분)&lt;/b&gt;이란 여러 개의 노드의 집합에서 간선으로 연결된 각각의 그래프를 의미한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 12.png&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;158&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JJbxW/btqSxBGXsiO/AFfIzdd0Yr6TROeChNdgLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JJbxW/btqSxBGXsiO/AFfIzdd0Yr6TROeChNdgLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JJbxW/btqSxBGXsiO/AFfIzdd0Yr6TROeChNdgLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJJbxW%2FbtqSxBGXsiO%2FAFfIzdd0Yr6TROeChNdgLk%2Fimg.png&quot; data-filename=&quot;Untitled 12.png&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;158&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;연결 성분을 찾는 방법&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;그래프 상의 임의의 노드를 선택해 DFS 또는 BFS 탐색을 수행한다.&lt;br /&gt;(방문한 노드는 같은 성분으로 labeling 한다.)&lt;/li&gt;
&lt;li&gt;남은 노드 중 방문하지 않은 노드를 선택하여 다시 1번을 수행한다.&lt;/li&gt;
&lt;li&gt;그래프의 모든 노드가 방문처리될 때까지 1, 2번을 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;C++ 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/*
 * 9.9
 * 인접 행렬(Adjacency Matrix)과 DFS를 이용한 Connected Component (연결 성분) 탐색 프로그램 구현해보기
 */
#include &amp;lt;iostream&amp;gt;
using namespace std;

#define MAX_VTXS 256    // 최대 정점 개수

class AdjMatGraph{
protected:
    int size;                           // 정점의 개수
    char vertices[MAX_VTXS];            // 정점의 이름
    int adjMat[MAX_VTXS][MAX_VTXS];     // 인접 행렬

public:
    AdjMatGraph(){
        reset();
    }
    ~AdjMatGraph(){}

    char getVertex(int i){
        return vertices[i];
    }
    int getEdge(int i, int j){
        return adjMat[i][j];
    }
    void setEdge(int i, int j, int val){
        adjMat[i][j] = val;
    }

    // 그래프 초기화
    void reset(){
        for(int i=0; i&amp;lt;MAX_VTXS; i++){
            for(int j=0; j&amp;lt;MAX_VTXS; j++){
                setEdge(i, j, 0);
            }
        }
        size = 0;
    }

    // 정점 삽입
    void insertVertex(char name){
        if(isFull()){
            cout &amp;lt;&amp;lt; &quot;Graph vertex full error&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        vertices[size++] = name;
    }

    // 간선 삽입 (무방향 그래프)
    void insertEdge(int u, int v){
        setEdge(u, v, 1);       // 가중치 그래프에서는 1이 아닌 가중치 삽입
        setEdge(v, u, 1);       // 방향 그래프에서는 삭제 (&amp;lt;u,v&amp;gt;만 존재)
    }

    // 그래프 정보 출력
    void display(){
        cout &amp;lt;&amp;lt; &quot;vertex size : &quot; &amp;lt;&amp;lt; size &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; &quot;    &quot;;
        for(int i=0; i&amp;lt;size; i++){
            cout &amp;lt;&amp;lt; getVertex(i) &amp;lt;&amp;lt; &quot; &quot;;
        }
        cout &amp;lt;&amp;lt; endl;

        for(int i=0; i&amp;lt;size; i++){
            cout &amp;lt;&amp;lt; getVertex(i) &amp;lt;&amp;lt; &quot; : &quot;;
            for(int j=0; j&amp;lt;size; j++){
                cout &amp;lt;&amp;lt; getEdge(i, j) &amp;lt;&amp;lt; &quot; &quot;;
            }
            cout &amp;lt;&amp;lt; endl;
        }
    }

    // 두개의 정점이 연결되어 있는지 검사
    bool isLinked(int u, int v){
        return getEdge(u, v) != 0;
    }

    bool isEmpty(){
        return size == 0;
    }
    bool isFull(){
        return size &amp;gt;= MAX_VTXS;
    }
};

// DFS를 이용하여 그래프의 연결성분 탐색
class ConnectedComponentGraph : public AdjMatGraph{
private:
    int labelValue = 0;
    int label[MAX_VTXS];     // component label;

public:
    // 모든 node의 label을 0으로 초기화
    void resetVisited(){
        for(int i=0; i&amp;lt;size; i++){
            label[i] = 0;
        }
    }

    // 깊이우선탐색 (순환)
    void labelDfsRecur(int v){
        label[v] = labelValue;

        for(int n=0; n&amp;lt;size; n++){
            if(isLinked(v, n) &amp;amp;&amp;amp; label[n]==0){
                labelDfsRecur(n);
            }
        }
    }

    void findConnectedComponent(){
        cout &amp;lt;&amp;lt; &quot;== Connected Component ==&quot; &amp;lt;&amp;lt; endl;
        int cnt = 0;        // component 개수
        labelValue = 0;     // labelValue 초기화
        resetVisited();     // label array 초기화

        for(int i=0; i&amp;lt;size; i++){
            if(label[i]==0){        // 방문하지 않은 node의 경우
                labelValue++;       // label 값 증가
                labelDfsRecur(i);
                cnt++;
            }
        }

        cout &amp;lt;&amp;lt; &quot;- component 개수 : &quot; &amp;lt;&amp;lt; cnt &amp;lt;&amp;lt; endl;
        for(int i=1; i&amp;lt;=labelValue; i++){
            cout &amp;lt;&amp;lt; &quot;  &quot; &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; : &quot;;
            for(int j=0; j&amp;lt;size; j++){
                if(label[j]==i){
                    cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; getVertex(j) &amp;lt;&amp;lt; &quot;]&quot;;
                }
            }
            cout &amp;lt;&amp;lt; endl;
        }

    }
};

int main(){
    // 그래프 모양
    cout &amp;lt;&amp;lt; &quot;== Graph Shape ==&quot; &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;A - C   E - G&quot; &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;          \\ &quot; &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;B - D - F   H&quot; &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; endl;

    // 인접 행렬 그래프
    ConnectedComponentGraph graph;

    cout &amp;lt;&amp;lt; &quot;== Adjacency Matrix Graph == &quot; &amp;lt;&amp;lt; endl;
    // 정점 삽입 (A, B, C, D)
    graph.insertVertex('A');    // 0
    graph.insertVertex('B');    // 1
    graph.insertVertex('C');    // 2
    graph.insertVertex('D');    // 3
    graph.insertVertex('E');    // 4
    graph.insertVertex('F');    // 5
    graph.insertVertex('G');    // 6
    graph.insertVertex('H');    // 7
    // 간선 삽입
    graph.insertEdge(0, 2);     // A-&amp;gt;C
    graph.insertEdge(1, 3);     // B-&amp;gt;D
    graph.insertEdge(3, 5);     // D-&amp;gt;F
    graph.insertEdge(4, 6);     // E-&amp;gt;G
    graph.insertEdge(4, 7);     // E-&amp;gt;H

    graph.display();

    graph.findConnectedComponent();

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-12-08__4.23.31.png&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;1070&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQtUiu/btqSduW3T9O/FDdPnOTpLZKW1bTLflmfAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQtUiu/btqSduW3T9O/FDdPnOTpLZKW1bTLflmfAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQtUiu/btqSduW3T9O/FDdPnOTpLZKW1bTLflmfAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQtUiu%2FbtqSduW3T9O%2FFDdPnOTpLZKW1bTLflmfAK%2Fimg.png&quot; data-filename=&quot;_2020-12-08__4.23.31.png&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;1070&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.4 신장 트리 (Spanning Tree)&lt;/h3&gt;
&lt;p&gt;신장 트리(spanning tree)란 그래프 내의 모든 정점이 연결되어 있으면서 사이클이 없는 트리를 의미한다.&lt;/p&gt;
&lt;p&gt;신장 트리는 그래프의 n개의 정점을 정확하게 n-1개의 간선으로 연결하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;신장 트리를 구현하기 위해서는 DFS 또는 BFS 도중에 사용된 간선들만 남겨두면 된다.&lt;/p&gt;
&lt;p&gt;즉, 이를 그림으로 표현하면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 13.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;347&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxCEfQ/btqSa96M8Ie/NmsKEidRSsctxhjwV2J1A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxCEfQ/btqSa96M8Ie/NmsKEidRSsctxhjwV2J1A0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxCEfQ/btqSa96M8Ie/NmsKEidRSsctxhjwV2J1A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxCEfQ%2FbtqSa96M8Ie%2FNmsKEidRSsctxhjwV2J1A0%2Fimg.png&quot; data-filename=&quot;Untitled 13.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;347&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Data Structure</category>
      <category>Adjacency List</category>
      <category>Adjacency Materix</category>
      <category>BFS</category>
      <category>connected component</category>
      <category>DFS</category>
      <category>Graph</category>
      <category>Spanning Tree</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/32</guid>
      <comments>https://suyeon96.tistory.com/32#entry32comment</comments>
      <pubDate>Mon, 4 Jan 2021 04:05:49 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 우선순위 큐와 힙 (Priority Queue &amp;amp; Heap)</title>
      <link>https://suyeon96.tistory.com/31</link>
      <description>&lt;h2&gt;1. 우선순위 큐&lt;/h2&gt;
&lt;h3&gt;1.1 우선순위 큐란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;큐(Queue)&lt;/b&gt;는 먼저 들어오는 데이터가 먼저 나가는 &lt;b&gt;FIFO(First In First Out)&lt;/b&gt; 형식의 자료구조이다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;우선순위 큐(Priority Queue)&lt;/b&gt;는 먼저 들어오는 데이터가 아니라, 우선순위가 높은 데이터가 먼저 나가는 형태의 자료구조이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우선순위 큐는 일반적으로 &lt;b&gt;힙(Heap)&lt;/b&gt;을 이용하여 구현한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.2 힙이란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;힙(Heap)&lt;/b&gt;은 우선순위 큐를 위해 고안된 완전이진트리 형태의 자료구조이다.&lt;/p&gt;
&lt;p&gt;여러 개의 값 중 최댓값 또는 최솟값을 찾아내는 연산이 빠르다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;힙의 특징&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;완전이진트리&lt;/b&gt; 형태로 이루어져 있다.&lt;/li&gt;
&lt;li&gt;부모노드와 서브트리간 대소 관계가 성립된다. (반정렬 상태)&lt;/li&gt;
&lt;li&gt;이진탐색트리(BST)와 달리 중복된 값이 허용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;힙의 종류&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;최대 힙 (Max Heap)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;부모 노드의 키 값이 자식 노드보다 크거나 같은 완전이진트리이다.&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span&gt;❝ &lt;span&gt;key(부모노드) &amp;ge; key(자식노드) ❞&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;231&quot; width=&quot;301&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cT2Dxb/btqSATggBLA/CIBeKSLq0s6MDTNVM345Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cT2Dxb/btqSATggBLA/CIBeKSLq0s6MDTNVM345Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cT2Dxb/btqSATggBLA/CIBeKSLq0s6MDTNVM345Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcT2Dxb%2FbtqSATggBLA%2FCIBeKSLq0s6MDTNVM345Jk%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;231&quot; width=&quot;301&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;최소 힙 (Min Heap)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;부모 노드의 키 값이 자식 노드보다 작거나 같은 완전이진트리이다.&lt;/p&gt;
&lt;p&gt;&lt;i&gt;❝ &lt;span&gt;key(부모노드) &amp;ge; key(자식노드) &lt;span&gt;&lt;span&gt;❞&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;215&quot; width=&quot;300&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwtTZl/btqSASIpEE1/zJxtetzfI1OGHucT99Mcuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwtTZl/btqSASIpEE1/zJxtetzfI1OGHucT99Mcuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwtTZl/btqSASIpEE1/zJxtetzfI1OGHucT99Mcuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwtTZl%2FbtqSASIpEE1%2FzJxtetzfI1OGHucT99Mcuk%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;215&quot; width=&quot;300&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.3 우선순위 큐 구현방법 비교&lt;/h3&gt;
&lt;p&gt;우선순위 큐를 힙이 아니라 배열 또는 연결리스트를 이용하여 구현할 수도 있다.&lt;/p&gt;
&lt;p&gt;하지만 배열과 연결리스트는 선형 구조의 자료구조이므로 삽입 또는 삭제 연산을 위한 시간복잡도는 &lt;code&gt;O(n)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;반면 힙트리는 완전이진트리 구조이므로 힙트리의 높이는 &lt;code&gt;log₂(n+1)&lt;/code&gt;이며, 힙의 시간복잡도는 &lt;code&gt;O(log₂n)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 이를 정리한 표이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;234&quot; width=&quot;407&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZgks1/btqSpGIQnTq/maNdQlnXSBRC1KWGJtzXYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZgks1/btqSpGIQnTq/maNdQlnXSBRC1KWGJtzXYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZgks1/btqSpGIQnTq/maNdQlnXSBRC1KWGJtzXYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZgks1%2FbtqSpGIQnTq%2FmaNdQlnXSBRC1KWGJtzXYK%2Fimg.png&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;234&quot; width=&quot;407&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.4 우선순위 큐 ADT&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;객체&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;우선순위를 가진 요소들의 모음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;연산&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;insert&lt;/b&gt;(x) : 우선순위 큐에 요소 x 추가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;remove&lt;/b&gt;() : 우선순위 큐에서 가장 우선순위가 높은 요소를 삭제하고 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;find&lt;/b&gt;() : 우선순위 큐에서 가장 우선순위가 높은 요소를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. 우선순위 큐 구현&lt;/h2&gt;
&lt;h3&gt;2.1 힙 구현&lt;/h3&gt;
&lt;p&gt;힙은 일반적으로 &lt;b&gt;배열을 이용하여 구현&lt;/b&gt;한다.&lt;/p&gt;
&lt;p&gt;완전 이진트리이므로 중간에 비어있는 요&lt;span style=&quot;color: #333333;&quot;&gt;소가 없기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;294&quot; width=&quot;437&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/trcoJ/btqR9mecYaz/rrzQSqsZDoGZ5mhfqHzf61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/trcoJ/btqR9mecYaz/rrzQSqsZDoGZ5mhfqHzf61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/trcoJ/btqR9mecYaz/rrzQSqsZDoGZ5mhfqHzf61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtrcoJ%2FbtqR9mecYaz%2FrrzQSqsZDoGZ5mhfqHzf61%2Fimg.png&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;294&quot; width=&quot;437&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 그림과 같이 트리의 각 노드에 번호를 붙이고, 이 번호를 배열의 인덱스로 생각하면 효과적으로 힙을 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;배열로 구현하였기 때문에 부모 또는 자식 노드를 찾아가는 연산을 구현하기도 간편하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;자식노드를 구하고 싶을 때&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;왼쪽 자식노드 index = (부모 노드 index) * 2&lt;/li&gt;
&lt;li&gt;오른쪽 자식노드 index = (부모 노드 index) * 2 + 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;부모노드를 구하고 싶을 때&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;부모 노드 index = (자식노드 index) / 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 삽입 연산&lt;/h3&gt;
&lt;p&gt;힙에 삽입을 하기 위해서는 힙 트리의 성질을 만족시키면서 새로운 요소를 추가해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;삽입 방법&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;우선 완전이진트리의 마지막 노드에 이어서 새로운 노드를 추가한다.&lt;/li&gt;
&lt;li&gt;추가된 새로운 노드를 부모의 노드와 비교하여 교환한다.&lt;/li&gt;
&lt;li&gt;정상적인 힙트리가 될 때 까지 (더이상 부모노드와 교환할 필요가 없을 때까지) 2번을 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;최악의 경우 새로 추가된 노드가 루트노트까지 비교하며 올라가야 하므로 시간복잡도가 &lt;code&gt;O(log₂n)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.3 삭제 연산&lt;/h3&gt;
&lt;p&gt;힙 트리에서 루트노드가 가장 우선순위가 높으므로 루트 노드를 삭제해야 한다.&lt;/p&gt;
&lt;p&gt;삭제가 이뤄진 후 힙 트리의 성질이 유지돼야 하므로 아래와 같은 방법으로 삭제를 진행한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;삭제 방법&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;루트 노드를 삭제한다.&lt;/li&gt;
&lt;li&gt;루트 노드가 삭제된 빈자리에 완전이진트리의 마지막 노드를 가져온다.&lt;/li&gt;
&lt;li&gt;루트 자리에 위치한 새로운 노드를 자식 노드와 비교하여 교환한다.&lt;br /&gt;이때 최대 힙인 경우 자식노드 중 더 큰 값과 교환을 하며, 최소 힙인 경우 더 작은 값과 교환을 한다.&lt;/li&gt;
&lt;li&gt;정상적인 힙트리가 될 때까지 (더 이상 자식노드와 교환할 필요가 없을 때까지) 3번을 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;삭제 연산 또한 최악의 경우 루트노트부터 가장 아래까지 내려가야 하므로 시간복잡도가 &lt;code&gt;O(log₂n)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.4 우선순위 큐 구현 (C++)&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

#define MAX_ELEMENT 200      // heap array size

template &amp;lt;typename T&amp;gt;
struct Node{
private:
    int key;
    T data;
public:
    Node(){
        key = 0;
    }
    Node(int _key, T _data){
        key = _key;
        data = _data;
    }
    ~Node(){}

    // getter/setter
    int getKey(){
        return key;
    }
    void setKey(int _key){
        key = _key;
    }
    T getData(){
        return data;
    }
    void setData(T _data){
        data = _data;
    }
};

template &amp;lt;typename T&amp;gt;
class MaxHeap{
private:
    Node&amp;lt;T&amp;gt; node[MAX_ELEMENT];
    int size;   // heap 요소 개수
public:
    MaxHeap(){
        size = 0;
    }
    ~MaxHeap(){}

    // search node
    Node&amp;lt;T&amp;gt;&amp;amp; getParent(int index){
        return node[index/2];
    }
    Node&amp;lt;T&amp;gt;&amp;amp; getLeftChild(int index){
        return node[index*2];
    }
    Node&amp;lt;T&amp;gt;&amp;amp; getRightChild(int index){
        return node[index*2+1];
    }

    // 삽입
    void insert(int key, T data){
        if(isFull()){
            cout &amp;lt;&amp;lt; &quot;Heap is Full&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        int index = ++size;     // 힙트리 마지막 노드의 다음 위치에서 시작

        // 힙트리를 거슬러 올라가며 부모 노드와 비교
        while(index != 1 &amp;amp;&amp;amp; key &amp;gt; getParent(index).getKey()){
            node[index] = getParent(index);
            index /= 2;
        }

        // 최종 위치에 데이터 삽입
        node[index].setKey(key);
        node[index].setData(data);
    }

    // 삭제
    T remove(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Heap is Empty&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }

        Node&amp;lt;T&amp;gt; itemNode = node[1];         // root node (삭제 대상)
        Node&amp;lt;T&amp;gt; lastNode = node[size--];    // 힙트리의 마지막 노드
        int index = 1;                      // 마지막 노드의 index (root 부터 시작)

        // root 부터 내려가며 자식 노드와 비교
        while(index &amp;lt;= size){
            if(index*2 &amp;gt; size){             // leaf node인 경우 (자식 노드가 없는 경우)
                break;
            }else if(index*2 == size){      // 자식노드가 하나인 경우
                if(lastNode.getKey() &amp;lt; getLeftChild(index).getKey()){
                    node[index] = getLeftChild(index);
                    index *= 2;
                }else{
                    break;
                }
            }else{                          // 자식노드가 두개인 경우
                int leftChildKey = getLeftChild(index).getKey();
                int rightChildKey = getRightChild(index).getKey();

                // 둘 중 key가 더 큰 자식노드와 교환
                if(lastNode.getKey() &amp;lt; leftChildKey || lastNode.getKey() &amp;lt; rightChildKey){
                    node[index] = leftChildKey &amp;gt; rightChildKey ? getLeftChild(index) : getRightChild(index);
                    index = leftChildKey &amp;gt; rightChildKey ? index*2 : index*2+1;
                }else{
                    break;
                }
            }
        }
        node[index] = lastNode;     // 마지막 노드를 최종 위치에 저장
        return itemNode.getData();  // 삭제 노드의 data 반환
    }

    // 출력
    void display(){
        int level = 1;
        for(int i=1; i&amp;lt;= size; i++){
            if(i == level){
                cout &amp;lt;&amp;lt; endl;
                level *= 2;
            }
            cout &amp;lt;&amp;lt; node[i].getData() &amp;lt;&amp;lt; &quot;(&quot; &amp;lt;&amp;lt; node[i].getKey() &amp;lt;&amp;lt; &quot;)  &quot;;
        }
        cout &amp;lt;&amp;lt; '\n' &amp;lt;&amp;lt; &quot;-------------------------&quot; &amp;lt;&amp;lt; endl;
    }

    bool isEmpty(){
        return size == 0;
    }
    bool isFull(){
        return size == MAX_ELEMENT - 1;
    }

};

int main(){
    MaxHeap&amp;lt;int&amp;gt; priorityQueue;

    // 삽입
    priorityQueue.insert(10, 10);
    priorityQueue.insert(5, 5);
    priorityQueue.insert(30, 30);
    priorityQueue.insert(8, 8);
    priorityQueue.insert(9, 9);
    priorityQueue.insert(3, 3);
    priorityQueue.insert(7, 7);
    priorityQueue.display();

    // 삭제
    priorityQueue.remove();
    priorityQueue.display();
    priorityQueue.remove();
    priorityQueue.display();

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-29__9.35.35.png&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;820&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHxJ00/btqSxBGW0lA/JRi4AxN1fsEHZUDpX72VJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHxJ00/btqSxBGW0lA/JRi4AxN1fsEHZUDpX72VJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHxJ00/btqSxBGW0lA/JRi4AxN1fsEHZUDpX72VJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHxJ00%2FbtqSxBGW0lA%2FJRi4AxN1fsEHZUDpX72VJK%2Fimg.png&quot; data-filename=&quot;_2020-11-29__9.35.35.png&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;820&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.5 C++에서의 우선순위 큐 사용 (STL)&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

int main(){
    // Max Heap
    priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, less&amp;lt;int&amp;gt;&amp;gt; pq1;

    pq1.push(5);
    pq1.push(2);
    pq1.push(8);
    pq1.push(9);
    pq1.push(1);
    pq1.push(14);

    pq1.pop();
    pq1.pop();

    cout &amp;lt;&amp;lt; &quot;Max Heap PQ : &quot;;
    while(!pq1.empty()){
        cout &amp;lt;&amp;lt; pq1.top() &amp;lt;&amp;lt; &quot; &quot;;
        pq1.pop();
    }
    cout &amp;lt;&amp;lt; endl;

    // Min Heap
    priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq2;

    pq2.push(5);
    pq2.push(2);
    pq2.push(8);
    pq2.push(9);
    pq2.push(1);
    pq2.push(14);

    pq2.pop();
    pq2.pop();

    cout &amp;lt;&amp;lt; &quot;Min Heap PQ : &quot;;
    while(!pq2.empty()){
        cout &amp;lt;&amp;lt; pq2.top() &amp;lt;&amp;lt; &quot; &quot;;
        pq2.pop();
    }
    cout &amp;lt;&amp;lt; endl;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-29__9.49.56.png&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;322&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM69rj/btqSsQj7Ait/Yze5JfKWZfKAhZ3S15b8K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM69rj/btqSsQj7Ait/Yze5JfKWZfKAhZ3S15b8K1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM69rj/btqSsQj7Ait/Yze5JfKWZfKAhZ3S15b8K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM69rj%2FbtqSsQj7Ait%2FYze5JfKWZfKAhZ3S15b8K1%2Fimg.png&quot; data-filename=&quot;_2020-11-29__9.49.56.png&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;322&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.6 [응용] 힙 정렬 (Heap Sort)&lt;/h3&gt;
&lt;p&gt;힙 정렬이란 최대 힙트리나 최소 힙트리를 이용한 정렬 알고리즘 중 하나이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;힙 정렬 알고리즘&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;n개의 요소를 하나씩 힙에 삽입한다.&lt;/li&gt;
&lt;li&gt;n번에 걸쳐 힙에서 요소를 하나씩 삭제하고 반환된 값을 순차적으로 정렬한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;앞에서 힙의 삽입&amp;middot;삭제 시간복잡도는 &lt;code&gt;O(log₂n)&lt;/code&gt; 이라는 것을 알아보았다.&lt;/p&gt;
&lt;p&gt;힙 정렬을 위해 n번의 삽입과 n번의 삭제가 이뤄지므로 정렬에 필요한 전체 시간은 &lt;code&gt;log₂n + log₂n&lt;/code&gt;에 비례한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이는 &lt;code&gt;O(log₂n)&lt;/code&gt; 시간복잡도로 표현 가능하며, 삽입정렬과 같은 간단한 정렬 알고리즘의 시간복잡도가 &lt;code&gt;O(n&amp;sup2;)&lt;/code&gt;이라는 것과 비교했을 때 매우 효율적인 알고리즘이다.&lt;/p&gt;
&lt;p&gt;특히 힙 정렬은 전체 자료를 정렬하는 것보다 전체에서 가장 큰 값 몇 개가 필요할 때 유용하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;C++ 힙 정렬 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/*
 * 8.5
 * Max Heap을 이용하여 내림차순 힙 정렬 구현
 */
#include &amp;lt;iostream&amp;gt;
using namespace std;

#define MAX_ELEMENT 200      // heap array size

struct Node{
private:
    int key;
public:
    Node(){
        key = 0;
    }
    Node(int _key){
        key = _key;
    }
    ~Node(){}

    // getter/setter
    int getKey(){
        return key;
    }
    void setKey(int _key){
        key = _key;
    }
};

class MaxHeap{
private:
    Node node[MAX_ELEMENT];
    int size;   // heap 요소 개수
public:
    MaxHeap(){
        size = 0;
    }
    ~MaxHeap(){}

    // search node
    Node&amp;amp; getParent(int index){
        return node[index/2];
    }
    Node&amp;amp; getLeftChild(int index){
        return node[index*2];
    }
    Node&amp;amp; getRightChild(int index){
        return node[index*2+1];
    }

    // 삽입
    void insert(int key){
        if(isFull()){
            cout &amp;lt;&amp;lt; &quot;Heap is Full&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        int index = ++size;     // 힙트리 마지막 노드의 다음 위치에서 시작

        // 힙트리를 거슬러 올라가며 부모 노드와 비교
        while(index != 1 &amp;amp;&amp;amp; key &amp;gt; getParent(index).getKey()){
            node[index] = getParent(index);
            index /= 2;
        }

        // 최종 위치에 데이터 삽입
        node[index].setKey(key);
    }

    // 삭제
    int remove(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Heap is Empty&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }

        Node itemNode = node[1];         // root node (삭제 대상)
        Node lastNode = node[size--];    // 힙트리의 마지막 노드
        int index = 1;                      // 마지막 노드의 index (root 부터 시작)

        // root 부터 내려가며 자식 노드와 비교
        while(index &amp;lt;= size){
            if(index*2 &amp;gt; size){             // leaf node인 경우 (자식 노드가 없는 경우)
                break;
            }else if(index*2 == size){      // 자식노드가 하나인 경우
                if(lastNode.getKey() &amp;lt; getLeftChild(index).getKey()){
                    node[index] = getLeftChild(index);
                    index *= 2;
                }else{
                    break;
                }
            }else{                          // 자식노드가 두개인 경우
                int leftChildKey = getLeftChild(index).getKey();
                int rightChildKey = getRightChild(index).getKey();

                // 둘 중 key가 더 큰 자식노드와 교환
                if(lastNode.getKey() &amp;lt; leftChildKey || lastNode.getKey() &amp;lt; rightChildKey){
                    node[index] = leftChildKey &amp;gt; rightChildKey ? getLeftChild(index) : getRightChild(index);
                    index = leftChildKey &amp;gt; rightChildKey ? index*2 : index*2+1;
                }else{
                    break;
                }
            }
        }
        node[index] = lastNode;     // 마지막 노드를 최종 위치에 저장
        return itemNode.getKey();  // 삭제 노드의 data 반환
    }

    bool isEmpty(){
        return size == 0;
    }
    bool isFull(){
        return size == MAX_ELEMENT - 1;
    }

};

// 힙 정렬
void heapSort(int a[], int n){
    MaxHeap heap;

    for(int i=0; i&amp;lt;n; i++){
        heap.insert(a[i]);
    }

    for(int i=0; i&amp;lt;n; i++){
        a[i] = heap.remove();
    }
}

int main(){
    int data[10];

    for(int i=0; i&amp;lt;10; i++){
        data[i] = rand() % 100;     // random data
    }

    cout &amp;lt;&amp;lt; &quot;origin data : &quot;;
    for(int i=0; i&amp;lt;10; i++) cout &amp;lt;&amp;lt; data[i] &amp;lt;&amp;lt; &quot; &quot;;
    cout &amp;lt;&amp;lt; endl;

    heapSort(data, sizeof(data)/sizeof(int));
    cout &amp;lt;&amp;lt; &quot;sorted data : &quot;;
    for(int i=0; i&amp;lt;10; i++) cout &amp;lt;&amp;lt; data[i] &amp;lt;&amp;lt; &quot; &quot;;
    cout &amp;lt;&amp;lt; endl;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-29__10.38.46.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;334&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rHzv8/btqSpFQIw9e/MskdaQxEFHNkdAbh5HSnE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rHzv8/btqSpFQIw9e/MskdaQxEFHNkdAbh5HSnE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rHzv8/btqSpFQIw9e/MskdaQxEFHNkdAbh5HSnE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrHzv8%2FbtqSpFQIw9e%2FMskdaQxEFHNkdAbh5HSnE1%2Fimg.png&quot; data-filename=&quot;_2020-11-29__10.38.46.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;334&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;오름차순 구현을 위해서는 Min Heap을 사용하고, 내림차순 구현을 위해서는 Max Heap을 사용하는 것이 편하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Data Structure</category>
      <category>Heap</category>
      <category>heap sort</category>
      <category>max heap</category>
      <category>Min Heap</category>
      <category>priority queue</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/31</guid>
      <comments>https://suyeon96.tistory.com/31#entry31comment</comments>
      <pubDate>Mon, 4 Jan 2021 03:07:10 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 이진탐색트리 (Binary Search Tree)</title>
      <link>https://suyeon96.tistory.com/30</link>
      <description>&lt;h2&gt;1. 이진탐색트리&lt;/h2&gt;
&lt;h3&gt;1.1 이진탐색트리란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;이진탐색트리(BST: Binary Search Tree)&lt;/b&gt;는 이진트리 기반의 탐색을 위한 자료구조이다.&lt;/p&gt;
&lt;p&gt;이진탐색의 효율적인 탐색 능력을 가지며, 삽입과 삭제가 가능한 것이 특징이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.2 이진탐색트리의 4가지 조건&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;모든 노드는 유일한 키를 갖는다.&lt;/li&gt;
&lt;li&gt;왼쪽 서브트리의 키들은 루트의 키보다 작다.&lt;/li&gt;
&lt;li&gt;오른쪽 서브트리의 키들은 루트의 키보다 작다.&lt;/li&gt;
&lt;li&gt;왼쪽과 오른쪽 서브트리도 이진탐색트리이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;254&quot; width=&quot;350&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QFfvN/btqSjB2nU4o/mNjK5Oj7WpYkxfkdlBZXk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QFfvN/btqSjB2nU4o/mNjK5Oj7WpYkxfkdlBZXk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QFfvN/btqSjB2nU4o/mNjK5Oj7WpYkxfkdlBZXk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQFfvN%2FbtqSjB2nU4o%2FmNjK5Oj7WpYkxfkdlBZXk0%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;254&quot; width=&quot;350&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.3 이진탐색트리 특징&lt;/h3&gt;
&lt;p&gt;이진탐색트리를 순회할 때는 &lt;b&gt;중위순회(In-order Traversal)&lt;/b&gt; 방법을 사용한다.&lt;br /&gt;(왼쪽 서브트리 &amp;rarr; 루트 &amp;rarr; 오른쪽 서브트리 순서)&lt;/p&gt;
&lt;p&gt;이진탐색트리를 중위 순회한 결과는 트리 내의 모든 값들을 오름차순으로 정렬한 결과를 나타낸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들어 위 그림의 트리를 중위 순회한 결과는 다음과 같다.&lt;br /&gt;&amp;rarr; &lt;code&gt;3 7 12 18 26 27 31&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.4 이진탐색트리 ADT&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;객체&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이진탐색트리의 4가지 조건을 만족하는 이진트리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;연산&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;search(key) : 키 값이 key인 노드를 찾아 반환&lt;/li&gt;
&lt;li&gt;insert(n) : 이진탐색트리의 특성을 유지하며 노드 n 삽입&lt;/li&gt;
&lt;li&gt;delete(n) : 이진탐색트리의 특성을 유지하며 노드 n 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. 이진탐색트리 연산&lt;/h2&gt;
&lt;h3&gt;2.1 탐색&lt;/h3&gt;
&lt;p&gt;이진탐색트리에서 root 노드가 왼쪽 서브트리보다 크고 오른쪽 서브트리보다 작다는 점을 기억하자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;탐색 방법&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;찾고자하는 키와 현재 루트 노드의 키를 비교한다.&lt;/li&gt;
&lt;li&gt;찾고자하는 키가 루트 노드의 키보다 작으면 왼쪽 자식 노드를 기준으로 다시 1번부터 시작한다.&lt;br /&gt;찾고자하는 키가 루트 노드의 키보다 크면 오른쪽 자식 노드를 기준으로 다시 1번부터 시작한다.&lt;/li&gt;
&lt;li&gt;찾고자하는 키와 루트 노드의 키가 같으면 탐색이 종료된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;굉장히 easy한 방법이다.&lt;/p&gt;
&lt;p&gt;위의 알고리즘을 구현하려고 보니 순환(Recursive)을 써도 되고, 반복(Iterative)을 사용해도 된다.&lt;/p&gt;
&lt;p&gt;각각의 방법을 이용하여 한번 구현해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;a. 순환으로 탐색 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;// 탐색 (순환)
Node&amp;lt;T&amp;gt;* searchRecur(Node&amp;lt;T&amp;gt;* node, T key){
    if(node == NULL) return NULL;

    if(key == node-&amp;gt;getData()) return node;     // key == root 노드의 data
    else if(key &amp;lt; node-&amp;gt;getData()) return searchRecur(node-&amp;gt;getLeft(), key);    // key &amp;lt; root 노드의 data
    else if(key &amp;gt; node-&amp;gt;getData()) return searchRecur(node-&amp;gt;getRight(), key);   // key &amp;gt; root 노드의 data
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;b. 반복으로 탐색 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;// 탐색 (반복)
Node&amp;lt;T&amp;gt;* searchIter(Node&amp;lt;T&amp;gt;* node, T key){
    while(node != NULL){
        if(key == node-&amp;gt;getData()) return node;     // key == root 노드의 data
        else if(key &amp;lt; node-&amp;gt;getData()) node = node-&amp;gt;getLeft();   // key &amp;lt; root 노드의 data
        else if(key &amp;gt; node-&amp;gt;getData()) node = node-&amp;gt;getRight();  // key &amp;gt; root 노드의 data
    }
    return NULL;    // 탐색이 실패한 경우 NULL 반환
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 효율성을 따지면 반복으로 구현한 탐색 함수가 훨씬 유리하다.&lt;/p&gt;
&lt;p&gt;하지만 우리는 공부를 하는 단계이니 여러 방법으로 시도를 해봐야 도움이 될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 잠깐. 트리의 각 노드는 자신을 루트로 하는 이진트리가 될 수 있다.&lt;/p&gt;
&lt;p&gt;그럼 &lt;code&gt;Node&lt;/code&gt; 클래스에서 멤버 함수로 탐색을 구현해 보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;c. Node 클래스에서 멤버함수로 순환 탐색 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;template &amp;lt;typename T&amp;gt;
struct Node{
private:
    T data;
    Node* left;
    Node* right;
public:
    Node(T _data, Node* _left, Node* _right){
        data = _data;
        left = _left;
        right = _right;
    }
    ~Node(){}

    // Node 멤버함수로 순환탐색 구현 (ㅎㅎ)
    Node* search(T key){
        if(key == data)                         // key == 노드의 data
            return this;
        else if(key &amp;lt; data &amp;amp;&amp;amp; left != NULL)     // key &amp;lt; 노드의 data
            return left-&amp;gt;search(key);
        else if(key &amp;gt; data &amp;amp;&amp;amp; right != NULL)    // key &amp;gt; 노드의 data
            return left-&amp;gt;search(key);
        else                                    // 탐색이 실패한 경우
            return NULL;
    }

    ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여러가지 방법으로 탐색 알고리즘을 구현해 보았다.&lt;/p&gt;
&lt;p&gt;여기서 알 수 있듯이 이진탐색트리 &lt;b&gt;탐색 연산의 시간복잡도&lt;/b&gt;는 트리의 높이가 &lt;code&gt;h&lt;/code&gt; 일 때 &lt;code&gt;O(h)&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 삽입&lt;/h3&gt;
&lt;p&gt;이진탐색트리에 삽입을 하기 위해서는 이진탐색트리의 조건을 만족하는 위치를 찾아 새로운 노드를 추가해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;삽입 방법&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;추가하고자 하는 key로 탐색을 수행한다.&lt;/li&gt;
&lt;li&gt;이진탐색트리에는 중복된 key가 있을 수 없으므로, 추가하고자 하는 key를 갖는 노드가 있다면 삽입이 불가능하다.&lt;/li&gt;
&lt;li&gt;탐색에 실패한다면 실패한 위치가 바로 새로운 노드가 삽입 되는 위치가 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;왜 3번에서 탐색이 실패한 위치에 새로운 노드가 삽입되는 지는 탐색을 이해하였다면 충분히 이해했으리라고 생각한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;순환을 이용하여 삽입 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;// 삽입 (순환)
void insertRecur(Node&amp;lt;T&amp;gt;* node, T key){
    Node&amp;lt;T&amp;gt;* newNode = new Node&amp;lt;T&amp;gt;(key, nullptr, nullptr);  // 새로운 key를 가지는 node 생성

    if(key == node-&amp;gt;getData()){         // 트리에 이미 key값을 가지는 node가 있는 경우
        return;
    }else if(key &amp;lt; node-&amp;gt;getData()){    // key &amp;lt; root 노드의 data
        if(node-&amp;gt;getLeft() == NULL){
            node-&amp;gt;setLeft(newNode);
        }else{
            insertRecur(node-&amp;gt;getLeft(), key);
        }
    }else if(key &amp;gt; node-&amp;gt;getData()){    // key &amp;gt; root 노드의 data
        if(node-&amp;gt;getRight() == NULL){
            node-&amp;gt;setRight(newNode);
        }else{
            insertRecur(node-&amp;gt;getRight(), key);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이진탐색트리 &lt;b&gt;삽입 연산의 시간복잡도&lt;/b&gt; 또한 탐색 연산과 동일하게 트리의 높이가 &lt;code&gt;h&lt;/code&gt; 일 때 &lt;code&gt;O(h)&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;p&gt;물론 삽입이라는 계산이 한번 추가되지만, &lt;code&gt;O(1)&lt;/code&gt; 수준이므로 무시한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.3 삭제&lt;/h3&gt;
&lt;p&gt;이진탐색트리에서 삭제는 기존 노드를 삭제하면서 이진탐색트리의 조건을 만족시켜야 하므로, 탐색과 삽입 연산보다 조금 복잡하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;삭제 시 발생할 수 있는 &lt;b&gt;경우의 수&lt;/b&gt;는 3가지이다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;삭제하려는 노드가 leaf 노드인 경우&lt;/li&gt;
&lt;li&gt;삭제하려는 노드가 왼쪽이나 오른쪽 자식노드 중 하나만 가지고 있는 경우&lt;/li&gt;
&lt;li&gt;삭제하려는 노드가 두 개의 자식노드를 모두 가지고 있는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫 번째 &quot;&lt;b&gt;삭제하려는 노드가 leaf 노드인 경우&lt;/b&gt;&quot; 해당 노드만 삭제하면 된다.&lt;/p&gt;
&lt;p&gt;즉 부모노드를 찾아서 부모노드에서 해당 link를 null로 만들어 연결을 끊으면 되고, 동적으로 할당된 노드였을 경우 메모리 반납까지 해주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;두 번째 &quot;&lt;b&gt;삭제하려는 노드가 하나의 자식노드만 가지는 경우&lt;/b&gt;&quot; 해당 노드의 부모노드와 자식노드를 연결해주면 된다.&lt;/p&gt;
&lt;p&gt;LinkedList에서의 삭제를 생각하면 이해가 쉬울 것이다.&lt;/p&gt;
&lt;p&gt;부모노드를 찾아서 부모노드에서 해당 link를 자식노드의 link로 연결시켜주면 된다.&lt;/p&gt;
&lt;p&gt;역시나 마찬가지로 동적으로 할당된 노드였을 경우 삭제 노드를 먼저 반납해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;마지막으로 &quot;&lt;b&gt;삭제하려는 노드가 두개의 자식노드를 모두 가지는 경우&lt;/b&gt;&quot; 조금 복잡하다.&lt;/p&gt;
&lt;p&gt;그림을 보며천천히 생각해 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;228&quot; width=&quot;299&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vdYa4/btqSgMXpHWe/1dnx8GWYJMaWCFxYbS30P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vdYa4/btqSgMXpHWe/1dnx8GWYJMaWCFxYbS30P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vdYa4/btqSgMXpHWe/1dnx8GWYJMaWCFxYbS30P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvdYa4%2FbtqSgMXpHWe%2F1dnx8GWYJMaWCFxYbS30P1%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;228&quot; width=&quot;299&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;18&lt;/code&gt; 노드를 삭제한다고 가정했을 때 노드가 삭제된 후 그 자리를 누가 채워야 할까?&lt;/p&gt;
&lt;p&gt;그림에서 유추했듯이 정답은 &lt;b&gt;왼쪽 서브트리에서 가장 큰 값&lt;/b&gt; 또는 &lt;b&gt;오른쪽 서브트리에서 가장 작은 값&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;이유는 단순하다.&lt;/p&gt;
&lt;p&gt;그래야만 이진탐색트리의 조건을 만족하며 트리의 변동성을 최소화할 수 있기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이진탐색트리를 중위 순회하면, 우리가 알다시피 &lt;code&gt;Left&lt;/code&gt; &amp;rarr; &lt;code&gt;Root&lt;/code&gt; &amp;rarr; &lt;code&gt;Right&lt;/code&gt; 순서로 순회한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Left&lt;/code&gt;는 왼쪽 서브트리에서 가장 오른쪽 아래의 node이며, &lt;code&gt;Right&lt;/code&gt;는 오른쪽 서브트리에서 가장 왼쪽 아래의 node이다.&lt;/p&gt;
&lt;p&gt;따라서 위 트리에서 삭제될 &lt;code&gt;18&lt;/code&gt; 노드의 이전 노드는 &lt;code&gt;12&lt;/code&gt; 노드이며, 이후 노드는 &lt;code&gt;22&lt;/code&gt; 노드이다.&lt;/p&gt;
&lt;p&gt;이들 중 하나를 삭제될 노드 자리에 채우면 삭제 이후 이진탐색트리의 형태가 유지되는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;삭제 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;// 삭제
void remove(Node&amp;lt;T&amp;gt;* parentNode, Node&amp;lt;T&amp;gt;* deleteNode){
    // Case1. 삭제하려는 node가 leaf 노드인 경우
    if(deleteNode-&amp;gt;isLeaf()){
        if(deleteNode == this-&amp;gt;getRoot()){     // 삭제하려는 node가 root인 경우
            this-&amp;gt;setRoot(nullptr);
        }else{
            if(parentNode-&amp;gt;getLeft() == deleteNode){        // parent의 왼쪽 자식노드 삭제
                parentNode-&amp;gt;setLeft(nullptr);
            }else if(parentNode-&amp;gt;getRight() == deleteNode){ // parent의 오른쪽 자식노드 삭제
                parentNode-&amp;gt;setRight(nullptr);
            }
        }
    }
    // Case2. 삭제하려는 node가 하나의 자식노드만 갖는 경우
    else if(deleteNode-&amp;gt;getLeft() == NULL || deleteNode-&amp;gt;getRight() == NULL){
        Node&amp;lt;T&amp;gt;* childNode = (deleteNode-&amp;gt;getLeft() != NULL) ? deleteNode-&amp;gt;getLeft() : deleteNode-&amp;gt;getRight();  // 삭제할 node의 유일한 자식노드

        if(deleteNode == this-&amp;gt;getRoot()){    // 삭제하려는 node가 root인 경우
            this-&amp;gt;setRoot(childNode);
        }else{
            if(parentNode-&amp;gt;getLeft() == deleteNode){        // parent의 왼쪽 자식노드에 childNode link
                parentNode-&amp;gt;setLeft(childNode);
            }else if(parentNode-&amp;gt;getRight() == deleteNode){ // parent의 오른쪽 자식노드 childNode link
                parentNode-&amp;gt;setRight(childNode);
            }
        }
    }
    // Case3. 삭제하려는 node가 2개의 자식노드를 모두 갖는 경우
    else{
        // 삭제하려는 노드의 오른쪽 서브트리에서 가장 작은 노드를 탐색하는 과정
        Node&amp;lt;T&amp;gt;* succp = deleteNode;
        Node&amp;lt;T&amp;gt;* succ = deleteNode-&amp;gt;getRight();
        while(succ-&amp;gt;getLeft() != NULL){
            succp = succ;
            succ = succ-&amp;gt;getLeft();
        }

        // 3-1. 후계자 노드의 부모와 후계자 노드의 자식 연결
        if(succp-&amp;gt;getLeft() == succ){
            succp-&amp;gt;setLeft(succ-&amp;gt;getRight());
        }else{  // 후계자 노드가 삭제할 노드의 바로 오른쪽 자식인 경우
            succp-&amp;gt;setRight(succ-&amp;gt;getRight());
        }

        // 3-2. 삭제할 노드의 data를 후계자노드의 data로 대체
        deleteNode-&amp;gt;setData(succ-&amp;gt;getData());

        // 3-3. 삭제할 노드를 후계자노드로 변경 (후계자노드를 delete 하기 위해)
        deleteNode = succ;
    }

    delete deleteNode;  // memory 반납
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;트리의 높이가 &lt;code&gt;h&lt;/code&gt; 이고 삭제 대상 노드의 레벨이 &lt;code&gt;d&lt;/code&gt; 일 때 &lt;code&gt;h-d&lt;/code&gt; 만큼의 비교 연산을 수행해야 한다.&lt;/p&gt;
&lt;p&gt;복사 및 삭제 과정의 연산은 상수시간으로 무시하면, 이진탐색트리 &lt;b&gt;삭제연산의 시간복잡도&lt;/b&gt; 또한 &lt;code&gt;O(h)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.4 이진탐색트리 연산의 시간복잡도&lt;/h3&gt;
&lt;p&gt;위에서 알아본 것과 같이 이진탐색트리의 탐색, 삽입, 삭제 연산의 시간복잡도는 모두 O(h)이다.&lt;/p&gt;
&lt;p&gt;h는 트리의 높이이며, 이전 글에서 아래와 같이 공부했었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;❝ n개의 노드를 가지는 이진트리에서 &lt;b&gt;최소 높이&lt;/b&gt;는 $&lt;span&gt;h &amp;ge; \lceil{log_2(n+1)}\rceil$&amp;nbsp;이며,&lt;/span&gt; &lt;b&gt;최대 높이&lt;/b&gt;는 &lt;code&gt;n&lt;/code&gt;이다. ❞&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;결과적으로 &lt;b&gt;최악의 경우&lt;/b&gt; (경사이진트리 모양) 이진탐색트리의 시간복잡도는 &lt;code&gt;O(n)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;b&gt;최선의 경우&lt;/b&gt; (포화이진트리 모양) 이진탐색트리의 시간복잡도는 &lt;code&gt;O(log₂n)&lt;/code&gt;이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.5 C++ 이진탐색트리 구현 (전체 소스코드)&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

template &amp;lt;typename T&amp;gt;
struct Node{
private:
    T data;
    Node* left;
    Node* right;
public:
    Node(T _data, Node* _left, Node* _right){
        data = _data;
        left = _left;
        right = _right;
    }
    ~Node(){}

    // getter/setter
    T getData(){
        return data;
    }
    void setData(T _data){
        data = _data;
    }
    Node* getLeft(){
        return left;
    }
    void setLeft(Node* _left){
        left = _left;
    }
    Node* getRight(){
        return right;
    }
    void setRight(Node* _right){
        right = _right;
    }

    // is leaf node?
    bool isLeaf(){
        return left==nullptr &amp;amp;&amp;amp; right==nullptr;
    }

    // Node 멤버함수로 순환탐색 구현 (ㅎㅎ)
    Node* search(T key){
        if(key == data)                         // key == 노드의 data
            return this;
        else if(key &amp;lt; data &amp;amp;&amp;amp; left != NULL)     // key &amp;lt; 노드의 data
            return left-&amp;gt;search(key);
        else if(key &amp;gt; data &amp;amp;&amp;amp; right != NULL)    // key &amp;gt; 노드의 data
            return left-&amp;gt;search(key);
        else                                    // 탐색이 실패한 경우
            return NULL;
    }
};

template &amp;lt;typename T&amp;gt;
class BinaryTree{
private:
    Node&amp;lt;T&amp;gt;* root;
public:
    BinaryTree(){
        root = nullptr;
    }
    ~BinaryTree(){}

    // set root node
    void setRoot(Node&amp;lt;T&amp;gt;* _root){
        root = _root;
    }
    // get root node
    Node&amp;lt;T&amp;gt;* getRoot(){
        return root;
    }

    // 전위 순회(Pre-order Traversal)
    void preorder(){
        cout &amp;lt;&amp;lt; &quot;Preorder  : [ &quot;;
        preorder(root);
        cout &amp;lt;&amp;lt; &quot;]&quot; &amp;lt;&amp;lt; endl;
    }
    void preorder(Node&amp;lt;T&amp;gt;* node){
        if(node != NULL){
            cout &amp;lt;&amp;lt; node-&amp;gt;getData() &amp;lt;&amp;lt; &quot; &quot;;
            preorder(node-&amp;gt;getLeft());
            preorder(node-&amp;gt;getRight());
        }
    }

    // 중위 순회(In-order Traversal)
    void inorder(){
        cout &amp;lt;&amp;lt; &quot;Inorder   : [ &quot;;
        inorder(root);
        cout &amp;lt;&amp;lt; &quot;]&quot; &amp;lt;&amp;lt; endl;
    }
    void inorder(Node&amp;lt;T&amp;gt;* node){
        if(node != NULL){
            inorder(node-&amp;gt;getLeft());
            cout &amp;lt;&amp;lt; node-&amp;gt;getData() &amp;lt;&amp;lt; &quot; &quot;;
            inorder(node-&amp;gt;getRight());
        }
    }

    // 후위 순회(Post-order Traversal)
    void postorder(){
        cout &amp;lt;&amp;lt; &quot;Postorder : [ &quot;;
        postorder(root);
        cout &amp;lt;&amp;lt; &quot;]&quot; &amp;lt;&amp;lt; endl;
    }
    void postorder(Node&amp;lt;T&amp;gt;* node){
        if(node != NULL){
            postorder(node-&amp;gt;getLeft());
            postorder(node-&amp;gt;getRight());
            cout &amp;lt;&amp;lt; node-&amp;gt;getData() &amp;lt;&amp;lt; &quot; &quot;;
        }
    }

    // 레벨 순회(Level-order Traversal)
    void levelorder(){
        cout &amp;lt;&amp;lt; &quot;Levelorder: [ &quot;;
        if(!isEmpty()){
            queue&amp;lt;Node&amp;lt;T&amp;gt;*&amp;gt; q;
            q.push(root);
            while(!q.empty()){
                Node&amp;lt;T&amp;gt;* node = q.front();
                q.pop();
                if(node != NULL){
                    cout &amp;lt;&amp;lt; node-&amp;gt;getData() &amp;lt;&amp;lt; &quot; &quot;;
                    q.push(node-&amp;gt;getLeft());
                    q.push(node-&amp;gt;getRight());
                }
            }
        }
        cout &amp;lt;&amp;lt; &quot;]&quot; &amp;lt;&amp;lt; endl;
    }

    // 전체 노드 개수 구하기
    int getCount(){
        return isEmpty() ? 0 : getCount(root);
    }
    int getCount(Node&amp;lt;T&amp;gt;* node){
        if(node == NULL) return 0;
        return 1 + getCount(node-&amp;gt;getLeft()) + getCount(node-&amp;gt;getRight());
    }

    // leaf 노드 개수 구하기
    int getLeafCount(){
        return isEmpty() ? 0 : getLeafCount(root);
    }
    int getLeafCount(Node&amp;lt;T&amp;gt;* node){
        if(node == NULL) return 0;
        if(node-&amp;gt;isLeaf()) return 1;
        else return getLeafCount(node-&amp;gt;getLeft()) + getLeafCount(node-&amp;gt;getRight());
    }

    // 트리 높이 구하기
    int getHeight(){
        return isEmpty() ? 0 : getHeight(root);
    }
    int getHeight(Node&amp;lt;T&amp;gt;* node){
        if(node == NULL) return 0;
        int lHeight = getHeight(node-&amp;gt;getLeft());   // 왼쪽 서브트리 높이 계산
        int rHeight = getHeight(node-&amp;gt;getRight());  // 오른쪽 서브트리 높이 계산
        return (lHeight &amp;gt; rHeight) ? lHeight + 1 : rHeight + 1;
    }

    bool isEmpty(){
        return root == nullptr;
    }

};

// 이진탐색트리 (이진트리 상속)
template &amp;lt;typename T&amp;gt;
class BinarySearchTree : public BinaryTree&amp;lt;T&amp;gt;{
private:
    // 탐색 (순환)
    Node&amp;lt;T&amp;gt;* searchRecur(Node&amp;lt;T&amp;gt;* node, T key){
        if(node == NULL) return NULL;

        if(key == node-&amp;gt;getData()) return node;     // key == root 노드의 data
        else if(key &amp;lt; node-&amp;gt;getData()) return searchRecur(node-&amp;gt;getLeft(), key);    // key &amp;lt; root 노드의 data
        else if(key &amp;gt; node-&amp;gt;getData()) return searchRecur(node-&amp;gt;getRight(), key);   // key &amp;gt; root 노드의 data
    }

    // 탐색 (반복)
    Node&amp;lt;T&amp;gt;* searchIter(Node&amp;lt;T&amp;gt;* node, T key){
        while(node != NULL){
            if(key == node-&amp;gt;getData()) return node;     // key == root 노드의 data
            else if(key &amp;lt; node-&amp;gt;getData()) node = node-&amp;gt;getLeft();   // key &amp;lt; root 노드의 data
            else if(key &amp;gt; node-&amp;gt;getData()) node = node-&amp;gt;getRight();  // key &amp;gt; root 노드의 data
        }
        return NULL;    // 탐색이 실패한 경우 NULL 반환
    }

    // 삽입 (순환)
    void insertRecur(Node&amp;lt;T&amp;gt;* node, T key){
        Node&amp;lt;T&amp;gt;* newNode = new Node&amp;lt;T&amp;gt;(key, nullptr, nullptr);  // 새로운 key를 가지는 node 생성

        if(key == node-&amp;gt;getData()){         // 트리에 이미 key값을 가지는 node가 있는 경우
            return;
        }else if(key &amp;lt; node-&amp;gt;getData()){    // key &amp;lt; root 노드의 data
            if(node-&amp;gt;getLeft() == NULL){
                node-&amp;gt;setLeft(newNode);
            }else{
                insertRecur(node-&amp;gt;getLeft(), key);
            }
        }else if(key &amp;gt; node-&amp;gt;getData()){    // key &amp;gt; root 노드의 data
            if(node-&amp;gt;getRight() == NULL){
                node-&amp;gt;setRight(newNode);
            }else{
                insertRecur(node-&amp;gt;getRight(), key);
            }
        }
    }

    // 삭제
    void remove(Node&amp;lt;T&amp;gt;* parentNode, Node&amp;lt;T&amp;gt;* deleteNode){
        // Case1. 삭제하려는 node가 leaf 노드인 경우
        if(deleteNode-&amp;gt;isLeaf()){
            if(deleteNode == this-&amp;gt;getRoot()){     // 삭제하려는 node가 root인 경우
                this-&amp;gt;setRoot(nullptr);
            }else{
                if(parentNode-&amp;gt;getLeft() == deleteNode){        // parent의 왼쪽 자식노드 삭제
                    parentNode-&amp;gt;setLeft(nullptr);
                }else if(parentNode-&amp;gt;getRight() == deleteNode){ // parent의 오른쪽 자식노드 삭제
                    parentNode-&amp;gt;setRight(nullptr);
                }
            }
        }
        // Case2. 삭제하려는 node가 하나의 자식노드만 갖는 경우
        else if(deleteNode-&amp;gt;getLeft() == NULL || deleteNode-&amp;gt;getRight() == NULL){
            Node&amp;lt;T&amp;gt;* childNode = (deleteNode-&amp;gt;getLeft() != NULL) ? deleteNode-&amp;gt;getLeft() : deleteNode-&amp;gt;getRight();  // 삭제할 node의 유일한 자식노드

            if(deleteNode == this-&amp;gt;getRoot()){    // 삭제하려는 node가 root인 경우
                this-&amp;gt;setRoot(childNode);
            }else{
                if(parentNode-&amp;gt;getLeft() == deleteNode){        // parent의 왼쪽 자식노드에 childNode link
                    parentNode-&amp;gt;setLeft(childNode);
                }else if(parentNode-&amp;gt;getRight() == deleteNode){ // parent의 오른쪽 자식노드 childNode link
                    parentNode-&amp;gt;setRight(childNode);
                }
            }
        }
        // Case3. 삭제하려는 node가 2개의 자식노드를 모두 갖는 경우
        else{
            // 삭제하려는 노드의 오른쪽 서브트리에서 가장 작은 노드를 탐색하는 과정
            Node&amp;lt;T&amp;gt;* succp = deleteNode;
            Node&amp;lt;T&amp;gt;* succ = deleteNode-&amp;gt;getRight();
            while(succ-&amp;gt;getLeft() != NULL){
                succp = succ;
                succ = succ-&amp;gt;getLeft();
            }

            // 3-1. 후계자 노드의 부모와 후계자 노드의 자식 연결
            if(succp-&amp;gt;getLeft() == succ){
                succp-&amp;gt;setLeft(succ-&amp;gt;getRight());
            }else{  // 후계자 노드가 삭제할 노드의 바로 오른쪽 자식인 경우
                succp-&amp;gt;setRight(succ-&amp;gt;getRight());
            }

            // 3-2. 삭제할 노드의 data를 후계자노드의 data로 대체
            deleteNode-&amp;gt;setData(succ-&amp;gt;getData());

            // 3-3. 삭제할 노드를 후계자노드로 변경 (후계자노드를 delete 하기 위해)
            deleteNode = succ;
        }

        delete deleteNode;  // memory 반납
    }

public:
    BinarySearchTree(){}
    ~BinarySearchTree(){}

    // 탐색
    Node&amp;lt;T&amp;gt;* search(T key){
        //return searchRecur(this-&amp;gt;getRoot(), key);
        return searchIter(this-&amp;gt;getRoot(), key);
    }

    // 삽입
    void insert(T key){
        if(this-&amp;gt;isEmpty()){
            Node&amp;lt;T&amp;gt;* newNode = new Node&amp;lt;T&amp;gt;(key, nullptr, nullptr);  // 새로운 key를 가지는 node 생성
            this-&amp;gt;setRoot(newNode);
        }else{
            insertRecur(this-&amp;gt;getRoot(), key);
        }
    }

    // 삭제
    void remove(T key){
        if(this-&amp;gt;isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Tree is Empty&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        // parentNode와 deleteNode를 찾는 과정
        Node&amp;lt;T&amp;gt;* parentNode = nullptr;          // 삭제하려는 node의 부모 node (최초 root의 부모노드는 없음)
        Node&amp;lt;T&amp;gt;* deleteNode = this-&amp;gt;getRoot();    // 삭제하려는 node
        while(deleteNode != NULL &amp;amp;&amp;amp; deleteNode-&amp;gt;getData() != key){
            parentNode = deleteNode;
            deleteNode = (key &amp;lt; parentNode-&amp;gt;getData()) ? parentNode-&amp;gt;getLeft() : parentNode-&amp;gt;getRight();
        }

        if(deleteNode == NULL){
            cout &amp;lt;&amp;lt; &quot;Key is not in Tree&quot; &amp;lt;&amp;lt; endl;
            return;
        }else{
            remove(parentNode, deleteNode);
        }

    }

};&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;int main(){
    BinarySearchTree&amp;lt;int&amp;gt; tree;

    // 10개 node 삽입
    tree.insert(35);
    tree.insert(18);
    tree.insert(7);
    tree.insert(26);
    tree.insert(12);
    tree.insert(3);
    tree.insert(68);
    tree.insert(22);
    tree.insert(30);
    tree.insert(99);

    // Calculate
    cout &amp;lt;&amp;lt; &quot;전체 노드개수 : &quot; &amp;lt;&amp;lt; tree.getCount() &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;단말 노드개수 : &quot; &amp;lt;&amp;lt; tree.getLeafCount() &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;이진트리 높이 : &quot; &amp;lt;&amp;lt; tree.getHeight() &amp;lt;&amp;lt; endl;

    // Traversal
    tree.preorder();
    tree.inorder();
    tree.postorder();
    tree.levelorder();

    // 삭제
    cout &amp;lt;&amp;lt; &quot;==== 노드 3 삭제 ====&quot; &amp;lt;&amp;lt; endl;
    tree.remove(3);
    tree.levelorder();
    cout &amp;lt;&amp;lt; &quot;==== 노드 68 삭제 ====&quot; &amp;lt;&amp;lt; endl;
    tree.remove(68);
    tree.levelorder();
    cout &amp;lt;&amp;lt; &quot;==== 노드 18 삭제 ====&quot; &amp;lt;&amp;lt; endl;
    tree.remove(18);
    tree.levelorder();
    cout &amp;lt;&amp;lt; &quot;==== 노드 35 삭제 ====&quot; &amp;lt;&amp;lt; endl;
    tree.remove(35);
    tree.levelorder();

    // Last Calculate
    cout &amp;lt;&amp;lt; &quot;전체 노드개수 : &quot; &amp;lt;&amp;lt; tree.getCount() &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;단말 노드개수 : &quot; &amp;lt;&amp;lt; tree.getLeafCount() &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;이진트리 높이 : &quot; &amp;lt;&amp;lt; tree.getHeight() &amp;lt;&amp;lt; endl;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-25__12.43.04.png&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;938&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rj939/btqSgNa0nHW/iYkmJksNTvKs5vMDJ4OYC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rj939/btqSgNa0nHW/iYkmJksNTvKs5vMDJ4OYC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rj939/btqSgNa0nHW/iYkmJksNTvKs5vMDJ4OYC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frj939%2FbtqSgNa0nHW%2FiYkmJksNTvKs5vMDJ4OYC0%2Fimg.png&quot; data-filename=&quot;_2020-11-25__12.43.04.png&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;938&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Data Structure</category>
      <category>Binary search tree</category>
      <category>BST</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/30</guid>
      <comments>https://suyeon96.tistory.com/30#entry30comment</comments>
      <pubDate>Mon, 4 Jan 2021 02:30:02 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 트리? + 이진 트리 (Binary Tree)</title>
      <link>https://suyeon96.tistory.com/29</link>
      <description>&lt;h2&gt;1. 트리&lt;/h2&gt;
&lt;h3&gt;1.1 트리란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;트리(Tree)&lt;/b&gt;란 계층적인 구조를 나타내는 자료구조이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;319&quot; width=&quot;200&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rmIKz/btqSdtXUccN/kaXeSEeK95sZukbveN3DF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rmIKz/btqSdtXUccN/kaXeSEeK95sZukbveN3DF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rmIKz/btqSdtXUccN/kaXeSEeK95sZukbveN3DF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrmIKz%2FbtqSdtXUccN%2FkaXeSEeK95sZukbveN3DF1%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;319&quot; width=&quot;200&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이전에 다룬 스택(Stack)과 큐(Queue)와 같은 선형 구조와 달리 트리는 비선형 구조이며, 나무를 거꾸로 그린 형태이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.2 트리 용어&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;binary_tree.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;351&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1cL1t/btqR9mruX36/OwDKZFkGdMkArPfrbunkAK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1cL1t/btqR9mruX36/OwDKZFkGdMkArPfrbunkAK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1cL1t/btqR9mruX36/OwDKZFkGdMkArPfrbunkAK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1cL1t%2FbtqR9mruX36%2FOwDKZFkGdMkArPfrbunkAK%2Fimg.jpg&quot; data-filename=&quot;binary_tree.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;351&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;노드(node)&lt;/b&gt;: 트리를 구성하는 기본 원소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;루트 노드(root node/root)&lt;/b&gt;: 트리에서 부모가 없는 최상위 노드, 트리의 시작점&lt;/li&gt;
&lt;li&gt;&lt;b&gt;부모 노드(parent node)&lt;/b&gt;: 루트 노드 방향으로 직접 연결된 노드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자식 노드(child node)&lt;/b&gt;: 루트 노드 반대방향으로 직접 연결된 노드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;형제 노드(siblings node)&lt;/b&gt;: 같은 부모 노드를 갖는 노드들&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리프 노드(leaf (node))/단말 노드(terminal node)&lt;/b&gt;: 자식이 없는 노드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;간선(edge)&lt;/b&gt;: 노드간의 연결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;레벨(level)&lt;/b&gt;: 루트 노드(level=1)부터 노드까지 연결된 링크 수의 합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;높이(height)&lt;/b&gt;: 가장 긴 루트 경로의 길이&lt;/li&gt;
&lt;li&gt;&lt;b&gt;차수(degree)&lt;/b&gt;: 각 노드의 자식의 개수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트리의 차수(degree of tree)&lt;/b&gt;: 트리의 최대 차수 = max[deg1, deg2, ..., degn]&lt;/li&gt;
&lt;li&gt;&lt;b&gt;경로(path)&lt;/b&gt;: 한 노드에서 다른 한 노드에 이르는 길 사이에 있는 노드들의 순서&lt;/li&gt;
&lt;li&gt;&lt;b&gt;길이(length)&lt;/b&gt;: 출발 노드에서 도착 노드까지 거치는 노드의 개수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;깊이(depth)&lt;/b&gt;: 루트 경로의 길이&lt;/li&gt;
&lt;li&gt;&lt;b&gt;크기(size)&lt;/b&gt;: 노드의 개수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;너비(width)&lt;/b&gt;: 가장 많은 노드를 갖고 있는 레벨의 크기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.3 트리 구현의 문제점&lt;/h3&gt;
&lt;p&gt;만약 하나의 노드가 여러개의 자식노드를 가지는 불규칙한 트리의 경우 node를 구현하기가 상당히 복잡해진다.&lt;/p&gt;
&lt;p&gt;이러한 이유로 실제로는 하나의 노드가 2개의 자식노드만 가지는 &lt;b&gt;이진트리&lt;/b&gt;가 많이 사용된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. 이진트리&lt;/h2&gt;
&lt;h3&gt;2.1 이진트리란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;이진트리(Binary Tree)&lt;/b&gt;란 모든 노드의 차수가 최대 2인 형태의 트리이다.&lt;/p&gt;
&lt;p&gt;서브트리는 &lt;b&gt;공집합일 수&lt;/b&gt; 있으며 반드시 &lt;b&gt;왼쪽 서브트리와 오른쪽 서브트리가 서로 구별&lt;/b&gt;되어야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;binaryTreeSimple.png&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;235&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UBglJ/btqSdubrdZx/5upI999Ee9voqRrYV4iZjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UBglJ/btqSdubrdZx/5upI999Ee9voqRrYV4iZjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UBglJ/btqSdubrdZx/5upI999Ee9voqRrYV4iZjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUBglJ%2FbtqSdubrdZx%2F5upI999Ee9voqRrYV4iZjK%2Fimg.png&quot; data-filename=&quot;binaryTreeSimple.png&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;235&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 이진트리 ADT&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;객체&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;노드와 간선의 집합&lt;/li&gt;
&lt;li&gt;노드는 공집합이거나 공집합이 아닌 경우 루트노드와 왼쪽 서브트리, 오른쪽 서브트리로 구성됨&lt;/li&gt;
&lt;li&gt;모든 서브트리도 이진트리임&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;연산&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create() : 이진트리 생성&lt;/li&gt;
&lt;li&gt;insertNode(n) : 이진트리에 노드 n 삽입&lt;/li&gt;
&lt;li&gt;deleteNode(n) : 이진트리에서 노드 n 삭제&lt;/li&gt;
&lt;li&gt;isEmpty() : 이진트리가 공백 상태인지 확인&lt;/li&gt;
&lt;li&gt;getRoot() : 이진트리의 루프노드 반환&lt;/li&gt;
&lt;li&gt;getCount() : 이진트리의 노드 개수 반환&lt;/li&gt;
&lt;li&gt;getHeight() : 이진트리의 높이 반환&lt;/li&gt;
&lt;li&gt;display() : 이진트리의 내용 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.3 일반트리 &amp;rarr; 이진트리 변환&lt;/h3&gt;
&lt;p&gt;그렇다면 자식노드의 개수가 2개 이상인 트리는 구현이 불가능한 것일까?&lt;/p&gt;
&lt;p&gt;아니다. 대부분의 트리는 이진트리로 변환이 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들어 아래의 그림에서 왼쪽 트리는 오른&lt;span style=&quot;color: #333333;&quot;&gt;쪽 이진트리로 변환이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;302&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k0Jsj/btqSgNBMRcV/ybGkLRC0FlKCD2k1br9xf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k0Jsj/btqSgNBMRcV/ybGkLRC0FlKCD2k1br9xf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k0Jsj/btqSgNBMRcV/ybGkLRC0FlKCD2k1br9xf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk0Jsj%2FbtqSgNBMRcV%2FybGkLRC0FlKCD2k1br9xf0%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;302&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이해가 되는가?&lt;/p&gt;
&lt;p&gt;일반트리에서 첫 번째 자식노드를 이진트리에서는 왼쪽 서브트리에 위치시킨다.&lt;/p&gt;
&lt;p&gt;나머지 형제노드는 이진트리에서 오른쪽 서브트리에 위치된다.&lt;/p&gt;
&lt;p&gt;위와 같은 방법을 통해 이진트리로 변환하여 구현하면 보다 간단하게 구현이 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.4 이진트리 종류&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;포화 이진트리 (Full Binary Tree)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;155&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNeJ3P/btqSgMQqh1L/yjEUKH5OwWQQKPnpIjBKT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNeJ3P/btqSgMQqh1L/yjEUKH5OwWQQKPnpIjBKT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNeJ3P/btqSgMQqh1L/yjEUKH5OwWQQKPnpIjBKT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNeJ3P%2FbtqSgMQqh1L%2FyjEUKH5OwWQQKPnpIjBKT0%2Fimg.png&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;155&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;트리의 각 레벨에 노드가 완전히 채워져있는 이진트리를 뜻한다.&lt;/p&gt;
&lt;p&gt;모든 leaf 노드는 같은 level에 있어야하며, 마지막 level의 노드의 개수는 최대가 되어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;완전 이진트리 (Complete Binary Tree)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;204&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQhPZI/btqSsShIkdA/wEr3J0JZNfUyp3SDLEt3F0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQhPZI/btqSsShIkdA/wEr3J0JZNfUyp3SDLEt3F0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQhPZI/btqSsShIkdA/wEr3J0JZNfUyp3SDLEt3F0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQhPZI%2FbtqSsShIkdA%2FwEr3J0JZNfUyp3SDLEt3F0%2Fimg.png&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;204&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;마지막 level을 제외한 모든 level의 노드가 완전히 채워져 있으며, 마지막 level의 노드들은 왼쪽부터 순차적으로 채워져 있는 이진트리를 뜻한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.5 이진트리의 성질&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;이진트리에서의 edge 개수&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이진트리에서 루트 노드를 제외하고 모든 노드가 하나의 부모 노드를 가지고 있다.&lt;br /&gt;&amp;there4; n개의 노드의 이진트리에서 &lt;b&gt;간선(edge)의 개수&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;n-1&lt;/code&gt;개이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;같은 높이의 이진트리에서 노드의 최소&amp;middot;최대 개수&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;234&quot; width=&quot;482&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckCxnY/btqR6wgQ6wu/z0MD8wAYk7sclCkr9TiX4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckCxnY/btqR6wgQ6wu/z0MD8wAYk7sclCkr9TiX4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckCxnY/btqR6wgQ6wu/z0MD8wAYk7sclCkr9TiX4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckCxnY%2FbtqR6wgQ6wu%2Fz0MD8wAYk7sclCkr9TiX4k%2Fimg.png&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;234&quot; width=&quot;482&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;br /&gt;또한 하나의 노드는 최대 2개의 자식을 가질 수 있으므로 level $i$에서의 노드의 최대 개수는 $2^{i-1}$이다.&lt;br /&gt;즉 높이가 h인 이진트리의 최대 노드 개수는이다.&lt;br /&gt;&amp;there4; 높이가 h인 이진트리에서 &lt;b&gt;최소 노드의 개수&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;h&lt;/code&gt;개이며,&lt;b&gt;최대 노드의 개수&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 $\sum_{i=1}^{h}{2^{i-1}} = 2^h-1$&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;개이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;노드의 개수가 동일한 이진트리의 최소&amp;middot;최대 높이&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;313&quot; width=&quot;490&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4sEeB/btqSsShIswZ/laYBpgHRFOKZ2KAKscFHR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4sEeB/btqSsShIswZ/laYBpgHRFOKZ2KAKscFHR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4sEeB/btqSsShIswZ/laYBpgHRFOKZ2KAKscFHR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4sEeB%2FbtqSsShIswZ%2FlaYBpgHRFOKZ2KAKscFHR0%2Fimg.png&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;313&quot; width=&quot;490&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&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;하나의 레벨에서 최소한 하나의 노드는 있어야 하므로 최대 높이는 n을 넘을 수 없다.&lt;br /&gt;또한 $n &amp;le; 2^h - 1$ 이므로 $h &amp;ge; log_2(n+1)$ 이다. (h가 정수이므로 올림 처리를 해야 함)&lt;br /&gt;&amp;there4; n개의 노드를 가지는 이진트리에서 &lt;b&gt;최소 높이&lt;/b&gt;는 $h &amp;ge; \lceil{log_2(n+1)}\rceil$ &lt;span style=&quot;color: #333333;&quot;&gt;이며,&amp;nbsp;&lt;/span&gt;&lt;b&gt;최대 높이&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.6 이진트리 구현 (Array? Link?)&lt;/h3&gt;
&lt;p&gt;아래와 같이 &lt;b&gt;배열&lt;/b&gt;을 이용해 이진트리를 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;288&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beqrih/btqSxCMoPq3/kxqE57qfEG6Z2aaSRvX4Qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beqrih/btqSxCMoPq3/kxqE57qfEG6Z2aaSRvX4Qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beqrih/btqSxCMoPq3/kxqE57qfEG6Z2aaSRvX4Qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbeqrih%2FbtqSxCMoPq3%2FkxqE57qfEG6Z2aaSRvX4Qk%2Fimg.png&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;288&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;특정한 상황에서 완전 이진트리 또는 포화 이진트리의 경우 배열을 이용하면 효과적으로 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;하지만 일반적으로 배열로 이진트리를 구현하는 경우 메모리 낭비가 심할뿐더러 배열에 크기에 따라 트리의 높이가 제한된다.&lt;/p&gt;
&lt;p&gt;따라서 대부분의 경우 Link를 이용하여 이진트리를 구현한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Link&lt;/b&gt;를 이용하여 이진트리를 구현하는 방법은 LinkedList를 구현하는 방법과 유사하다.&lt;/p&gt;
&lt;p&gt;각 노드는 data 속성과 2개의 Link 속성을 가진다.&lt;/p&gt;
&lt;p&gt;여기서 Link 속성은 각각 왼쪽 서브트리와 오른쪽 서브트리의 노드 주소를 가리키는 2개의 포인터 변수를 뜻한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;137&quot; width=&quot;250&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X5CUF/btqSjB2aEzg/rPOBuWWNVdkxQQCfMZlIJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X5CUF/btqSjB2aEzg/rPOBuWWNVdkxQQCfMZlIJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X5CUF/btqSjB2aEzg/rPOBuWWNVdkxQQCfMZlIJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX5CUF%2FbtqSjB2aEzg%2FrPOBuWWNVdkxQQCfMZlIJ0%2Fimg.png&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;137&quot; width=&quot;250&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서 한 노드를 root 노드로 지정하면, 노드간의 Link를 통해 이진트리가 구성되는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;284&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pNB64/btqSxA17Gyy/6CHZ5QwY52kLLW8d6bt1Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pNB64/btqSxA17Gyy/6CHZ5QwY52kLLW8d6bt1Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pNB64/btqSxA17Gyy/6CHZ5QwY52kLLW8d6bt1Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpNB64%2FbtqSxA17Gyy%2F6CHZ5QwY52kLLW8d6bt1Uk%2Fimg.png&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;284&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.7 Link를 이용한 이진트리 구현 (C++)&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

template &amp;lt;typename T&amp;gt;
struct Node{
private:
    T data;
    Node* left;
    Node* right;
public:
    Node(T _data, Node* _left, Node* _right){
        data = _data;
        left = _left;
        right = _right;
    }
    ~Node(){}

    // getter/setter
    T getData(){
        return data;
    }
    void setData(T _data){
        data = _data;
    }
    Node* getLeft(){
        return left;
    }
    void setLeft(Node* _left){
        left = _left;
    }
    Node* getRight(){
        return right;
    }
    void setRight(Node* _right){
        right = _right;
    }

    // is leaf node?
    bool isLeaf(){
        return left==nullptr &amp;amp;&amp;amp; right==nullptr;
    }
};

template &amp;lt;typename T&amp;gt;
class BinaryTree{
private:
    Node&amp;lt;T&amp;gt;* root;
public:
    BinaryTree(){
        root = nullptr;
    }
    ~BinaryTree(){}

    void setRoot(Node&amp;lt;T&amp;gt;* _root){
        root = _root;
    }

    Node&amp;lt;T&amp;gt;* getRoot(){
        return root;
    }

    bool isEmpty(){
        return root == nullptr;
    }

};

int main(){
    BinaryTree&amp;lt;char&amp;gt; tree;

    // Create Node
    Node&amp;lt;char&amp;gt; *d = new Node&amp;lt;char&amp;gt;('D', nullptr, nullptr );
    Node&amp;lt;char&amp;gt; *e = new Node&amp;lt;char&amp;gt;('E', nullptr, nullptr );
    Node&amp;lt;char&amp;gt; *b = new Node&amp;lt;char&amp;gt;('B', d, e );
    Node&amp;lt;char&amp;gt; *f = new Node&amp;lt;char&amp;gt;('F', nullptr, nullptr );
    Node&amp;lt;char&amp;gt; *c = new Node&amp;lt;char&amp;gt;('C', f, nullptr );
    Node&amp;lt;char&amp;gt; *a = new Node&amp;lt;char&amp;gt;('A', b, c );

    // Create Tree
    tree.setRoot(a);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;221&quot; width=&quot;160&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A8uV2/btqSduWOTLW/9YvXv3xiTMh6Rqs6saRxM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A8uV2/btqSduWOTLW/9YvXv3xiTMh6Rqs6saRxM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A8uV2/btqSduWOTLW/9YvXv3xiTMh6Rqs6saRxM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA8uV2%2FbtqSduWOTLW%2F9YvXv3xiTMh6Rqs6saRxM0%2Fimg.png&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;221&quot; width=&quot;160&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;main()&lt;/code&gt; 에서 위 그림과 같은 이진트리를 생성하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.8 이진트리 순회&lt;/h3&gt;
&lt;p&gt;트리는 선형 구조의 자료구조가 아니기 때문에 노드들을 방문할 규칙을 선택해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;표준적인 방법에는 전위, 중위, 후위 순회 방법이 있으며 재귀호출을 통해 이를 구현한다.&lt;/p&gt;
&lt;p&gt;대부분의 경우 위 3가지 방법으로 트리를 순회하며 문제를 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;하지만 이외의 경우 개발자는 각각의 상황에 맞게 트리를 순회하는 알고리즘을 작성하여 이용하면 된다. (레벨 순회 등)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;전위 순회(Pre-order Traversal)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;루트 &amp;rarr; 왼쪽 서브트리 &amp;rarr; 오른쪽 서브트리 순서로 방문하는 순회 방법&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;183&quot; data-filename=&quot;Untitled 10.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8dtaw/btqSpHgtpQo/IF7sz63B0KZmWMG2afysQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8dtaw/btqSpHgtpQo/IF7sz63B0KZmWMG2afysQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8dtaw/btqSpHgtpQo/IF7sz63B0KZmWMG2afysQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8dtaw%2FbtqSpHgtpQo%2FIF7sz63B0KZmWMG2afysQ0%2Fimg.png&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;183&quot; data-filename=&quot;Untitled 10.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;void preorder(Node&amp;lt;T&amp;gt;* node){
    if(node != NULL){
        cout &amp;lt;&amp;lt; node-&amp;gt;getData() &amp;lt;&amp;lt; &quot; &quot;;
        preorder(node-&amp;gt;getLeft());
        preorder(node-&amp;gt;getRight());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;중위 순회(In-order Traversal)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;왼쪽 서브트리 &amp;rarr; 루트 &amp;rarr; 오른쪽 서브트리 순서로 방문하는 순회 방법&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 11.png&quot; data-origin-width=&quot;265&quot; data-origin-height=&quot;192&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2qjir/btqSdubrxSV/aqhUcIeKkix0pUfeXABg31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2qjir/btqSdubrxSV/aqhUcIeKkix0pUfeXABg31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2qjir/btqSdubrxSV/aqhUcIeKkix0pUfeXABg31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2qjir%2FbtqSdubrxSV%2FaqhUcIeKkix0pUfeXABg31%2Fimg.png&quot; data-filename=&quot;Untitled 11.png&quot; data-origin-width=&quot;265&quot; data-origin-height=&quot;192&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;void inorder(Node&amp;lt;T&amp;gt;* node){
    if(node != NULL){
        inorder(node-&amp;gt;getLeft());
        cout &amp;lt;&amp;lt; node-&amp;gt;getData() &amp;lt;&amp;lt; &quot; &quot;;
        inorder(node-&amp;gt;getRight());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;후위 순회(Post-order Traversal)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;왼쪽 서브트리 &amp;rarr; 오른쪽 서브트리 &amp;rarr; 루트 순서로 방문하는 순회 방법&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 12.png&quot; data-origin-width=&quot;262&quot; data-origin-height=&quot;189&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyYBU5/btqSmBAOfyn/QEeAbLjLMTgBs2UxC3SMM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyYBU5/btqSmBAOfyn/QEeAbLjLMTgBs2UxC3SMM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyYBU5/btqSmBAOfyn/QEeAbLjLMTgBs2UxC3SMM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyYBU5%2FbtqSmBAOfyn%2FQEeAbLjLMTgBs2UxC3SMM1%2Fimg.png&quot; data-filename=&quot;Untitled 12.png&quot; data-origin-width=&quot;262&quot; data-origin-height=&quot;189&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;void postorder(Node&amp;lt;T&amp;gt;* node){
    if(node != NULL){
        postorder(node-&amp;gt;getLeft());
        postorder(node-&amp;gt;getRight());
        cout &amp;lt;&amp;lt; node-&amp;gt;getData() &amp;lt;&amp;lt; &quot; &quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;레벨 순회(Level-order Traversal)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;레벨 순서로 방문하는 순회 방법&lt;/p&gt;
&lt;p&gt;너비 우선 순회(Breadth-First traversal)라고도 함&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 13.png&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;375&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg3gxK/btqSsQxpmsL/jOF8udrbMSHKun21RW7wo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg3gxK/btqSsQxpmsL/jOF8udrbMSHKun21RW7wo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg3gxK/btqSsQxpmsL/jOF8udrbMSHKun21RW7wo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg3gxK%2FbtqSsQxpmsL%2FjOF8udrbMSHKun21RW7wo1%2Fimg.png&quot; data-filename=&quot;Untitled 13.png&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;375&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;void levelorder(){
    cout &amp;lt;&amp;lt; &quot;Levelorder: [ &quot;;
    if(!isEmpty()){
        queue&amp;lt;Node&amp;lt;T&amp;gt;*&amp;gt; q;
        q.push(root);
        while(!q.empty()){
            Node&amp;lt;T&amp;gt;* node = q.front();
            q.pop();
            if(node != NULL){
                cout &amp;lt;&amp;lt; node-&amp;gt;getData() &amp;lt;&amp;lt; &quot; &quot;;
                q.push(node-&amp;gt;getLeft());
                q.push(node-&amp;gt;getRight());
            }
        }
    }
    cout &amp;lt;&amp;lt; &quot;]&quot; &amp;lt;&amp;lt; endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위에서 구현한 이진트리를 각각의 순회 방법&lt;span style=&quot;color: #333333;&quot;&gt;으로 출력한 결과이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;221&quot; width=&quot;150&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHsGPW/btqSa8T3rwJ/JNXfSQFtJj7k9HEHiUfrU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHsGPW/btqSa8T3rwJ/JNXfSQFtJj7k9HEHiUfrU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHsGPW/btqSa8T3rwJ/JNXfSQFtJj7k9HEHiUfrU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHsGPW%2FbtqSa8T3rwJ%2FJNXfSQFtJj7k9HEHiUfrU0%2Fimg.png&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;221&quot; width=&quot;150&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-17__9.28.06.png&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;364&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QnZKl/btqSgLRxVet/FogYGglyc3FwXP5mV0IXwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QnZKl/btqSgLRxVet/FogYGglyc3FwXP5mV0IXwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QnZKl/btqSgLRxVet/FogYGglyc3FwXP5mV0IXwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQnZKl%2FbtqSgLRxVet%2FFogYGglyc3FwXP5mV0IXwk%2Fimg.png&quot; data-filename=&quot;_2020-11-17__9.28.06.png&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;364&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Data Structure</category>
      <category>Binary Tree</category>
      <category>complete binary tree</category>
      <category>In-order Traversal</category>
      <category>Level-order Traversal</category>
      <category>Post-order Traversal</category>
      <category>Pre-order Traversal</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/29</guid>
      <comments>https://suyeon96.tistory.com/29#entry29comment</comments>
      <pubDate>Sun, 3 Jan 2021 18:45:32 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 순환 (Recursion) - 팩토리얼, 거듭제곱 계산, 피보나치 수열, 하노이의 탑, 영역 채색</title>
      <link>https://suyeon96.tistory.com/28</link>
      <description>&lt;h2&gt;1. 순환&lt;/h2&gt;
&lt;h3&gt;1.1 순환이란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;순환(Recursion)&lt;/b&gt;은 컴퓨터 과학에서 &lt;b&gt;재귀&lt;/b&gt;라고도 하며 자신을 정의할 때 자기 자신을 재참조하는 방법을 뜻한다.&lt;/p&gt;
&lt;p&gt;순환은 알고리즘을 해결하는데 사용되는 일종의 프로그래밍 기법이며, 트리 자료구조를 공부하기에 앞서 간단하게 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;392&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rdDeN/btqSa80t5Vx/DMncHZY9hYmEQO1bEVOJ0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rdDeN/btqSa80t5Vx/DMncHZY9hYmEQO1bEVOJ0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rdDeN/btqSa80t5Vx/DMncHZY9hYmEQO1bEVOJ0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrdDeN%2FbtqSa80t5Vx%2FDMncHZY9hYmEQO1bEVOJ0K%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;392&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.2 반복 vs 순환&lt;/h3&gt;
&lt;p&gt;프로그래밍 언어에서 같은 일을 반복하기 위한 방법에는 &lt;b&gt;반복(iteration)&lt;/b&gt;과 &lt;b&gt;순환(recursion)&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;반복&lt;/b&gt;이란 for나 while 등의 반복문을 사용하여 조건이 만족할 때까지 계속하여 반복시키는 방식이다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;순환&lt;/b&gt;은 위에서 말한대로 method가 자기 자신을 다시 호출하는 방식을 의미한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일반적으로 순환은 method 호출을 하게 되므로 반복에 비해 수행 속도가 떨어진다.&lt;/p&gt;
&lt;p&gt;하지만, 필요한 경우 순환을 사용하면 반복을 사용하는 것보다 훨씬 간단하고 효율적으로 문제를 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;각각의 경우에 따라 상황에 맞게 반복과 순환 중 선택을 하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. 팩토리얼&lt;/h2&gt;
&lt;h3&gt;2.1 팩토리얼의 정의&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;n! = n &amp;middot; (n-1) &amp;middot; (n-2) &amp;middot; (n-3) &amp;middot; &amp;middot;&amp;middot;&amp;middot; &amp;middot; 3 &amp;middot; 2 &amp;middot; 1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;b&gt;팩토리얼(Factorial)&lt;/b&gt;은 1에서부터 n까지의 범위에 있는 모든 정수를 곱하는 것을 의미한다.&lt;/p&gt;
&lt;p&gt;예를들어 5! = 5 &amp;middot; 4 &amp;middot; 3 &amp;middot; 2 &amp;middot; 1 = 120이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;양의 정수 n의 팩토리얼은 아래와 같이 정의할 수 &lt;span style=&quot;color: #333333;&quot;&gt;있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;335&quot; data-origin-height=&quot;95&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vgr4q/btqSpGuLESZ/P0youj3H31VEtZ2pz8WBK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vgr4q/btqSpGuLESZ/P0youj3H31VEtZ2pz8WBK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vgr4q/btqSpGuLESZ/P0youj3H31VEtZ2pz8WBK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVgr4q%2FbtqSpGuLESZ%2FP0youj3H31VEtZ2pz8WBK0%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;335&quot; data-origin-height=&quot;95&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위의 정의에서 &lt;code&gt;n!&lt;/code&gt;을 정의하는데 다시 &lt;code&gt;(n-1)!&lt;/code&gt;가 사용되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이러한 구조가 순환 구조인 것이다.&lt;/p&gt;
&lt;p&gt;이를 코드로 작성해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 팩토리얼 구현 (순환) &lt;code&gt;C++&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int factorial(int n){
    if(n==1) return 1;
    else return (n * factorial(n-1));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;line3&lt;/code&gt;에서 위의 공식처럼 다시 자기 자신(factorial)을 호출하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;3!을 구하는 경우 factorial(3)은 아래와 같이 동작된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;328&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB32vE/btqSmBHfAGF/35kpLbuxIJd0aKoakoYhhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB32vE/btqSmBHfAGF/35kpLbuxIJd0aKoakoYhhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB32vE/btqSmBHfAGF/35kpLbuxIJd0aKoakoYhhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB32vE%2FbtqSmBHfAGF%2F35kpLbuxIJd0aKoakoYhhk%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;328&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또한 순환 알고리즘에서는 반드시 순환 호출을 멈추는 조건이 필요하다.&lt;/p&gt;
&lt;p&gt;팩토리얼 코드에서는 &lt;code&gt;line2&lt;/code&gt;에서 n=1 일 때 순환을 멈추고 상수 1을 반환한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 순환호출을 멈추는 조건이 없다면, 시스템 오류가 발생할 때까지 자기자신을 무한히 호출하며 무한루프에 빠지게 될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. 거듭제곱 계산&lt;/h2&gt;
&lt;p&gt;&lt;b&gt;xⁿ&lt;/b&gt;을 구하기 위해서는 일반적으로 x를 n번 반복적으로 더하는 방법을 사용한다.&lt;/p&gt;
&lt;p&gt;하지만 순환을 이용해서 거듭제곱 계산을 할 수 도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;xⁿ&lt;/b&gt;을 구하는 코드를 반복과 순환호출 방법으로 각각 구현하고 비교해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.1 거듭제곱 계산 구현 (반복)&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;double power(double x, int n){
    double r = 1.0;
    for(int i=0; i&amp;lt;n; i++){
        r = r*x;
    }
    return r;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;n만큼 반복하며 x를 곱해가는 방식이다.&lt;/p&gt;
&lt;p&gt;당연히 시간복잡도는 &lt;code&gt;O(n)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.2 거듭제곱 계산 구현 (순환호출)&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;double power(double x, int n){
    if(n==0) return 1.0;
    else if ((n%2)==0)             // n이 짝수인 경우
        return power(x*x, n/2);
    else                           // n이 홀수인 경우
        return x * power(x*x, (n-1)/2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;n이 짝수인 경우 &lt;code&gt;x&amp;sup2;&lt;/code&gt;을 먼저 계산한 후 이 값을 &lt;code&gt;n/2&lt;/code&gt;로 제곱한다.&lt;/p&gt;
&lt;p&gt;n이 홀수인 경우 &lt;code&gt;x&amp;sup2;&lt;/code&gt;을 먼저 계산한 후 이 값을 &lt;code&gt;(n-1)/2&lt;/code&gt;로 제곱하고 x를 한 번 더 곱해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2&amp;sup1;⁰을 계산하는 경우 아래와 같이 동작한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;318&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rsf0s/btqR3R6oK6k/Qb1zstouUQTu2Z8sKSeIa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rsf0s/btqR3R6oK6k/Qb1zstouUQTu2Z8sKSeIa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rsf0s/btqR3R6oK6k/Qb1zstouUQTu2Z8sKSeIa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frsf0s%2FbtqR3R6oK6k%2FQb1zstouUQTu2Z8sKSeIa1%2Fimg.png&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;318&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한번 순환할 때마다 반복 횟수가 절반씩(n/2) 줄어드는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;이를 시간복잡도로 나타내면 &lt;code&gt;O(log₂n)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.3 정리&lt;/h3&gt;
&lt;p&gt;반복을 사용하여 거듭제곱을 계산하는 것보다, 순환호출을 사용하여 거듭제곱을 계산하는 경우 시간복잡도 측면에서 더욱 효율적임을 알 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;반복&lt;/b&gt; &lt;code&gt;O(n)&lt;/code&gt; &amp;gt; &lt;b&gt;순환호출&lt;/b&gt; &lt;code&gt;O(log₂n)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;4. 피보나치 수열 계산&lt;/h2&gt;
&lt;h3&gt;4.1 피보나치 수열 정의&lt;/h3&gt;
&lt;p&gt;수학에서 &lt;b&gt;피보나치 수(Fibonacci numbers)&lt;/b&gt;는 첫째 및 둘째 항이 1이며 그 뒤의 모든 항은 바로 앞 두 항의 합인 수열이다.&lt;/p&gt;
&lt;p&gt;편의상 0번째 항을 0으로 두기도 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;피보나치 수 ${\displaystyle F_{n}}$을 정의하면 다&lt;span style=&quot;color: #333333;&quot;&gt;음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;76&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nLgRm/btqR3RFjYOE/m8SsvcwvZkNUX4jprYzgh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nLgRm/btqR3RFjYOE/m8SsvcwvZkNUX4jprYzgh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nLgRm/btqR3RFjYOE/m8SsvcwvZkNUX4jprYzgh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnLgRm%2FbtqR3RFjYOE%2Fm8SsvcwvZkNUX4jprYzgh0%2Fimg.png&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;76&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위 점화식을 보아하니 피보나치 수열 또한 순환호출을 이용하여 구현이 가능한 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번에는 피보나치 수열을 구하는 코드를 반복과 순환호출 방법으로 각각 구현하고 비교해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.2 피보나치 수열 계산 구현 (반복)&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int fibo(int n){
    if(n&amp;lt;2) return n;
    else{
        int tmp, current=1, last=0;
        for(int i=2; i&amp;lt;=n; i++){
            tmp = current;
            current += last;
            last = tmp;
        }
        return current;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2~n만큼 반복하며 순차적으로 두 항의 수를 더해나가는 방법이다.&lt;/p&gt;
&lt;p&gt;당연히 시간복잡도는 &lt;code&gt;O(n)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.3 피보나치 수열 계산 구현 (순환호출)&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int fibo(int n){
    if(n==0) return 0;
    if(n==1) return 1;
    else return (fibo(n-1) + fibo(n-2));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;반복문을 이용하여 구현한 코드에 비해 단순하고 이해하기 쉽게 구현되었다.&lt;/p&gt;
&lt;p&gt;하지만 실제로는 매우 비효율적이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를들어 fibo(6)을 계산하는 경우 아래와 같이 동작한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;267&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UE2qE/btqSbaRx93y/xdUv2vDg59fu4Kkl5KnJXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UE2qE/btqSbaRx93y/xdUv2vDg59fu4Kkl5KnJXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UE2qE/btqSbaRx93y/xdUv2vDg59fu4Kkl5KnJXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUE2qE%2FbtqSbaRx93y%2FxdUv2vDg59fu4Kkl5KnJXk%2Fimg.png&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;267&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;fibo(6)를 호출하면 fibo(4)가 2번 호출된다. fibo(3)은 3번 호출되며 fibo(2)는 5번 호출된다.&lt;/p&gt;
&lt;p&gt;fibo(6)을 구하기 위해 fibo() 함수가 총 25번이나 호출되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;순환 단계가 깊어질수록 이러한 현상은 더욱 심해진다.&lt;/p&gt;
&lt;p&gt;이를 시간복잡도로 나타내면 &lt;code&gt;O(2ⁿ)&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4.4 정리&lt;/h3&gt;
&lt;p&gt;피보나치 수열을 계산하는 경우 순환호출을 사용하는 것보다 반복으로 문제를 해결하는 것이 훨씬 효율적이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;반복&lt;/b&gt; &lt;code&gt;O(n)&lt;/code&gt; &amp;lt; &lt;b&gt;순환호출&lt;/b&gt; &lt;code&gt;O(2ⁿ)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇듯 문제에 따라 반복 또는 순환호출 중 적절한 방법을 선택하여 사용해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;5. 하노이 탑&lt;/h2&gt;
&lt;h3&gt;5.1 하노이 탑이란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;하노이의 탑(Tower of Hanoi)&lt;/b&gt;은 퍼즐의 일종이다. 세 개의 기둥과 이 기둥에 꽂을 수 있는 크기가 다양한 원판들이 있고, 퍼즐을 시작하기 전에는 한 기둥에 원판들이 작은 것이 위에 있도록 순서대로 쌓여 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;164&quot; width=&quot;340&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tv7T0/btqSgLRd9a6/MzZRkHDAQ5yrSGz1kFxBiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tv7T0/btqSgLRd9a6/MzZRkHDAQ5yrSGz1kFxBiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tv7T0/btqSgLRd9a6/MzZRkHDAQ5yrSGz1kFxBiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftv7T0%2FbtqSgLRd9a6%2FMzZRkHDAQ5yrSGz1kFxBiK%2Fimg.png&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;164&quot; width=&quot;340&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;게임의 목적은 다음 조건을 만족시키면서, 한 기둥에 꽂힌 원판들을 그 순서 그대로 다른 기둥으로 옮겨서 다시 쌓는 것이다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;
&lt;p&gt;1. 한 번에 하나의 원판만 이동할 수 있다.&lt;br /&gt;2. 맨 위에 있는 원판만 이동할 수 있다.&lt;br /&gt;3. 큰 원판이 작은 원판 위에 있어서는 안 된다.&lt;br /&gt;4. 중간의 막대를 임시적으로 이용할 수 있으나 앞의 조건들을 지켜야한다.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;하노이의 탑 이동순서&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Tower_of_Hanoi_4.gif&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;125&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbCleA/btqR6vIFaeE/uPbyhgXK1U2StOC0p8ual1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbCleA/btqR6vIFaeE/uPbyhgXK1U2StOC0p8ual1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbCleA/btqR6vIFaeE/uPbyhgXK1U2StOC0p8ual1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cbCleA/btqR6vIFaeE/uPbyhgXK1U2StOC0p8ual1/img.gif&quot; data-filename=&quot;Tower_of_Hanoi_4.gif&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;125&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;5.2 하노이의 탑 구현&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

void hanoi(int n, char from, char tmp, char to){
    if(n==1){
        cout &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &quot;: &quot; &amp;lt;&amp;lt; from &amp;lt;&amp;lt; &quot; -&amp;gt; &quot; &amp;lt;&amp;lt; to &amp;lt;&amp;lt; endl;
    }else{
        hanoi(n-1, from, to, tmp);                          // A의 맨 밑을 제외한 나머지 원판들을 B로 옮김
        cout &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &quot;: &quot; &amp;lt;&amp;lt; from &amp;lt;&amp;lt; &quot; -&amp;gt; &quot; &amp;lt;&amp;lt; to &amp;lt;&amp;lt; endl;  // A에 남은 한 개의 원판(맨 밑)을 C로 옮김
        hanoi(n-1, tmp, from, to);                          // B의 원판들을 C로 옮김
    }
}

int main(){
    hanoi(4, 'A', 'B', 'C');
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-10__5.52.56.png&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;808&quot; width=&quot;698&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cbews/btqR9mraIFe/2mo0VgukoycP4fD3YzNM01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cbews/btqR9mraIFe/2mo0VgukoycP4fD3YzNM01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cbews/btqR9mraIFe/2mo0VgukoycP4fD3YzNM01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCbews%2FbtqR9mraIFe%2F2mo0VgukoycP4fD3YzNM01%2Fimg.png&quot; data-filename=&quot;_2020-11-10__5.52.56.png&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;808&quot; width=&quot;698&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;6. 영역채색&lt;/h2&gt;
&lt;h3&gt;6.1 영역채색이란?&lt;/h3&gt;
&lt;p&gt;영상처리 분야에서 &lt;b&gt;영역 채색(Blob coloring)&lt;/b&gt; 또는 &lt;b&gt;연결화소 분석법(Connected Component Labeling)&lt;/b&gt;이라 불리는 알고리즘은 BW 이미지에서 연결된 객체를 찾는 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;6.2 영역채색 구현&lt;/h3&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;

#define WIDTH 20
#define HEIGHT 9

// 근접 4방향 화소가 이어져있는지 판단하여 labeling
void labelComponent(unsigned int img[HEIGHT][WIDTH], int x, int y, int label){
    if(x&amp;lt;0 || y&amp;lt;0 || x&amp;gt;=WIDTH || y&amp;gt;=HEIGHT){    // 이미지 영역 밖인 경우 return
        return;
    }

    if (img[y][x] == 9){    // labeling 처리가 안된 black 픽셀인 경우
        img[y][x] = label;  // 해당 픽셀에 label값 부여

        // (4방향 연결) 좌,상,우,하 방향으로 순환호출
        labelComponent(img, x-1, y, label);
        labelComponent(img, x, y-1, label);
        labelComponent(img, x+1, y, label);
        labelComponent(img, x, y+1, label);
    }
}

// BW 이미지 영역채색
void blobColoring(unsigned int img[HEIGHT][WIDTH]){
    int label = 1;  // label 초기화

    // [0,0] 화소부터 모든 화소에 대해 차례대로 loop 돌면서 labeling 처리가 안된 화소인 경우 labelComponent() 호출
    for(int y=0; y&amp;lt;HEIGHT; y++){
        for(int x=0; x&amp;lt;WIDTH; x++){
            if(img[y][x] == 9){
                labelComponent(img, x, y, label++);
            }
        }
    }
}

// 이미지 출력
void printImage(unsigned int img[HEIGHT][WIDTH]){
    for(int y=0; y&amp;lt;HEIGHT; y++){
        for(int x=0; x&amp;lt;WIDTH; x++){
            if(img[y][x] == 0){
                cout &amp;lt;&amp;lt; &quot;.&quot;;
            }else{
                cout &amp;lt;&amp;lt; img[y][x];
            }
        }
        cout &amp;lt;&amp;lt; endl;
    }
    cout &amp;lt;&amp;lt; endl;
}

int main(){
    unsigned int image[HEIGHT][WIDTH] = {0,0,0,0,0,0,9,0,0,0,0,9,9,9,9,9,0,0,9,9,
                                         9,9,9,9,9,0,9,0,0,0,0,0,0,0,0,9,0,0,9,9,
                                         0,0,9,0,0,0,9,0,0,0,0,9,9,9,9,9,0,0,9,9,
                                         0,9,9,9,0,0,9,9,9,0,0,9,0,0,0,0,0,0,9,9,
                                         0,9,0,9,0,0,9,0,0,0,0,9,9,9,9,9,0,0,9,9,
                                         9,9,0,9,9,0,9,0,0,0,0,0,0,0,0,0,0,0,9,9,
                                         9,0,0,0,9,0,9,0,0,0,0,0,9,0,9,0,0,0,0,0,
                                         9,0,0,0,9,0,9,0,0,0,0,0,9,0,9,0,0,0,9,9,
                                         0,0,0,0,0,0,9,0,0,0,0,9,9,9,9,9,0,0,9,9};

    cout &amp;lt;&amp;lt; &quot;&amp;lt;Original Image&amp;gt;&quot; &amp;lt;&amp;lt; endl;
    printImage(image);
    cout &amp;lt;&amp;lt; &quot;&amp;lt;Labelled Image&amp;gt;&quot; &amp;lt;&amp;lt; endl;
    blobColoring(image);
    printImage(image);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-10__8.09.59.png&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;1104&quot; width=&quot;701&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwVZUC/btqR6vvatzc/rfdMqhmNjU6ikFMrbycsnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwVZUC/btqR6vvatzc/rfdMqhmNjU6ikFMrbycsnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwVZUC/btqR6vvatzc/rfdMqhmNjU6ikFMrbycsnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwVZUC%2FbtqR6vvatzc%2FrfdMqhmNjU6ikFMrbycsnK%2Fimg.png&quot; data-filename=&quot;_2020-11-10__8.09.59.png&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;1104&quot; width=&quot;701&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이미지의 [0,0] 위치에서부터 차례대로 loop를 돌며 labeling 되지않은 검은색 pixel(9)을 마주치면 &lt;code&gt;labelComponent(x, y)&lt;/code&gt;를 호출한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;labelComponent(x, y)&lt;/code&gt;에서는 (x, y) 픽셀이 labeling 되지않은 검은색 pixel(9)이면 label값을 부여하고 인접한 4방향의 픽셀값으로 &lt;code&gt;labelComponent(x, y)&lt;/code&gt;를 순환호출한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서 하나의 component를 만나는 경우 해당 component의 pixel은 전부 라벨링이 되는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Data Structure</category>
      <category>Connected Component Labeling</category>
      <category>factorial</category>
      <category>fibonacci</category>
      <category>iteration</category>
      <category>Power</category>
      <category>recursion</category>
      <category>Tower of Hanoi</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/28</guid>
      <comments>https://suyeon96.tistory.com/28#entry28comment</comments>
      <pubDate>Sat, 2 Jan 2021 23:17:59 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 리스트 (ArrayList vs LinkedList)</title>
      <link>https://suyeon96.tistory.com/27</link>
      <description>&lt;h2&gt;1. 리스트&lt;/h2&gt;
&lt;h3&gt;1.1 리스트란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;리스트(List)&lt;/b&gt;란 앞에서 공부한 배열, 스택, 큐 등과 같은 &lt;b&gt;선형 자료구조&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇다면 이들과 리스트의 차이점은 무엇일까?&lt;/p&gt;
&lt;p&gt;스택과 큐의 경우 자료의 접근이 전단(front) 혹은 후단(rear)에 제한되어 있다.&lt;/p&gt;
&lt;p&gt;하지만 리스트는 이러한 제한이 없다. 즉, &lt;b&gt;임의의 위치에 있는 항목에 접근이나 연산이 가능&lt;/b&gt;하다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.2 배열 vs 리스트&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;배열(array)&lt;/b&gt; 또한 임의의 위치에 접근이 가능하다.&lt;/p&gt;
&lt;p&gt;그렇다면 배열과 리스트의 차이는 무엇일까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;배열&lt;/b&gt;은 각각의 원소에 순서대로 &lt;b&gt;index&lt;/b&gt;가 할당되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;64&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KPFcl/btqSgNhaFFh/EPEtwrfcd7RHN2wBtdS6B1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KPFcl/btqSgNhaFFh/EPEtwrfcd7RHN2wBtdS6B1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KPFcl/btqSgNhaFFh/EPEtwrfcd7RHN2wBtdS6B1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKPFcl%2FbtqSgNhaFFh%2FEPEtwrfcd7RHN2wBtdS6B1%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;64&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 배열에서 &lt;code&gt;c&lt;/code&gt; 원소를 삭제하는 경우 아래와 같은 형태가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;349&quot; data-origin-height=&quot;64&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxel2L/btqSduWs3ZY/X1HrXH75C9paNyBItscfV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxel2L/btqSduWs3ZY/X1HrXH75C9paNyBItscfV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxel2L/btqSduWs3ZY/X1HrXH75C9paNyBItscfV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdxel2L%2FbtqSduWs3ZY%2FX1HrXH75C9paNyBItscfV1%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;349&quot; data-origin-height=&quot;64&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;삭제한 &lt;code&gt;c&lt;/code&gt;의 메모리 공간은 빈 공간으로 남아있으며, &lt;code&gt;d&lt;/code&gt;와 &lt;code&gt;e&lt;/code&gt;의 index는 변함이 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 위의 행위가 &lt;b&gt;리스트&lt;/b&gt;로 구현이 된다면 아래와 같은 형태가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;63&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kBt3j/btqR9m5Jggx/j6p1fzOIM9s33kTPzFFRZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kBt3j/btqR9m5Jggx/j6p1fzOIM9s33kTPzFFRZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kBt3j/btqR9m5Jggx/j6p1fzOIM9s33kTPzFFRZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkBt3j%2FbtqR9m5Jggx%2Fj6p1fzOIM9s33kTPzFFRZk%2Fimg.png&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;63&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;더 이상 리스트에 &lt;code&gt;c&lt;/code&gt;의 메모리 공간 자체가 존재하지 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;덕분에 데이터 밀도가 촘촘해졌으며, &lt;b&gt;메모리 공간을 효율적으로 사용&lt;/b&gt;할 수 있게 되었다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;code&gt;c&lt;/code&gt; 뒤에 위치하고 있었던 &lt;code&gt;d&lt;/code&gt;와 &lt;code&gt;e&lt;/code&gt;의 index가 변경되었다.&lt;/p&gt;
&lt;p&gt;즉, &lt;b&gt;리스트에서는 index를 식별자로 사용할 수 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 예제에서 봤듯이 리스트에서는 index가 중요하지 않다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;리스트에서 중요한 것은 원소들 간의 순서이다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;따라서 리스트를 다른 말로 &lt;b&gt;시퀀스(sequence)&lt;/b&gt;라고도 한다.&lt;/p&gt;
&lt;p&gt;수학에서의 유한수열(finite sequence)과 같은 개념이라고 생각하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1.3 리스트 ADT&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;객체&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;임의의 위치에 접근이 가능한 동일한 자료형 요소들의 순서 있는 모음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;연산&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insert(i, x) : i 위치에 새로운 요소 x 삽입&lt;/li&gt;
&lt;li&gt;delete(i) : i 위치의 요소를 삭제하고 반환&lt;/li&gt;
&lt;li&gt;getEntry(i) : i 위치의 요소를 반환&lt;/li&gt;
&lt;li&gt;find(x) : 리스트에 요소 x가 있는지 확인&lt;/li&gt;
&lt;li&gt;replace(i, x) : i 위치에 있는 요소를 x로 변경&lt;/li&gt;
&lt;li&gt;isEmpty() : 리스트가 비어있으면 true 아니면 false 반환&lt;/li&gt;
&lt;li&gt;isFull() : 리스트가 가득 차 있으면 true 아니면 false 반환&lt;/li&gt;
&lt;li&gt;size() : 리스트 내의 모든 요소 개수를 반환&lt;/li&gt;
&lt;li&gt;display() : 리스트 내의 모든 요소 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. ArrayList&lt;/h2&gt;
&lt;h3&gt;2.1 ArrayList란?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;ArrayList&lt;/b&gt;는 배열을 이용하여 리스트를 구현한 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ArrayList를 구현하기 위해서는 &lt;b&gt;크기가 정해진 1차원 배열&lt;/b&gt;이 필요하다.&lt;/p&gt;
&lt;p&gt;이 배열에 리스트에 요소들이 순서대로 저장되는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;159&quot; width=&quot;430&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RcWiH/btqSpGuKg8J/fvVNcVNwkEN4MF37TQhc7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RcWiH/btqSpGuKg8J/fvVNcVNwkEN4MF37TQhc7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RcWiH/btqSpGuKg8J/fvVNcVNwkEN4MF37TQhc7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRcWiH%2FbtqSpGuKg8J%2FfvVNcVNwkEN4MF37TQhc7k%2Fimg.png&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;159&quot; width=&quot;430&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;또한 배열 내에서 리스트의 범위를 알기 위해, 리스트에 저장된 요소의 개수를 나타내는 변수 &lt;code&gt;length&lt;/code&gt;가 필요하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 ArrayList 구현 (C++)&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

#define MAX_LIST_SIZE 100

template &amp;lt;typename T&amp;gt;
class ArrayList {
private:
    T data[MAX_LIST_SIZE];
    int length;

public:
    ArrayList(){
        length = 0;
    }
    ~ArrayList(){}

    // [삽입] list의 idx번째에 요소 x 추가
    void insert(int idx, T x){
        if(isFull()){
            cout &amp;lt;&amp;lt; &quot;List full error&quot; &amp;lt;&amp;lt; endl;
            return;
        }
        if(idx&amp;lt;0 || idx&amp;gt;length){
            cout &amp;lt;&amp;lt; &quot;insert index error&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        for(int i=length; i&amp;gt;idx; i--){  // idx 뒤의 요소들을 한칸씩 뒤로 이동
            data[i] = data[i-1];
        }
        data[idx] = x;  // idx번째에 x 대입

        length++;
    }

    // [삭제] idx번째 요소를 리스트에서 제거 후 반환
    T remove(int idx){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;List empty error&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }
        if(idx&amp;lt;0 || idx&amp;gt;length){
            cout &amp;lt;&amp;lt; &quot;remove index error&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }

        T x = data[idx];                    // 삭제될 요소
        for(int i=idx+1; i&amp;lt;length; i++){    // idx 뒤의 요소들을 한칸씩 앞으로 이동
            data[i-1] = data[i];
        }

        length--;
        return x;
    }

    // idx번째 항목 반환
    T getEntry(int idx){
        return data[idx];
    }

    // 요소 x가 리스트에 있는지 확인
    bool find(T x){
        for(int i=0; i&amp;lt;length; i++){
            if(data[i] == x){
                return true;
            }
        }
        return false;
    }

    // idx번째 요소를 x로 변경
    void replace(int idx, T x){
        data[idx] = x;
    }

    // List size 반환
    int size(){
        return length;
    }

    // List 출력
    void display() {
        cout &amp;lt;&amp;lt; &quot;List : &quot;;
        for(int i=0; i&amp;lt;size(); i++){
            cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; data[i] &amp;lt;&amp;lt; &quot;]&quot;;
        }
        cout &amp;lt;&amp;lt; endl;
    }

    bool isEmpty(){
        return length == 0;
    }
    bool isFull(){
        return length == MAX_LIST_SIZE;
    }
};

int main(){
    ArrayList&amp;lt;char&amp;gt; list;

    cout &amp;lt;&amp;lt; &quot;===== insert list x5 =====&quot; &amp;lt;&amp;lt; endl;
    list.insert(0, 'a');
    list.insert(1, 'b');
    list.insert(2, 'c');
    list.insert(list.size(), 'd');  // list 마지막에 'd' 삽입
    list.insert(4, 'e');
    cout &amp;lt;&amp;lt; &quot;size : &quot; &amp;lt;&amp;lt; list.size() &amp;lt;&amp;lt; endl;
    list.display();

    cout &amp;lt;&amp;lt; &quot;===== remove index3 =====&quot; &amp;lt;&amp;lt; endl;
    list.remove(3); // 3번째 요소 삭제
    cout &amp;lt;&amp;lt; &quot;size : &quot; &amp;lt;&amp;lt; list.size() &amp;lt;&amp;lt; endl;
    list.display();

    cout &amp;lt;&amp;lt; &quot;===== insert 'f' at index1 =====&quot; &amp;lt;&amp;lt; endl;
    list.insert(1, 'f');
    cout &amp;lt;&amp;lt; &quot;size : &quot; &amp;lt;&amp;lt; list.size() &amp;lt;&amp;lt; endl;
    list.display();

    cout &amp;lt;&amp;lt; &quot;===== replace 'g' at index0 =====&quot; &amp;lt;&amp;lt; endl;
    list.replace(0, 'g');
    list.display();

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-03__9.28.54.png&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;648&quot; width=&quot;690&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEe62f/btqSmzJp860/8iRRaigvWQyApfujHSA9v0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEe62f/btqSmzJp860/8iRRaigvWQyApfujHSA9v0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEe62f/btqSmzJp860/8iRRaigvWQyApfujHSA9v0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEe62f%2FbtqSmzJp860%2F8iRRaigvWQyApfujHSA9v0%2Fimg.png&quot; data-filename=&quot;_2020-11-03__9.28.54.png&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;648&quot; width=&quot;690&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.3 [번외] Java에서의 ArrayList (java.util.ArrayList)&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;ArrayList&lt;/b&gt;는 Java에서 가장 많이 사용되는 자료구조 중 하나이다.&lt;/p&gt;
&lt;p&gt;Java에서 기본적으로 제공하는 ArrayList에 대해 알아보자.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;import java.util.ArrayList;
ArrayList&amp;lt;Integer&amp;gt; lists = new ArrayList&amp;lt;&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;ArrayList 사용 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;lists.add(10);
lists.add(20);
lists.add(1, 40);  // 1번째에 40 추가

lists.remove(1);   // 리스트의 1번째 요소 삭제

lists.get(0);      // 리스트의 0번째 요소 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ArrayList를 사용함에도 최대 배열 size를 지정하지 않았다.&lt;/p&gt;
&lt;p&gt;왜 그럴까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Java의 ArrayList는 내부적으로 배열의 크기가 고정되어 설정된다.&lt;/p&gt;
&lt;p&gt;만약 배열이 가득 찼음에도 새로운 데이터가 추가된다면, 기존의 배열보다 1.5배 긴 새로운 배열을 만들어 기존 리스트 데이터를 새로운 배열로 복제한다.&lt;/p&gt;
&lt;p&gt;하지만 이러한 경우 당연히 시간이 오래 걸리기 때문에 프로그래머는 이를 고려하여 ArrayList를 사용해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. Linked List&lt;/h2&gt;
&lt;h3&gt;3.1 Linked List란?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Linked List&lt;/code&gt;는 &lt;b&gt;Node 간의 연결(Link)을&lt;/b&gt; 이용하여 동적으로 크기를 할당할 수 있는 선형 자료구조이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVNl7d/btqR9lTim4J/aepHKwSL2N0GFArssU5tS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVNl7d/btqR9lTim4J/aepHKwSL2N0GFArssU5tS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVNl7d/btqR9lTim4J/aepHKwSL2N0GFArssU5tS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVNl7d%2FbtqR9lTim4J%2FaepHKwSL2N0GFArssU5tS1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Node&lt;/code&gt;는 데이터와 포인터를 가지고 있으며 각 Node의 포인터는 다음 Node 또는 이전 Node와의 연결을 담당한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.2 Linked List 구조&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHQs9B/btqR6uXie6H/nTdqFpBJiw8g5BzTYsvFOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHQs9B/btqR6uXie6H/nTdqFpBJiw8g5BzTYsvFOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHQs9B/btqR6uXie6H/nTdqFpBJiw8g5BzTYsvFOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHQs9B%2FbtqR6uXie6H%2FnTdqFpBJiw8g5BzTYsvFOk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;각각의 &lt;code&gt;Node&lt;/code&gt;는 &lt;code&gt;data&lt;/code&gt;와 &lt;code&gt;link&lt;/code&gt; 변수를 가진다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;data&lt;/code&gt; 변수에는 Linked List에서 사용할 자료형의 데이터가 들어가고, &lt;code&gt;link&lt;/code&gt; 변수에는 다음 Node의 메모리 주소값이 들어간다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;head pointer&lt;/code&gt;란 첫 번째 Node의 주소를 가리키는 변수를 뜻한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.3 ArrayList vs LinkedList&lt;/h3&gt;
&lt;p&gt;ArrayList와 LinkedList의 차이점을 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lfYX3/btqSgNBtgYz/DHrngOnzImJUfZW9OUZYg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lfYX3/btqSgNBtgYz/DHrngOnzImJUfZW9OUZYg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lfYX3/btqSgNBtgYz/DHrngOnzImJUfZW9OUZYg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlfYX3%2FbtqSgNBtgYz%2FDHrngOnzImJUfZW9OUZYg0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;203&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qwFBq/btqSgMifu3B/UfU6yFCWRu2eWy2FIs0RJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qwFBq/btqSgMifu3B/UfU6yFCWRu2eWy2FIs0RJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qwFBq/btqSgMifu3B/UfU6yFCWRu2eWy2FIs0RJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqwFBq%2FbtqSgMifu3B%2FUfU6yFCWRu2eWy2FIs0RJ0%2Fimg.png&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;203&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.4 Linked List 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Singly Linked List (단일 연결 리스트)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;61&quot; data-filename=&quot;Untitled 7.png&quot; width=&quot;470&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIiO6i/btqSdu3cNIe/rqHob8ta3jTTehGX8pGob0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIiO6i/btqSdu3cNIe/rqHob8ta3jTTehGX8pGob0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIiO6i/btqSdu3cNIe/rqHob8ta3jTTehGX8pGob0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIiO6i%2FbtqSdu3cNIe%2FrqHob8ta3jTTehGX8pGob0%2Fimg.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;61&quot; data-filename=&quot;Untitled 7.png&quot; width=&quot;470&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Singly Linked List&lt;/code&gt;는 각 노드에 data와 한 개의 포인터 변수가 있고, &lt;b&gt;각 노드의 포인터는 다음 노드를 가리킨다&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Circular Linked List (원형 연결 리스트)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;87&quot; width=&quot;437&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dipIx3/btqR9lMAfkT/cMb5jUZM4gKQtziAC8lmmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dipIx3/btqR9lMAfkT/cMb5jUZM4gKQtziAC8lmmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dipIx3/btqR9lMAfkT/cMb5jUZM4gKQtziAC8lmmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdipIx3%2FbtqR9lMAfkT%2FcMb5jUZM4gKQtziAC8lmmK%2Fimg.png&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;87&quot; width=&quot;437&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Circular Linked List&lt;/code&gt;는 Singly Linked List와 동일하지만 &lt;b&gt;마지막 노드의 포인터가 처음 노드를 가리킨다&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;우리가 앞에서 다뤘던 Circular Queue처럼 &lt;b&gt;원형 구조의 리스트&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Doubly Linked List (이중 연결 리스트)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;60&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K2Xae/btqSa9ZnbUs/b713jZEk9PPGl6RKppxnp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K2Xae/btqSa9ZnbUs/b713jZEk9PPGl6RKppxnp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K2Xae/btqSa9ZnbUs/b713jZEk9PPGl6RKppxnp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK2Xae%2FbtqSa9ZnbUs%2Fb713jZEk9PPGl6RKppxnp1%2Fimg.png&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;60&quot; width=&quot;499&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Doubly Linked List&lt;/code&gt; 또한 Singly Linked List와 비슷하지만, 각 노드에 2개의 포인터 변수가 있다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;각각의 포인터는 이전 노드와 다음 노드를 가리킨다&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.5 head pointer를 사용하여 LinkedList 구현 (C++)&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Singly Linkedlist&lt;/b&gt;를 구현해보자.&lt;/p&gt;
&lt;p&gt;LinkedList의 head를 표현하기 위해 &lt;code&gt;head pointer&lt;/code&gt; 개념을 사용한다. (자세한 내용은 뒤에서...)&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

// Node
template &amp;lt;typename T&amp;gt;
struct Node {
private:
    T data;
    Node&amp;lt;T&amp;gt;* next = nullptr;
public:
    Node(T d, Node&amp;lt;T&amp;gt;* pNode){
        data = d;
        next = pNode;
    }
    ~Node(){}
    Node&amp;lt;T&amp;gt;* getNext() {
        return next;
    }
    void setNext(Node&amp;lt;T&amp;gt;* pNode){
        next = pNode;
    }
    T getData(){
        return data;
    }
};

template &amp;lt;typename T&amp;gt;
class SinglyLinkedList {
private:
    Node&amp;lt;T&amp;gt;* head;  // head pointer
    int size = 0;

public:
    SinglyLinkedList(){
        head = nullptr;
    }
    ~SinglyLinkedList(){}

    // list 마지막에 data 삽입
    void insert(T data){
        Node&amp;lt;T&amp;gt;* node = new Node&amp;lt;T&amp;gt;(data, nullptr);

        if(isEmpty()){      // 비어있는 경우 head에 node 추가
            head = node;
        }else{              // 아닌 경우 마지막 node를 찾아서 해당 node의 next에 새로운 node를 연결
            Node&amp;lt;T&amp;gt;* prevNode = head;
            for(int i=0; i&amp;lt;size-1; i++) {
                prevNode = prevNode-&amp;gt;getNext();
            }
            prevNode-&amp;gt;setNext(node);
        }

        size++;
    }

    // list의 idx번째에 data 삽입
    void insert(T data, int idx){
        if(idx &amp;lt; 0 || idx &amp;gt; size){
            cout &amp;lt;&amp;lt; &quot;index error&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        Node&amp;lt;T&amp;gt;* node = new Node&amp;lt;T&amp;gt;(data, nullptr);

        if(idx==0){     // 0번째 index에 추가하는 경우
            node-&amp;gt;setNext(head);        // 새로운 node의 next에 기존 head node를 연결 (비어있는 경우 nullptr로 들어감)
            head = node;                // 0번째 index가 바꼈기 때문에 head를 바꾸어줌
        }else{          // 아닌경우
            Node&amp;lt;T&amp;gt;* prevNode = head;
            for(int i=0; i&amp;lt;idx-1; i++) {
                prevNode = prevNode-&amp;gt;getNext();     // index-1번째 node를 찾은 후
            }
            node-&amp;gt;setNext(prevNode-&amp;gt;getNext());     // 새로운 node의 next에 이전 node의 next를 대입.
            prevNode-&amp;gt;setNext(node);                // 이전 node의 next에는 새로운 node를 연결
        }

        size++;
    }

    // idx번째 요소 삭제
    void remove(int idx){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;List is Empty&quot; &amp;lt;&amp;lt; endl;
            return;
        }
        if(idx &amp;lt; 0 || idx &amp;gt; size){
            cout &amp;lt;&amp;lt; &quot;index error&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        if(idx==0){     // 0번째 index를 지우는 경우 head를 다음노드로 바꿔줌
            Node&amp;lt;T&amp;gt;* tmpNode = head;
            head = tmpNode-&amp;gt;getNext();
            delete tmpNode;
        }else{          // 아닌경우
            Node&amp;lt;T&amp;gt;* prevNode = head;
            for(int i=0; i&amp;lt;idx-1; i++) {
                prevNode = prevNode-&amp;gt;getNext();     // index-1번째 node를 찾은 후
            }
            Node&amp;lt;T&amp;gt;* tmpNode = prevNode-&amp;gt;getNext();
            prevNode-&amp;gt;setNext(tmpNode-&amp;gt;getNext());  // 해당 node의 next를 삭제할 node의 next로 바꿔줌
            delete tmpNode;
        }

        size--;
    }

    // list 출력
    void display(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;List is Empty&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        Node&amp;lt;T&amp;gt;* tmpNode = head;
        while(tmpNode){
            cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; tmpNode-&amp;gt;getData() &amp;lt;&amp;lt; &quot;]&quot;;
            tmpNode = tmpNode-&amp;gt;getNext();
        }
        cout &amp;lt;&amp;lt; endl;
    }

    bool isEmpty(){
        return size==0 ? true : false;
    }
};

int main(){
    SinglyLinkedList&amp;lt;char&amp;gt; list;

    cout &amp;lt;&amp;lt; &quot;===== insert x3 =====&quot; &amp;lt;&amp;lt; endl;
    list.insert('a');
    list.insert('b');
    list.insert('c');
    list.insert('d', 2);   // insert 'd' at index2
    list.display();

    cout &amp;lt;&amp;lt; &quot;===== remove index1 =====&quot; &amp;lt;&amp;lt; endl;
    list.remove(1);
    list.display();

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-03__10.16.43.png&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;370&quot; width=&quot;696&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tSM3x/btqSmzWYlo1/xDOWzAvKDaa8aZbR1kiAkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tSM3x/btqSmzWYlo1/xDOWzAvKDaa8aZbR1kiAkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tSM3x/btqSmzWYlo1/xDOWzAvKDaa8aZbR1kiAkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtSM3x%2FbtqSmzWYlo1%2FxDOWzAvKDaa8aZbR1kiAkk%2Fimg.png&quot; data-filename=&quot;_2020-11-03__10.16.43.png&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;370&quot; width=&quot;696&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.6 head node를 사용하여 LinkedList 구현 (C++)&lt;/h3&gt;
&lt;p&gt;이번엔 &lt;code&gt;head node&lt;/code&gt; 개념을 사용해서 구현해보자. (역시나 자세한 내용은 뒤에서...)&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

// Node
template &amp;lt;typename T&amp;gt;
struct Node {
private:
    T data;
    Node&amp;lt;T&amp;gt;* next = nullptr;
public:
    Node(){
        data = T();
        next = nullptr;
    }
    Node(T d, Node&amp;lt;T&amp;gt;* pNode){
        data = d;
        next = pNode;
    }
    ~Node(){}

    Node&amp;lt;T&amp;gt;* getNext() {
        return next;
    }
    void setNext(Node&amp;lt;T&amp;gt;* pNode){
        next = pNode;
    }
    T getData(){
        return data;
    }
};

template &amp;lt;typename T&amp;gt;
class SinglyLinkedList {
private:
    Node&amp;lt;T&amp;gt; org;  // head node

public:
    SinglyLinkedList(){}
    ~SinglyLinkedList(){}

    // first node 반환 (list의 head pointer)
    Node&amp;lt;T&amp;gt;* getHead(){
        return org.getNext();
    }

    // idx번째 항목 반환
    Node&amp;lt;T&amp;gt;* getEntry(int idx){
        if(idx&amp;lt;-1 || idx&amp;gt;size()){
            cout &amp;lt;&amp;lt; &quot;index error&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }

        Node&amp;lt;T&amp;gt;* node = &amp;amp;org;

        // head node부터 idx번 반복해서 next node를 찾아감
        for(int i=-1; i&amp;lt;idx; i++){
            node = node-&amp;gt;getNext();
        }
        return node;
    }

    // list의 idx번째에 data 삽입
    void insert(int idx, T data){
        Node&amp;lt;T&amp;gt;* prev = getEntry(idx-1);                // idx번째 node의 이전 node

        Node&amp;lt;T&amp;gt;* node = new Node&amp;lt;T&amp;gt;(data, prev-&amp;gt;getNext()); // 새로운 node의 next에 prev의 next를 대입
        prev-&amp;gt;setNext(node);                                // prev의 next에는 새로운 node의 주소 대입
    }

    // list의 idx번째 요소 삭제 및 반환
    T remove(int idx){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;List is Empty&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }
        Node&amp;lt;T&amp;gt;* node = getEntry(idx);          // 삭제할 node
        T data = node-&amp;gt;getData();               // 삭제할 node의 data

        Node&amp;lt;T&amp;gt;* prev = getEntry(idx-1);    // idx번째 node의 이전 node
        prev-&amp;gt;setNext(node-&amp;gt;getNext());         // prev의 next에 삭제할 node의 next 대입
        delete node;

        return data;
    }

    // list의 idx번째 요소를 data로 변경
    void replace(int idx, T data){
        Node&amp;lt;T&amp;gt;* prev = getEntry(idx-1);    // 삭제될 node의 이전 node

        Node&amp;lt;T&amp;gt;* node = new Node&amp;lt;T&amp;gt;(data, prev-&amp;gt;getNext()-&amp;gt;getNext());  // 새로운 node
        delete prev-&amp;gt;getNext();
        prev-&amp;gt;setNext(node);
    }

    // list 출력
    void display(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;List is Empty&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        for(Node&amp;lt;T&amp;gt; *p = getHead(); p != nullptr; p=p-&amp;gt;getNext()){
            cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; p-&amp;gt;getData() &amp;lt;&amp;lt; &quot;]&quot;;
        }
        cout &amp;lt;&amp;lt; endl;
    }

    int size(){
        int cnt = 0;
        for(Node&amp;lt;T&amp;gt; *p = getHead(); p != nullptr; p=p-&amp;gt;getNext()){
            cnt++;
        }
        return cnt;
    }

    bool isEmpty(){
        return size()==0 ? true : false;
    }

    void clear(){
        while(!isEmpty()){
            remove(0);
        }
    }
};

int main(){
//    SinglyLinkedList&amp;lt;char&amp;gt; list;
//
//    cout &amp;lt;&amp;lt; &quot;===== insert x3 =====&quot; &amp;lt;&amp;lt; endl;
//    list.insert(0, 'a');
//    list.insert(1, 'b');
//    list.insert(2, 'c');
//    list.display();
//
//    cout &amp;lt;&amp;lt; &quot;===== insert 'd' at index2 =====&quot; &amp;lt;&amp;lt; endl;
//    list.insert(2, 'd');
//    list.display();
//
//    cout &amp;lt;&amp;lt; &quot;===== remove index1 =====&quot; &amp;lt;&amp;lt; endl;
//    list.remove(1);
//    list.display();

    SinglyLinkedList&amp;lt;int&amp;gt; list;
    list.insert(0, 10);
    list.insert(0, 20);
    list.insert(1, 30);
    list.insert(list.size(), 40);
    list.insert(2, 50);
    list.display();
    list.remove(2);
    list.remove(list.size()-1);
    list.remove(0);
    list.replace(1, 90);
    list.display();
    list.clear();
    list.display();

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-03__11.28.16.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;372&quot; width=&quot;700&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o14qL/btqSgLKoT07/kKveaB6mg2wrMlBvcgqbi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o14qL/btqSgLKoT07/kKveaB6mg2wrMlBvcgqbi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o14qL/btqSgLKoT07/kKveaB6mg2wrMlBvcgqbi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo14qL%2FbtqSgLKoT07%2FkKveaB6mg2wrMlBvcgqbi0%2Fimg.png&quot; data-filename=&quot;_2020-11-03__11.28.16.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;372&quot; width=&quot;700&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.7 head pointer vs head node&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;LinkedList&lt;/b&gt;에서는 첫 번째 node의 주소를 관리하기 위해 &lt;code&gt;head pointer&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;code&gt;head node&lt;/code&gt;를 사용하여 첫 번째 node를 가리킬 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;head pointer&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cixzQ6/btqSsR3P0zg/6KLpLmdInXauHCKegKtEa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cixzQ6/btqSsR3P0zg/6KLpLmdInXauHCKegKtEa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cixzQ6/btqSsR3P0zg/6KLpLmdInXauHCKegKtEa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcixzQ6%2FbtqSsR3P0zg%2F6KLpLmdInXauHCKegKtEa0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;Class LinkedList{
    Node* head;
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;head node&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UoLak/btqR9mxWTCS/KLQPLJTxz2UjVcwozCSTFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UoLak/btqR9mxWTCS/KLQPLJTxz2UjVcwozCSTFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UoLak/btqR9mxWTCS/KLQPLJTxz2UjVcwozCSTFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUoLak%2FbtqR9mxWTCS%2FKLQPLJTxz2UjVcwozCSTFk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;Class LinkedList{
    Node org;    // 실제 head point는 &quot;org.link&quot;
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;head pointer&lt;/code&gt;를 사용하는 경우 첫 번째 node인지 구별하여 첫번째 node에서 삽입, 삭제가 이뤄지는 경우 따로 처리를 해주어야 한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;// list의 idx번째에 data 삽입
void insert(int idx, T data){
    Node&amp;lt;T&amp;gt;* node = new Node&amp;lt;T&amp;gt;(data, nullptr);

    if(idx==0){     // 0번째 index에 추가하는 경우
        node-&amp;gt;setNext(head);
        head = node;
    }else{          // 아닌경우
        Node&amp;lt;T&amp;gt;* prevNode = head;
        for(int i=0; i&amp;lt;idx-1; i++) {
            prevNode = prevNode-&amp;gt;getNext();
        }
        node-&amp;gt;setNext(prevNode-&amp;gt;getNext());
        prevNode-&amp;gt;setNext(node);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;head node&lt;/code&gt;를 사용하면 실제 head 앞에 임의의 empty node가 있어 항상 동일한 logic으로 구현할 수 있다. &amp;rarr; 삽입과 삭제 용이&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;// list의 idx번째에 data 삽입
void insert(int idx, T data){
    Node&amp;lt;T&amp;gt;* prev = getEntry(idx-1);                // idx번째 node의 이전 node

    Node&amp;lt;T&amp;gt;* node = new Node&amp;lt;T&amp;gt;(data, prev-&amp;gt;getNext()); // 새로운 node의 next에 prev의 next를 대입
    prev-&amp;gt;setNext(node);                                // prev의 next에는 새로운 node의 주소 대입
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;head pointer&lt;/code&gt;를 사용한 &lt;code&gt;code&lt;/code&gt;와 비교했을 때 확실히 간단해진 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.8 Circular Linked List&lt;/h3&gt;
&lt;p&gt;위에서 설명했듯이 &lt;b&gt;Circular Linked List&lt;/b&gt;는 마지막 Node의 &lt;code&gt;link&lt;/code&gt;가 첫 번째 Node를 가리키는 원형 구조의 리스트이다.&lt;/p&gt;
&lt;p&gt;이러한 구조에서 가장 마지막 Node &lt;code&gt;tail&lt;/code&gt;에 쉽게 접근하기 위해 head pointer가 tail Node를 가리키게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 12.png&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;136&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GWFiH/btqR6vorcDr/8qfn2j5gHdTWNTxNsEJfyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GWFiH/btqR6vorcDr/8qfn2j5gHdTWNTxNsEJfyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GWFiH/btqR6vorcDr/8qfn2j5gHdTWNTxNsEJfyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGWFiH%2FbtqR6vorcDr%2F8qfn2j5gHdTWNTxNsEJfyk%2Fimg.png&quot; data-filename=&quot;Untitled 12.png&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;136&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;즉, 개념적으로 &lt;code&gt;head pointer&lt;/code&gt;가 가리키는 Node가 &lt;code&gt;tail&lt;/code&gt; Node이며, &lt;code&gt;tail&lt;/code&gt; Node의 다음 Node가 &lt;code&gt;head&lt;/code&gt; Node인 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.9 Doubly Linked List&lt;/h3&gt;
&lt;p&gt;이중연결리스트 또한 위에서 설명했을뿐더러 이전에 deque를 구현해 보았으니, 바로 C++로 구현해보자.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

// Node
template &amp;lt;typename T&amp;gt;
struct Node {
private:
    T data;
    Node&amp;lt;T&amp;gt;* prev;
    Node&amp;lt;T&amp;gt;* next;
public:
    Node(){
        data = T();
        prev = nullptr;
        next = nullptr;
    }
    Node(T d, Node&amp;lt;T&amp;gt;* prevNode, Node&amp;lt;T&amp;gt;* nextNode){
        data = d;
        prev = prevNode;
        next = nextNode;
    }
    ~Node(){}

    Node&amp;lt;T&amp;gt;* getPrev() {
        return prev;
    }
    void setPrev(Node&amp;lt;T&amp;gt;* pNode){
        prev = pNode;
    }
    Node&amp;lt;T&amp;gt;* getNext() {
        return next;
    }
    void setNext(Node&amp;lt;T&amp;gt;* pNode){
        next = pNode;
    }
    T getData(){
        return data;
    }
};

template &amp;lt;typename T&amp;gt;
class DoublyLinkedList {
private:
    Node&amp;lt;T&amp;gt; org;  // head node

public:
    DoublyLinkedList(){}
    ~DoublyLinkedList(){}

    // first node 반환 (list의 head pointer)
    Node&amp;lt;T&amp;gt;* getHead(){
        return org.getNext();
    }

    // idx번째 항목 반환
    Node&amp;lt;T&amp;gt;* getEntry(int idx){
        if(idx&amp;lt;-1 || idx&amp;gt;size()){
            cout &amp;lt;&amp;lt; &quot;index error&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }

        Node&amp;lt;T&amp;gt;* node = &amp;amp;org;

        // head node부터 idx번 반복해서 next node를 찾아감
        for(int i=-1; i&amp;lt;idx; i++){
            node = node-&amp;gt;getNext();
        }
        return node;
    }

    // list의 idx번째에 data 삽입
    void insert(int idx, T data){
        Node&amp;lt;T&amp;gt;* prev = getEntry(idx-1);                        // idx번째 node의 이전 node

        Node&amp;lt;T&amp;gt;* node = new Node&amp;lt;T&amp;gt;(data, prev, prev-&amp;gt;getNext());   // 새로운 node의 prev에는 prev, next에는 prev-&amp;gt;next 대입
        prev-&amp;gt;setNext(node);                                        // prev의 next에는 새로운 node 대입
        if(node-&amp;gt;getNext() != nullptr){                             // node의 next가 null이 아닌 경우 next-&amp;gt;prev에 새로운 node 대입
            node-&amp;gt;getNext()-&amp;gt;setPrev(node);
        }
    }

    // list의 idx번째 요소 삭제 및 반환
    T remove(int idx){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;List is Empty&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }
        Node&amp;lt;T&amp;gt;* node = getEntry(idx);          // 삭제할 node
        T data = node-&amp;gt;getData();               // 삭제할 node의 data

        Node&amp;lt;T&amp;gt;* prev = getEntry(idx-1);    // idx번째 node의 이전 node
        prev-&amp;gt;setNext(node-&amp;gt;getNext());         // prev의 next에 삭제할 node의 next 대입
        if(node-&amp;gt;getNext() != nullptr){         // node의 next가 null이 아닌 경우 next-&amp;gt;prev에 삭제할 node의 prev 대입
            node-&amp;gt;getNext()-&amp;gt;setPrev(prev);
        }
        delete node;

        return data;
    }

    // list의 idx번째 요소를 data로 변경
    void replace(int idx, T data){
        Node&amp;lt;T&amp;gt;* prev = getEntry(idx-1);    // 삭제될 node의 이전 node

        Node&amp;lt;T&amp;gt;* node = new Node&amp;lt;T&amp;gt;(data, prev, prev-&amp;gt;getNext()-&amp;gt;getNext());  // 새로운 node
        delete prev-&amp;gt;getNext();
        prev-&amp;gt;setNext(node);
        if(node-&amp;gt;getNext() != nullptr){
            node-&amp;gt;getNext()-&amp;gt;setPrev(node);
        }
    }

    // list 출력
    void display(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;List is Empty&quot; &amp;lt;&amp;lt; endl;
            return;
        }

        for(Node&amp;lt;T&amp;gt; *p = getHead(); p != nullptr; p=p-&amp;gt;getNext()){
            cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; p-&amp;gt;getData() &amp;lt;&amp;lt; &quot;]&quot;;
        }
        cout &amp;lt;&amp;lt; endl;
    }

    int size(){
        int cnt = 0;
        for(Node&amp;lt;T&amp;gt; *p = getHead(); p != nullptr; p=p-&amp;gt;getNext()){
            cnt++;
        }
        return cnt;
    }

    bool isEmpty(){
        return size()==0 ? true : false;
    }

    void clear(){
        while(!isEmpty()){
            remove(0);
        }
    }
};

int main(){
    DoublyLinkedList&amp;lt;int&amp;gt; list;

    list.insert(0, 10);
    list.insert(0, 20);
    list.insert(1, 30);
    list.insert(list.size(), 40);
    list.insert(2, 50);
    list.display();
    list.remove(2);
    list.remove(list.size()-1);
    list.remove(0);
    list.replace(1, 90);
    list.display();
    list.clear();
    list.display();

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-11-04__2.44.45.png&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;364&quot; width=&quot;698&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zehIt/btqSpHtFAUB/aNGAftoSK9PdCMen0AVjIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zehIt/btqSpHtFAUB/aNGAftoSK9PdCMen0AVjIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zehIt/btqSpHtFAUB/aNGAftoSK9PdCMen0AVjIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzehIt%2FbtqSpHtFAUB%2FaNGAftoSK9PdCMen0AVjIk%2Fimg.png&quot; data-filename=&quot;_2020-11-04__2.44.45.png&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;364&quot; width=&quot;698&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Data Structure</category>
      <category>ArrayList</category>
      <category>Circular LinkedList</category>
      <category>Doubly LinkedList</category>
      <category>Head node</category>
      <category>Head pointer</category>
      <category>LinkedList</category>
      <category>list</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/27</guid>
      <comments>https://suyeon96.tistory.com/27#entry27comment</comments>
      <pubDate>Sat, 2 Jan 2021 22:39:55 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 메모리 정적 할당 vs 동적 할당 (Stack vs Heap)</title>
      <link>https://suyeon96.tistory.com/26</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;  이전 글 : &lt;a href=&quot;https://suyeon96.tistory.com/25&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[C++]&amp;nbsp;포인터&amp;nbsp;한방에&amp;nbsp;이해하기&amp;nbsp;(Call&amp;nbsp;by&amp;nbsp;Value&amp;nbsp;vs&amp;nbsp;Call&amp;nbsp;by&amp;nbsp;Reference)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Memory 영역 (Stack vs Heap)&lt;/h3&gt;
&lt;p&gt;컴퓨터에서 &lt;code&gt;메모리 영역&lt;/code&gt;은 아래와 같이 나뉘어&lt;span style=&quot;color: #333333;&quot;&gt;있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;519&quot; width=&quot;413&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H5zii/btqSdtoNcjg/INiZeH2S9jShGTMEW4yB5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H5zii/btqSdtoNcjg/INiZeH2S9jShGTMEW4yB5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H5zii/btqSdtoNcjg/INiZeH2S9jShGTMEW4yB5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH5zii%2FbtqSdtoNcjg%2FINiZeH2S9jShGTMEW4yB5K%2Fimg.png&quot; data-filename=&quot;Untitled 9.png&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;519&quot; width=&quot;413&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Code&lt;/b&gt; : 실행한 프로그램의 코드가 저장됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Data&lt;/b&gt; : 전역변수와 static변수가 저장되며 프로그램 종료 시까지 사라지지 않고 남아있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Heap&lt;/b&gt; : 동적으로 할당된 메모리영역이며 프로그래머에 의해 할당( C++ : &lt;code&gt;new&lt;/code&gt;, C : &lt;code&gt;malloc&lt;/code&gt; ) 및 해제( C++ : &lt;code&gt;delete&lt;/code&gt;, C : &lt;code&gt;free&lt;/code&gt; )됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Stack&lt;/b&gt; : 지역변수와 매개변수가 할당되고 함수를 빠져나가면 자동 소멸됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;정적 메모리 할당 vs 동적 메모리 할당&lt;/h3&gt;
&lt;p&gt;프로그래밍 관점에서 메모리 영역에는 크게 &lt;code&gt;stack&lt;/code&gt;과 &lt;code&gt;heap&lt;/code&gt; 메모리 공간이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정적으로 메모리를 할당하면 컴파일 시 &lt;code&gt;stack&lt;/code&gt; 영역에 할당되며, 함수를 빠져나갈 때 소멸된다.&lt;/p&gt;
&lt;p&gt;프로그램 컴파일 시 &lt;code&gt;stack&lt;/code&gt;에 얼마만큼의 크기로 할당을 해야하는지 결정되기 때문에, 컴파일 이후 크기가 변경될 수 없다.&lt;/p&gt;
&lt;p&gt;따라서 정적 배열 선언 시 크기를 가변적으로 명시하면 문제가 될 수 있으므로 반드시 상수로 명시해야 한다.&lt;/p&gt;
&lt;pre class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;int main() {
    int n = 10;
    int arr[n];    // 불가능
    int arr[10];   // 가능
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;반면 동적으로 메모리를 할당하는 경우 데이터가 &lt;code&gt;heap&lt;/code&gt; 영역에 할당된다.&lt;/p&gt;
&lt;p&gt;위에서 다루었듯이 &lt;code&gt;heap&lt;/code&gt;은 프로그래머에 의해 할당(new)되거나 소멸(delete)된다.&lt;/p&gt;
&lt;p&gt;따라서 프로그래머가 원할 때 원하는 크기로 할당할 수 있는 것이다.&lt;/p&gt;
&lt;p&gt;참고로 포인터 변수는 &lt;code&gt;stack&lt;/code&gt;에 할당된다.&lt;/p&gt;
&lt;pre class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;int main() {
    int n = 10;
    int* arr = new int[n];    // arr 포인터 변수는 stack에 할당되며, arr 배열은 heap에 할당됨
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;new &amp;amp; delete&lt;/h3&gt;
&lt;p&gt;위에서 했던 이야기지만, &lt;code&gt;정적 메모리&lt;/code&gt;는 &lt;b&gt;Compile&lt;/b&gt; 시에 &lt;code&gt;stack&lt;/code&gt; 영역에 할당되고 함수를 빠져나올 때 소멸된다.&lt;/p&gt;
&lt;p&gt;반면 &lt;code&gt;동적 메모리&lt;/code&gt;는 &lt;b&gt;Runtime&lt;/b&gt; 시 프로그래머의 의도대로 &lt;code&gt;heap&lt;/code&gt; 영역에 할당되고 소멸된다.&lt;/p&gt;
&lt;p&gt;따라서 동적 메모리를 control 하기 위해 규칙을 정해놓았으며, C++에서는 할당 시 &lt;code&gt;new&lt;/code&gt; 연산자를 사용하며 소멸 시엔 &lt;code&gt;delete&lt;/code&gt; 연산자를 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;heap&lt;/code&gt; 영역에 &lt;b&gt;동적 할당되는 메모리의 경우&lt;/b&gt; 전적으로 프로그래머에 의해 관리되므로, 사용이 끝난다면 &lt;b&gt;반드시 delete를 해주어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;참고로 동적 배열을 delete 하는 경우 배열이라는 것을 명시하기 위해 &lt;code&gt;delete[]&lt;/code&gt; 연산자를 사용해야만 한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// 할당
int *pData = new int;
int *array = new int[size];

// 소멸
delete pData;
delete[] array;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2차원 배열의 동적 할당&lt;/h3&gt;
&lt;p&gt;2차원 배열을 동적할당 하는 경우 아래와 같은 &lt;span style=&quot;color: #333333;&quot;&gt;구조를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 10.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;190&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tPfSQ/btqRXIH2tzU/Vr6dN22oBkrpnKf3tacJMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tPfSQ/btqRXIH2tzU/Vr6dN22oBkrpnKf3tacJMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tPfSQ/btqRXIH2tzU/Vr6dN22oBkrpnKf3tacJMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtPfSQ%2FbtqRXIH2tzU%2FVr6dN22oBkrpnKf3tacJMK%2Fimg.png&quot; data-filename=&quot;Untitled 10.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;190&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mat : int**형&lt;/li&gt;
&lt;li&gt;mat[i] : int*형&lt;/li&gt;
&lt;li&gt;mat[i][j] : int형&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2차원 동적 배열은 단순하게 한 줄로 선언하기 힘들고 loop를 돌며 일일이 할당 및 해제를 해줘야 한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

// int형 2차원 배열 동적 할당
int** alloc2DArr (int rows, int cols){
    if(rows &amp;lt;= 0 || cols &amp;lt;= 0) return NULL;
    int** mat = new int* [rows];
    for (int i=0 ; i&amp;lt;rows ; i++ )
        mat[i] = new int[cols];
    return mat;
}

// int형 2차원 배열 동적 해제
void free2DArr ( int** mat, int rows, int cols=0){
    if(mat != NULL){
        for(int i=0 ; i&amp;lt;rows ; i++)
            delete[] mat[i];
        delete[] mat;
    }
}

int main() {

    int **mat;
    int rows, cols;
    cin &amp;gt;&amp;gt; rows &amp;gt;&amp;gt; cols;

    // mat 할당
    mat = alloc2DArr(rows, cols);

    // mat[][] 랜덤 입력
    for(int i=0; i&amp;lt;rows; i++){
        for(int j=0; j&amp;lt;cols; j++){
            mat[i][j] = rand()%100;
        }
    }

    // mat 출력
    for(int i=0; i&amp;lt;rows; i++){
        for(int j=0; j&amp;lt;cols; j++){
            cout &amp;lt;&amp;lt; mat[i][j] &amp;lt;&amp;lt; &quot; &quot;;
        }
        cout &amp;lt;&amp;lt; endl;
    }

    // mat 해제
    free2DArr(mat, rows, cols);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로 2중 loop를 돌리는 것보다 &lt;code&gt;memset&lt;/code&gt; (&lt;code&gt;#include &amp;lt;string.h&amp;gt;&lt;/code&gt;)을 사용하는 것이 유용하다.&lt;/p&gt;
&lt;p&gt;또한 이보다 vector를 사용하는 것이 더욱 편리하고 빠르다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(위의 내용은 나중에 따로 포스팅...)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Programming</category>
      <category>delete</category>
      <category>dynamic allocation</category>
      <category>Heap</category>
      <category>memory</category>
      <category>new</category>
      <category>stack</category>
      <category>Static Allocation</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/26</guid>
      <comments>https://suyeon96.tistory.com/26#entry26comment</comments>
      <pubDate>Fri, 1 Jan 2021 02:21:32 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 포인터 한방에 이해하기 (Call by Value vs Call by Reference)</title>
      <link>https://suyeon96.tistory.com/25</link>
      <description>&lt;h2&gt;포인터 (Pointer)&lt;/h2&gt;
&lt;h3&gt;포인터 변수&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;포인터&lt;/b&gt;란 &quot;어떤 것을 가리키는 것&quot;을 의미한다.&lt;/p&gt;
&lt;p&gt;C나 C++ 등의 프로그래밍 언어에서 &lt;code&gt;포인터&lt;/code&gt;는 &quot;주소를 가리키는 것&quot;을 뜻하며, 이러한 것을 저장하는 변수를 &lt;code&gt;포인터 변수&lt;/code&gt;라고 한다.&lt;/p&gt;
&lt;p&gt;프로그래밍에서 포인터가 악명이 높기로 유명하지만, 의외로 단순하니 겁먹을 필요가 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 코드를 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int a = 10;
int* p = &amp;amp;a;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;a&lt;/code&gt;라는 &lt;b&gt;int형 변수&lt;/b&gt;를&amp;nbsp;선언하고 10으로 초기화하였다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;p&lt;/code&gt;라는 &lt;b&gt;int형 포인터 변수&lt;/b&gt;를&amp;nbsp;선언하고 a의 주소값으로 초기화하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 한가지 짚고 넘어가야 할 것이 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;&amp;amp; (주소 연산자)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;주소 연산자 &lt;code&gt;&amp;amp;&lt;/code&gt;를 사용하면 변수에 할당된 &lt;b&gt;메모리 주소&lt;/b&gt;를&amp;nbsp;확인할 수 있다.&lt;br /&gt;참고로 비트 연산자 AND(&amp;amp;)와 모양은 같지만, 주소 연산자는 단항 연산자이고 비트 AND연산자는 이항 연산자이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;* (역참조 연산자)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;역참조 연산자 &lt;code&gt;*&lt;/code&gt;를 사용하면 특정 &lt;b&gt;메모리의 주소값&lt;/b&gt;에&amp;nbsp;접근할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉 위의 코드는 아래 그림과 같은 구조를 가&lt;span style=&quot;color: #333333;&quot;&gt;진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;404&quot; width=&quot;240&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdkQRx/btqSa9xkOC3/TlrFN1rQHiDWpRMkzv11Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdkQRx/btqSa9xkOC3/TlrFN1rQHiDWpRMkzv11Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdkQRx/btqSa9xkOC3/TlrFN1rQHiDWpRMkzv11Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdkQRx%2FbtqSa9xkOC3%2FTlrFN1rQHiDWpRMkzv11Bk%2Fimg.png&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;404&quot; width=&quot;240&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;int a = 10;&lt;/b&gt; // int형 변수 선언
&lt;ul&gt;
&lt;li&gt;int형이므로 a라는 이름의 4byte 메모리 공간을 할당 (메모리 주소 : 0x0014)&lt;/li&gt;
&lt;li&gt;해당 메모리에 숫자 10 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;int* p = &amp;amp;a;&lt;/b&gt; // int형 포인터 변수 선언
&lt;ul&gt;
&lt;li&gt;int형 이므로 p라는 이름의 4byte 메모리 공간 할당 (메모리 주소 : 0x0004)&lt;/li&gt;
&lt;li&gt;해당 메모리에 변수 a의 주소값(0x0014) 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한번 확인해보자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;int a = 10;
int* p = &amp;amp;a;

cout &amp;lt;&amp;lt; &quot;a : &quot; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;&amp;amp;a : &quot; &amp;lt;&amp;lt; &amp;amp;a &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;p : &quot; &amp;lt;&amp;lt; p &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;*p : &quot; &amp;lt;&amp;lt; *p &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;&amp;amp;p : &quot; &amp;lt;&amp;lt; &amp;amp;p &amp;lt;&amp;lt; endl;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;212&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfFYX6/btqRXHWykoK/cp8EY1vzEKKB7CxKKjpEbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfFYX6/btqRXHWykoK/cp8EY1vzEKKB7CxKKjpEbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfFYX6/btqRXHWykoK/cp8EY1vzEKKB7CxKKjpEbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfFYX6%2FbtqRXHWykoK%2Fcp8EY1vzEKKB7CxKKjpEbK%2Fimg.png&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;212&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이러한 구조로 메모리에 할당&lt;/span&gt;이 된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;310&quot; data-origin-height=&quot;406&quot; width=&quot;241&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNimDR/btqRXHvsdKl/fapgVLvwtK8Hkot4JfVLk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNimDR/btqRXHvsdKl/fapgVLvwtK8Hkot4JfVLk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNimDR/btqRXHvsdKl/fapgVLvwtK8Hkot4JfVLk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNimDR%2FbtqRXHvsdKl%2FfapgVLvwtK8Hkot4JfVLk1%2Fimg.png&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;310&quot; data-origin-height=&quot;406&quot; width=&quot;241&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 중요한 점은 역참조 연산자 &lt;code&gt;*&lt;/code&gt;를 통해 포인터 변수가 가리키는 주소 &lt;code&gt;a&lt;/code&gt;의 값 &lt;code&gt;10&lt;/code&gt;을 알 수 있다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;포인터 변수가 자료형을 가져야 하는 이유&lt;/b&gt;도 여기에 있다.&lt;/p&gt;
&lt;p&gt;자료형이 없다면 포인터는 역참조 시 해당 메모리의 값을 해석할 수 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 경우 int형이라고 명시가 되어 있었기 때문에 &lt;code&gt;0073FA74&lt;/code&gt; 주소의 메모리 값을 int형으로 읽어올 수 있는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;이중 포인터, 3중 포인터&lt;/h3&gt;
&lt;p&gt;위의 개념을 잘 이해했다면 2중 포인터, 3중 포인터도 거뜬하다.&lt;/p&gt;
&lt;p&gt;긴 말 필요 없이 아래 코드를 보자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;int a = 10;
int* p1 = &amp;amp;a;
int** p2 = &amp;amp;p1;
int*** p3 = &amp;amp;p2;

cout &amp;lt;&amp;lt; &quot;a : &quot; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;&amp;amp;a : &quot; &amp;lt;&amp;lt; &amp;amp;a &amp;lt;&amp;lt; '\n' &amp;lt;&amp;lt; endl;

cout &amp;lt;&amp;lt; &quot;p1 : &quot; &amp;lt;&amp;lt; p1 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;*p1 : &quot; &amp;lt;&amp;lt; *p1 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;&amp;amp;p1 : &quot; &amp;lt;&amp;lt; &amp;amp;p1 &amp;lt;&amp;lt; '\n' &amp;lt;&amp;lt;  endl;

cout &amp;lt;&amp;lt; &quot;p2 : &quot; &amp;lt;&amp;lt; p2 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;*p2 : &quot; &amp;lt;&amp;lt; *p2 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;**p2 : &quot; &amp;lt;&amp;lt; **p2 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;&amp;amp;p2 : &quot; &amp;lt;&amp;lt; &amp;amp;p2 &amp;lt;&amp;lt; '\n' &amp;lt;&amp;lt;  endl;

cout &amp;lt;&amp;lt; &quot;p3 : &quot; &amp;lt;&amp;lt; p3 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;*p3 : &quot; &amp;lt;&amp;lt; *p3 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;**p3 : &quot; &amp;lt;&amp;lt; **p3 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;***p3 : &quot; &amp;lt;&amp;lt; ***p3 &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;&amp;amp;p3 : &quot; &amp;lt;&amp;lt; &amp;amp;p3 &amp;lt;&amp;lt; endl;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;460&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjh5p7/btqSa9RDSg7/lUsKfMsFHFiRXvQmqUfG20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjh5p7/btqSa9RDSg7/lUsKfMsFHFiRXvQmqUfG20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjh5p7/btqSa9RDSg7/lUsKfMsFHFiRXvQmqUfG20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjh5p7%2FbtqSa9RDSg7%2FlUsKfMsFHFiRXvQmqUfG20%2Fimg.png&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;460&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Call by Value vs Call by Reference&lt;/h3&gt;
&lt;p&gt;자 이제 우리는 포인터가 어떤 역할을 하는지 알고 있다.&lt;/p&gt;
&lt;p&gt;그렇다면 함수의 매개변수에 변수 값을 전달하는 것과 주소 값을 전달할 때 어떤 차이가 있는지 보자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

void callByVal(int a){
    a = 20;
}

void callByRef(int* b){
    *b = 20;
}

int main()
{
    int a = 10;
    int b = 10;

    callByVal(a);
    callByRef(&amp;amp;b);

    cout &amp;lt;&amp;lt; &quot;a: &quot; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;b: &quot; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; endl;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;158&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zFAJK/btqRTSdcnLe/K3gZkcTxDLxLLsnuK02cVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zFAJK/btqRTSdcnLe/K3gZkcTxDLxLLsnuK02cVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zFAJK/btqRTSdcnLe/K3gZkcTxDLxLLsnuK02cVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzFAJK%2FbtqRTSdcnLe%2FK3gZkcTxDLxLLsnuK02cVk%2Fimg.png&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;158&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;int형 &lt;code&gt;a&lt;/code&gt;와 &lt;code&gt;b&lt;/code&gt;변수를 선언하고 &lt;code&gt;10&lt;/code&gt;으로 초기화하였다.&lt;/p&gt;
&lt;p&gt;이후 &lt;code&gt;callByVal&lt;/code&gt; 함수에서는 a의 값을 받아서 20으로 수정하였고, &lt;code&gt;callByRef&lt;/code&gt; 함수에서는 b의 주소값을 받아 20으로 수정하였다.&lt;/p&gt;
&lt;p&gt;그런 다음 다시 main에서 a와 b를 출력해보니 &lt;code&gt;b&lt;/code&gt;는 &lt;code&gt;20&lt;/code&gt;으로 바뀌었지만, &lt;code&gt;a&lt;/code&gt;는 그대로 &lt;code&gt;10&lt;/code&gt;으로 출력된다.&lt;/p&gt;
&lt;p&gt;이제 이해가 되는가?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;callByVal&lt;/code&gt; 함수에서는 &lt;b&gt;a의 값&lt;/b&gt; 10을 받아서 메모리에 새로운 공간을 할당하고 받은 값 10을 입력하였다. 그런 다음 &lt;b&gt;새로운 메모리 공간의 10을 20으로 변환&lt;/b&gt;하였다. 따라서 main에 있는 기존 a의 값은 전혀 변함이 없었던 것이다.&lt;/p&gt;
&lt;p&gt;반면 &lt;code&gt;callByRef&lt;/code&gt; 함수에서는 &lt;b&gt;b의 주소값&lt;/b&gt;을 받아서 메모리에 새로운 공간을 할당하고 b의 주소값을 입력하였다. 그런 다음 &lt;b&gt;새로운 메모리 공간에 있는 주소값이 가리키는 b변수를 찾아 해당 값을 20으로 변환&lt;/b&gt;하였다. 따라서 main에 있는 기존 b값이 20으로 변경된 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;배열과 포인터&lt;/h3&gt;
&lt;p&gt;배열은 사실 포인터와 같은 역할을 한다.&lt;/p&gt;
&lt;p&gt;포인터는 주소를 담는 변수라고 계속해서 이야기해왔다.&lt;/p&gt;
&lt;p&gt;배열 변수 또한 포인터와 동일하게 배열의 첫 번째 index의 주소를 가지고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;107&quot; width=&quot;529&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9oHcR/btqR9lY6kFs/JgkpdEJ0uUVLaYoqKxfrP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9oHcR/btqR9lY6kFs/JgkpdEJ0uUVLaYoqKxfrP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9oHcR/btqR9lY6kFs/JgkpdEJ0uUVLaYoqKxfrP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9oHcR%2FbtqR9lY6kFs%2FJgkpdEJ0uUVLaYoqKxfrP1%2Fimg.png&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;107&quot; width=&quot;529&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 코드를 보자.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;int arr[5] = {1, 2, 3, 4, 5};

cout &amp;lt;&amp;lt; &quot;arr[0] : &quot; &amp;lt;&amp;lt; arr[0] &amp;lt;&amp;lt; endl;
cout &amp;lt;&amp;lt; &quot;arr : &quot; &amp;lt;&amp;lt; arr &amp;lt;&amp;lt; endl;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;161&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLsXCi/btqR3RqJbqa/sKskPZxkDaAgq9Qumn6860/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLsXCi/btqR3RqJbqa/sKskPZxkDaAgq9Qumn6860/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLsXCi/btqR3RqJbqa/sKskPZxkDaAgq9Qumn6860/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLsXCi%2FbtqR3RqJbqa%2FsKskPZxkDaAgq9Qumn6860%2Fimg.png&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;161&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;arr 변수가 arr[0]의 주소값을 가지고 있음을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇다면 이를 응용하여 배열에 접근할 때 아래와 같이 코드를 작성할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;int arr[5] = {1, 2, 3, 4, 5};

cout &amp;lt;&amp;lt; &quot;arr : &quot; &amp;lt;&amp;lt; arr &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;arr[0] : &quot; &amp;lt;&amp;lt; arr[0] &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;*arr : &quot; &amp;lt;&amp;lt; *arr &amp;lt;&amp;lt; endl;

cout &amp;lt;&amp;lt; &quot;arr+1 : &quot; &amp;lt;&amp;lt; arr+1 &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;arr[1] : &quot; &amp;lt;&amp;lt; arr[1] &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;*(arr+1) : &quot; &amp;lt;&amp;lt; *(arr+1) &amp;lt;&amp;lt; endl;

cout &amp;lt;&amp;lt; &quot;arr+2 : &quot; &amp;lt;&amp;lt; arr+2 &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;arr[2] : &quot; &amp;lt;&amp;lt; arr[2] &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;*(arr+2) : &quot; &amp;lt;&amp;lt; *(arr+2) &amp;lt;&amp;lt; endl;

cout &amp;lt;&amp;lt; &quot;arr+3 : &quot; &amp;lt;&amp;lt; arr+3 &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;arr[3] : &quot; &amp;lt;&amp;lt; arr[3] &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;*(arr+3) : &quot; &amp;lt;&amp;lt; *(arr+3) &amp;lt;&amp;lt; endl;

cout &amp;lt;&amp;lt; &quot;arr+4 : &quot; &amp;lt;&amp;lt; arr+4 &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;arr[4] : &quot; &amp;lt;&amp;lt; arr[4] &amp;lt;&amp;lt; &quot;,  &quot;;
cout &amp;lt;&amp;lt; &quot;*(arr+4) : &quot; &amp;lt;&amp;lt; *(arr+4) &amp;lt;&amp;lt; endl;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;212&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIWGQL/btqRTSqEFLt/1X7sXwcJT2JqvTgkQCi7P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIWGQL/btqRTSqEFLt/1X7sXwcJT2JqvTgkQCi7P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIWGQL/btqRTSqEFLt/1X7sXwcJT2JqvTgkQCi7P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIWGQL%2FbtqRTSqEFLt%2F1X7sXwcJT2JqvTgkQCi7P1%2Fimg.png&quot; data-filename=&quot;Untitled 7.png&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;212&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;144&quot; width=&quot;548&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhGkSw/btqR1FxlnKQ/luOSZxGUfSdrAhw6Rrx0V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhGkSw/btqR1FxlnKQ/luOSZxGUfSdrAhw6Rrx0V0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhGkSw/btqR1FxlnKQ/luOSZxGUfSdrAhw6Rrx0V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhGkSw%2FbtqR1FxlnKQ%2FluOSZxGUfSdrAhw6Rrx0V0%2Fimg.png&quot; data-filename=&quot;Untitled 8.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;144&quot; width=&quot;548&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;객체 멤버의 접근&lt;/h3&gt;
&lt;p&gt;C++에서 객체의 멤버에 접근하는 방법에 대해 알아보자.&lt;/p&gt;
&lt;p&gt;객체에서 멤버에 접근할 때는 &lt;code&gt;.&lt;/code&gt; 연산자를 사용하며 포인터 객체의 멤버에 접근할 때는 &lt;code&gt;&amp;rarr;&lt;/code&gt; 연산자를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

class Test{
public:
    int a;
    int b;
};

int main()
{
    Test T;
    Test* pT = &amp;amp;T;

    T.a = 1;
    pT-&amp;gt;b = 2;

    cout &amp;lt;&amp;lt; &quot;a : &quot; &amp;lt;&amp;lt; pT-&amp;gt;a &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;b : &quot; &amp;lt;&amp;lt; T.b &amp;lt;&amp;lt; endl;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Test&lt;/code&gt; Class 객체의 &lt;code&gt;a&lt;/code&gt; 멤버변수에 접근하는 방법으로 &lt;code&gt;T.a&lt;/code&gt;, &lt;code&gt;(&amp;amp;T)&amp;rarr;a&lt;/code&gt;, &lt;code&gt;pT&amp;rarr;a&lt;/code&gt;, &lt;code&gt;(*pT).a&lt;/code&gt; 가 모두 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/Programming</category>
      <category>array pointer</category>
      <category>call by reference</category>
      <category>call by value</category>
      <category>pointer</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/25</guid>
      <comments>https://suyeon96.tistory.com/25#entry25comment</comments>
      <pubDate>Fri, 1 Jan 2021 02:10:06 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 덱 (Deque)</title>
      <link>https://suyeon96.tistory.com/24</link>
      <description>&lt;h2&gt;덱 (Deque)&lt;/h2&gt;
&lt;h3&gt;덱?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;덱(Deque)&lt;/b&gt;이란 Double-Ended Queue의 줄임말이다.&lt;/p&gt;
&lt;p&gt;즉, 앞쪽 &lt;code&gt;front&lt;/code&gt;와 뒤쪽 &lt;code&gt;rear&lt;/code&gt;에서 모두 삽입과 삭제가 가능한 큐를 의미한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;118&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ptOhu/btqRTSqAZRB/OrlMCriLQ3RTFVNB7hhRLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ptOhu/btqRTSqAZRB/OrlMCriLQ3RTFVNB7hhRLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ptOhu/btqRTSqAZRB/OrlMCriLQ3RTFVNB7hhRLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FptOhu%2FbtqRTSqAZRB%2FOrlMCriLQ3RTFVNB7hhRLk%2Fimg.png&quot; data-filename=&quot;Untitled 6.png&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;118&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;덱 ADT&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;객체&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전단과 후단 양쪽에서의 삽입과 삭제를 허용하는 동일한 자료형의 요소들의 모음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;연산&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;addFront(x)&lt;/b&gt; : 요소 x를 덱의 맨 앞에 추가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;addRear(x)&lt;/b&gt; : 요소 x를 덱의 맨 뒤에 추가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;deleteFront()&lt;/b&gt; : 큐의 맨 앞의 요소를 삭제하고 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;deleteRear()&lt;/b&gt; : 큐의 맨 뒤의 요소를 삭제하고 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;getFront()&lt;/b&gt; : 큐의 맨 앞의 요소를 삭제하지 않고 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;getRear()&lt;/b&gt; : 큐의 맨 뒤의 요소를 삭제하지 않고 반환&lt;/li&gt;
&lt;li&gt;isEmpty() : 큐가 비어있으면 true 아니면 false 반환&lt;/li&gt;
&lt;li&gt;isFull() : 큐가 가득 차 있으면 true 아니면 false 반환&lt;/li&gt;
&lt;li&gt;size() : 큐 내의 모든 요소 개수를 반환&lt;/li&gt;
&lt;li&gt;display() : 큐 내의 모든 요소 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Array로 Circular Deque 구현 (C++)&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/*
 * array를 이용하여 Circular Deque 구현
 */
#include &amp;lt;iostream&amp;gt;
using namespace std;

#define MAX_DEQUE_SIZE 10

class Deque {
private:
    int front;  // 첫번째 요소 앞의 index
    int rear;   // 마지막 요소 index
    int data[MAX_DEQUE_SIZE];

public:
    Deque(){
        front = 0;
        rear = 0;
    }
    ~Deque(){}

    void addFront(int n){
        if(isFull()){
            cout &amp;lt;&amp;lt; &quot;Deque Full Error&quot; &amp;lt;&amp;lt; endl;
            exit(1);
        }
        data[front] = n;
        front = (front-1+MAX_DEQUE_SIZE)%MAX_DEQUE_SIZE;    // front가 0 이하로 떨어지는 경우 max index로 순회
    }

    void addRear(int n){    // push
        if(isFull()){
            cout &amp;lt;&amp;lt; &quot;Deque Full Error&quot; &amp;lt;&amp;lt; endl;
            exit(1);
        }
        rear = (rear+1)%MAX_DEQUE_SIZE;   // rear가 max를 넘어가는 경우 다시 0번째 index로 순회
        data[rear] = n;
    }

    int deleteFront(){  // pop
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Deque Empty Error&quot; &amp;lt;&amp;lt; endl;
            exit(1);
        }
        front = (front+1)%MAX_DEQUE_SIZE;   // front가 max를 넘어가는 경우 다시 0번째 index로 순회
        return data[front];
    }

    int deleteRear(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Deque Empty Error&quot; &amp;lt;&amp;lt; endl;
            exit(1);
        }
        int tmp = data[rear];
        rear = (rear-1+MAX_DEQUE_SIZE)%MAX_DEQUE_SIZE;   // rear가 0 이하로 떨어지는 경우 max index로 순회
        return tmp;
    }

    int getFront(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Deque Empty Error&quot; &amp;lt;&amp;lt; endl;
            exit(1);
        }
        return data[(front+1)%MAX_DEQUE_SIZE];
    }

    int getRear(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Deque Empty Error&quot; &amp;lt;&amp;lt; endl;
            exit(1);
        }
        return data[rear];
    }

    int size(){
        return front&amp;lt;=rear ? rear-front : (rear+MAX_DEQUE_SIZE)-front;
    }

    void display(){
        for(int i=front+1; i&amp;lt;=front+size(); i++){
            cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; data[i%MAX_DEQUE_SIZE] &amp;lt;&amp;lt; &quot;]&quot;;
        }
        cout &amp;lt;&amp;lt; endl;
    }

    // circular array의 front와 rear 정보를 보기위한 메소드
    void displayDetail(){
        cout &amp;lt;&amp;lt; &quot;DEQUE :&quot;;
        for(int i=front+1; i&amp;lt;=front+size(); i++){
            cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; data[i%MAX_DEQUE_SIZE] &amp;lt;&amp;lt; &quot;]&quot;;
        }
        cout &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; &quot;index :&quot;;
        for(int i=front+1; i&amp;lt;=front+size(); i++){
            cout &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; i%MAX_DEQUE_SIZE &amp;lt;&amp;lt; &quot; &quot;;
        }
        cout &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; &quot;front : &quot; &amp;lt;&amp;lt; front &amp;lt;&amp;lt; &quot;, rear : &quot; &amp;lt;&amp;lt; rear &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; endl;
    }

    bool isEmpty(){
        return front == rear;
    }
    bool isFull() {
        return front == (rear+1)%MAX_DEQUE_SIZE;
    }
};

int main() {
    Deque deque;

    cout &amp;lt;&amp;lt; &quot;===== addRear x3 =====&quot; &amp;lt;&amp;lt; endl;
    deque.addRear(1);
    deque.addRear(2);
    deque.addRear(3);
    cout &amp;lt;&amp;lt; &quot; size : &quot; &amp;lt;&amp;lt; deque.size() &amp;lt;&amp;lt; endl;
    deque.displayDetail();

    cout &amp;lt;&amp;lt; &quot;===== addFront x2 ======&quot; &amp;lt;&amp;lt; endl;
    deque.addFront(5);
    deque.addFront(6);
    cout &amp;lt;&amp;lt; &quot; size : &quot; &amp;lt;&amp;lt; deque.size() &amp;lt;&amp;lt; endl;
    deque.displayDetail();

    cout &amp;lt;&amp;lt; &quot;===== deleteRear x1 ======&quot; &amp;lt;&amp;lt; endl;
    deque.deleteRear();
    cout &amp;lt;&amp;lt; &quot; size : &quot; &amp;lt;&amp;lt; deque.size() &amp;lt;&amp;lt; endl;
    deque.displayDetail();

    cout &amp;lt;&amp;lt; &quot;===== deleteFront x3 ======&quot; &amp;lt;&amp;lt; endl;
    deque.deleteFront();
    deque.deleteFront();
    deque.deleteFront();
    cout &amp;lt;&amp;lt; &quot; size : &quot; &amp;lt;&amp;lt; deque.size() &amp;lt;&amp;lt; endl;
    deque.displayDetail();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;1198&quot; data-filename=&quot;_2020-10-11__8.52.13.png&quot; width=&quot;645&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x7oUF/btqSbawcG3F/LvKW1iYvm2Hi4tsaKGMCK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x7oUF/btqSbawcG3F/LvKW1iYvm2Hi4tsaKGMCK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x7oUF/btqSbawcG3F/LvKW1iYvm2Hi4tsaKGMCK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx7oUF%2FbtqSbawcG3F%2FLvKW1iYvm2Hi4tsaKGMCK1%2Fimg.png&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;1198&quot; data-filename=&quot;_2020-10-11__8.52.13.png&quot; width=&quot;645&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;LinkedList로 Circular Deque 구현 (C++)&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;/*
 * 이중 Linked List를 이용하여 Deque 구현
 */
#include &amp;lt;iostream&amp;gt;
using namespace std;

// Doubly Linked List
struct Node {
    int data;
    Node* prev; // 이전 Node를 가리키는 pointer
    Node* next; // 다음 Node를 가리키는 pointer

public:
    Node(){
        prev = nullptr;
        next = nullptr;
        data = 0;
    }
    Node(int n, Node* prevNode = nullptr, Node* nextNode = nullptr){
        data = n;
        prev = prevNode;
        next = nextNode;
    }
    ~Node(){}

    void setPrev(Node* prevNode){
        prev = prevNode;
    }
    void setNext(Node* nextNode){
        next = nextNode;
    }
};

// Deque Class
class Deque {
private:
    Node* front;    // Deack의 front를 가리키는 Node pointer
    Node* rear;     // Deack의 rear를 가리키는 Node pointer
    int dataSize;   // Stack에 저장된 데이터 size

public:
    Deque(){
        front = nullptr;
        rear = nullptr;
        dataSize = 0;
    }
    ~Deque(){
        while(!isEmpty()){
            deleteFront(); //deleteRear();
        }
        delete front;
        delete rear;
    }

    //새로운 node를 생성 (prev : null, next : 기존의 front node)
    //front를 새로운 node로 변경
    //최초 생성이라면 rear = node 해줌
    //이후 front가 추가될 때마다 기존 node의 prev에 새로 추가되는 node 대입
    void addFront(int n){
        Node* node = new Node(n, nullptr, front);
        if(rear == nullptr){
            rear = node;
        }else{
            front-&amp;gt;setPrev(node);
        }
        front = node;
        dataSize++;
    }

    //addFront()와 같은 맥락.
    //새로운 node를 생성 (prev : 기존의 rear node, next : null)
    //rear를 새로운 node로 변경
    //최초 생성이라면 front = node 해줌
    //이후 rear가 추가될 때마다 기존 node의 next에 새로 추가되는 node 대입
    void addRear(int n){
        Node* node = new Node(n, rear, nullptr);
        if(front == nullptr){
            front = node;
        }else{
            rear-&amp;gt;setNext(node);
        }
        rear = node;
        dataSize++;
    }

    //deque가 비어있는 경우 error occurred
    //그렇지 않다면, 우선 front node의 데이터를 임시 변수 'data'에 담아놓음
    //front를 next node로 변경한 다음, 삭제된 node(front였던)는 delete 처리 (메모리자원을위해)
    //아까 담아놓은 'data'를 반환
    //또한 현재 front노드의 prev를 null로 지정
    int deleteFront(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Deque Empty Error&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }else{
            int data = front-&amp;gt;data;
            Node* node = front;
            front = front-&amp;gt;next;
            front-&amp;gt;setPrev(nullptr);
            delete node;
            dataSize--;
            return data;
        }
    }

    //deleteFront()와 같은 맥락.
    //deque가 비어있는 경우 error occurred
    //그렇지 않다면, 우선 rear node의 데이터를 임시 변수 'data'에 담아놓음
    //rear를 prev node로 변경한 다음, 삭제된 node(rear였던)는 delete 처리 (메모리자원을위해)
    //아까 담아놓은 'data'를 반환
    //또한 현재 rear노드의 next를 null로 지정
    int deleteRear(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Deque Empty Error&quot; &amp;lt;&amp;lt; endl;
            exit(EXIT_FAILURE);
        }else{
            int data = rear-&amp;gt;data;
            Node* node = rear;
            rear = rear-&amp;gt;prev;
            rear-&amp;gt;setNext(nullptr);
            delete node;
            dataSize--;
            return data;
        }
    }

    // deque의 모든 data 출력
    void display(){
        if(isEmpty()){
            cout &amp;lt;&amp;lt; &quot;Deque is Empty&quot; &amp;lt;&amp;lt; endl;
        }else{
            Node* node = front;
            while(node){
                cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; node-&amp;gt;data &amp;lt;&amp;lt; &quot;]&quot;;
                node = node-&amp;gt;next;
            }
            cout &amp;lt;&amp;lt; endl;
        }
    }

    // deque size 반환
    int size(){
        return dataSize;
    }

    bool isEmpty(){
        return dataSize == 0;
    }
};

int main() {
    Deque deque;

    cout &amp;lt;&amp;lt; &quot;===== addRear x3 =====&quot; &amp;lt;&amp;lt; endl;
    deque.addRear(1);
    deque.addRear(2);
    deque.addRear(3);
    cout &amp;lt;&amp;lt; &quot; size : &quot; &amp;lt;&amp;lt; deque.size() &amp;lt;&amp;lt; endl;
    deque.display();

    cout &amp;lt;&amp;lt; &quot;===== addFront x2 ======&quot; &amp;lt;&amp;lt; endl;
    deque.addFront(5);
    deque.addFront(6);
    cout &amp;lt;&amp;lt; &quot; size : &quot; &amp;lt;&amp;lt; deque.size() &amp;lt;&amp;lt; endl;
    deque.display();

    cout &amp;lt;&amp;lt; &quot;===== deleteRear x1 ======&quot; &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; deque.deleteRear() &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot; size : &quot; &amp;lt;&amp;lt; deque.size() &amp;lt;&amp;lt; endl;
    deque.display();

    cout &amp;lt;&amp;lt; &quot;===== deleteFront x3 ======&quot; &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; deque.deleteFront() &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; deque.deleteFront() &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; deque.deleteFront() &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot; size : &quot; &amp;lt;&amp;lt; deque.size() &amp;lt;&amp;lt; endl;
    deque.display();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;_2020-10-12__3.45.22.png&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;856&quot; width=&quot;679&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bML4g9/btqRQSR0swJ/GqY8FLv5Kg2RiNKpkm0M8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bML4g9/btqRQSR0swJ/GqY8FLv5Kg2RiNKpkm0M8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bML4g9/btqRQSR0swJ/GqY8FLv5Kg2RiNKpkm0M8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbML4g9%2FbtqRQSR0swJ%2FGqY8FLv5Kg2RiNKpkm0M8k%2Fimg.png&quot; data-filename=&quot;_2020-10-12__3.45.22.png&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;856&quot; width=&quot;679&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>IT/Data Structure</category>
      <category>c++</category>
      <category>Circular Deque</category>
      <category>deque</category>
      <author>suyeon96</author>
      <guid isPermaLink="true">https://suyeon96.tistory.com/24</guid>
      <comments>https://suyeon96.tistory.com/24#entry24comment</comments>
      <pubDate>Thu, 31 Dec 2020 23:50:07 +0900</pubDate>
    </item>
  </channel>
</rss>