<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>꾸준히 성장하는 개발자스토리</title>
    <link>https://ssdragon.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 18:31:04 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>묠니르묘묘</managingEditor>
    <image>
      <title>꾸준히 성장하는 개발자스토리</title>
      <url>https://tistory1.daumcdn.net/tistory/5132359/attach/4b9cacf5fb724d519fb68214493fe6c8</url>
      <link>https://ssdragon.tistory.com</link>
    </image>
    <item>
      <title>AWS 핵심 서비스로 웹 애플리케이션 구축하기 - 실습</title>
      <link>https://ssdragon.tistory.com/191</link>
      <description>&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://ssdragon.tistory.com/190&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS 핵심 서비스로 웹 애플리케이션 구축하기 - 이론&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS 핵심 서비스로 웹 애플리케이션 구축하기 - 실습&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 아키텍처 개요&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFo9mf/btsKbg6t6QD/bUkO2tlqc42Y9MtoCuGqp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFo9mf/btsKbg6t6QD/bUkO2tlqc42Y9MtoCuGqp1/img.png&quot; data-alt=&quot;AWS 핵심 서비스로 웹 애플리케이션 아키텍처 기본 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFo9mf/btsKbg6t6QD/bUkO2tlqc42Y9MtoCuGqp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFo9mf%2FbtsKbg6t6QD%2FbUkO2tlqc42Y9MtoCuGqp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;400&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS 핵심 서비스로 웹 애플리케이션 아키텍처 기본 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습으로 AWS 기본이 되는 컴퓨팅 서비스인 EC2 를 이용한 웹 애플리케이션을 구축한다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;944&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsFXrI/btsKb6aMKtQ/8E4Fr12Cri8J6ChJkYxCT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsFXrI/btsKb6aMKtQ/8E4Fr12Cri8J6ChJkYxCT1/img.png&quot; data-alt=&quot;최종 아키텍처 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsFXrI/btsKb6aMKtQ/8E4Fr12Cri8J6ChJkYxCT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsFXrI%2FbtsKb6aMKtQ%2F8E4Fr12Cri8J6ChJkYxCT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;642&quot; height=&quot;400&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;944&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최종 아키텍처 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 모니터링과 오토스케일링, 정적 웹 사이트 호스팅을 추가로 사용하도록 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 네트워크 구성하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1. VPC 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. VPC 마법사 시작을 클릭하여 VPC 구성을 쉽게 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpAXkw/btsKduVWgw9/bd3iE46BHY9gb9HaY26UTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpAXkw/btsKduVWgw9/bd3iE46BHY9gb9HaY26UTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpAXkw/btsKduVWgw9/bd3iE46BHY9gb9HaY26UTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpAXkw%2FbtsKduVWgw9%2Fbd3iE46BHY9gb9HaY26UTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;249&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. VPC 설정에서 아래 사진처럼 선택한다. 이름은 VPC-Lab , CIDR 블록은 기본값인 10.0.0.0/16 으로 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pktUg/btsKcPNgYUq/jzWbvXnu42vfd3sq5GZPG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pktUg/btsKcPNgYUq/jzWbvXnu42vfd3sq5GZPG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pktUg/btsKcPNgYUq/jzWbvXnu42vfd3sq5GZPG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpktUg%2FbtsKcPNgYUq%2FjzWbvXnu42vfd3sq5GZPG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;447&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;824&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;3. 가용영역(AZ)는 1개를 선택하고, 가용영역은 ap-northeast-2a 로 선택한다. 이 가용영역은 아까 설정했던 VPC의 부분집합이다. 밑에 있는 퍼블릭 서브넷 수도 1개로 선택하고, CIDR 블록을 10.0.10.0/24 로 설정한다. 프라이빗 서브넷은 만들지 않으므로 0개를 선택하고 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3pYJc/btsKchpy04O/ScfrnbqK5MnkxFzX6wb5K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3pYJc/btsKchpy04O/ScfrnbqK5MnkxFzX6wb5K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3pYJc/btsKchpy04O/ScfrnbqK5MnkxFzX6wb5K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3pYJc%2FbtsKchpy04O%2FScfrnbqK5MnkxFzX6wb5K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;418&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;577&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;4. VPC 이름을 아래 사진처럼 변경한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHtYU5/btsKdlLz1Eh/WcDdziveIvkZwIv0dCegdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHtYU5/btsKdlLz1Eh/WcDdziveIvkZwIv0dCegdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHtYU5/btsKdlLz1Eh/WcDdziveIvkZwIv0dCegdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHtYU5%2FbtsKdlLz1Eh%2FWcDdziveIvkZwIv0dCegdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;100&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 서브넷 이름도 public subnet A 로 변경한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nyOfI/btsKckmiu2i/KsjZ1OXz2Y6li2166eymD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nyOfI/btsKckmiu2i/KsjZ1OXz2Y6li2166eymD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nyOfI/btsKckmiu2i/KsjZ1OXz2Y6li2166eymD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnyOfI%2FbtsKckmiu2i%2FKsjZ1OXz2Y6li2166eymD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;110&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;177&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;6. 현재까지의 아키텍처 구성은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqsjXY/btsKbwBlhKa/VFjvxguqt23SxPsEaYx3G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqsjXY/btsKbwBlhKa/VFjvxguqt23SxPsEaYx3G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqsjXY/btsKbwBlhKa/VFjvxguqt23SxPsEaYx3G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqsjXY%2FbtsKbwBlhKa%2FVFjvxguqt23SxPsEaYx3G0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;281&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;710&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-2. 추가 서브넷 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성을 확보하기 위해 다중 가용 영역에 서비스를 배포하는 것이 중요하다. 따라서 기존에 생성한 가용영역A 외에 다른 가용영역 C에 서브넷을 생성해본다.&lt;/p&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. 왼쪽 사이드 바에서 서브넷 메뉴를 클릭하여 서브넷 생성을 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RMqvR/btsKbCBpAMf/oz57frrI2r9txRzgBFQzv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RMqvR/btsKbCBpAMf/oz57frrI2r9txRzgBFQzv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RMqvR/btsKbCBpAMf/oz57frrI2r9txRzgBFQzv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRMqvR%2FbtsKbCBpAMf%2Foz57frrI2r9txRzgBFQzv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;687&quot; height=&quot;175&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. VPC ID에는 방금 생성한 VPC 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnbQv3/btsKddmBINy/jDsRR7CSF9slT1AMFBNqBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnbQv3/btsKddmBINy/jDsRR7CSF9slT1AMFBNqBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnbQv3/btsKddmBINy/jDsRR7CSF9slT1AMFBNqBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnbQv3%2FbtsKddmBINy%2FjDsRR7CSF9slT1AMFBNqBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;428&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;518&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;3. 아래 사진처럼 설정한 후 서브넷 생성 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckLsQ3/btsKcQyGIm2/ysg9xKX5e2N8cQ9LNgQKMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckLsQ3/btsKcQyGIm2/ysg9xKX5e2N8cQ9LNgQKMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckLsQ3/btsKcQyGIm2/ysg9xKX5e2N8cQ9LNgQKMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckLsQ3%2FbtsKcQyGIm2%2Fysg9xKX5e2N8cQ9LNgQKMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;680&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1335&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;4. 현재까지 아키텍처 구조&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LRHgY/btsKb09Belh/kDSKJKccebACx1eWCZy1cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LRHgY/btsKb09Belh/kDSKJKccebACx1eWCZy1cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LRHgY/btsKb09Belh/kDSKJKccebACx1eWCZy1cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLRHgY%2FbtsKb09Belh%2FkDSKJKccebACx1eWCZy1cK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;342&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;708&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-3. 라우팅 테이블 편집하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 테이블에는 서브넷 또는 게이트웨이의 네트워크 트래픽이 전송되는 위치를 결정하는데 사용되는 라우팅이라는 규칙 집합이 포함되어 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 라우팅 테이블
&lt;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;다른 라우팅 테이블과 명시적으로 연결되지 않은 모든 서브넷의 라우팅을 제어하는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자 지정 라우팅 테이블
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 라우팅 테이블 외에 사용자가 생성한 라우팅 테이블&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/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;1. 서브넷 메뉴에서 작업 버튼 클릭 후, 라우팅 테이블 연결 편집 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmdFkU/btsKby63H1m/k9ecWtKGEJH42S7D4nrTXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmdFkU/btsKby63H1m/k9ecWtKGEJH42S7D4nrTXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmdFkU/btsKby63H1m/k9ecWtKGEJH42S7D4nrTXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmdFkU%2FbtsKby63H1m%2Fk9ecWtKGEJH42S7D4nrTXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;202&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 라우팅 테이블 ID에서 기본 라우팅 테이블이 아닌 다른 라우팅 테이블 선택 후 저장한다. 이때, 선택한 라우팅 테이블에 인터넷으로 향하는 경로가 있는지 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O6bLo/btsKcgxtyA0/k8siinin6HKYORcUTLc9Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O6bLo/btsKcgxtyA0/k8siinin6HKYORcUTLc9Zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O6bLo/btsKcgxtyA0/k8siinin6HKYORcUTLc9Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO6bLo%2FbtsKcgxtyA0%2Fk8siinin6HKYORcUTLc9Zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;586&quot; height=&quot;430&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. public subnet C 선택 후, 세부 정보 탭에서 변경된 라우팅 테이블 하이퍼 링크를 클릭하면 라우팅 정보 확인 가능&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V4Nub/btsKdGII7N5/jtCIeX2OM29RRVjHp4k3OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V4Nub/btsKdGII7N5/jtCIeX2OM29RRVjHp4k3OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V4Nub/btsKdGII7N5/jtCIeX2OM29RRVjHp4k3OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV4Nub%2FbtsKdGII7N5%2FjtCIeX2OM29RRVjHp4k3OK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;687&quot; height=&quot;346&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;644&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;4. 현재까지 아키텍처 구성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;1202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg0Zn9/btsKbMYbLrD/uLwVaaHG9zHwnY9GPnNkJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg0Zn9/btsKbMYbLrD/uLwVaaHG9zHwnY9GPnNkJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg0Zn9/btsKbMYbLrD/uLwVaaHG9zHwnY9GPnNkJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg0Zn9%2FbtsKbMYbLrD%2FuLwVaaHG9zHwnY9GPnNkJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;543&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;1202&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-4. 보안그룹 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 그룹은 인스턴스에 대한 인바운드 및 아운바운드 트래픽을 제어하는 가상 방화벽 역할을 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 왼쪽 사이드바에서 보안그룹 메뉴 클릭 후, 보안 그룹 생성 버튼 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sY8dH/btsKduaBOSJ/GTMXedVGwibyxxj8rEaCE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sY8dH/btsKduaBOSJ/GTMXedVGwibyxxj8rEaCE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sY8dH/btsKduaBOSJ/GTMXedVGwibyxxj8rEaCE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsY8dH%2FbtsKduaBOSJ%2FGTMXedVGwibyxxj8rEaCE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;105&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 아래 사진처럼 보안 그룹 및 설명 입력 후, 아까 생성한 VPC 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsULMS/btsKdMa1WHr/A5Od0sxZAKp1asVM7QGiNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsULMS/btsKdMa1WHr/A5Od0sxZAKp1asVM7QGiNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsULMS/btsKdMa1WHr/A5Od0sxZAKp1asVM7QGiNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsULMS%2FbtsKdMa1WHr%2FA5Od0sxZAKp1asVM7QGiNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;389&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;585&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;3. 인바운드 규칙에서 아래 사진처럼 규칙 부여 후, 보안 그룹 생성 버튼 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1fA7D/btsKbLLEkVh/bu2lHvRS2Iw8Vf82cVa8K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1fA7D/btsKbLLEkVh/bu2lHvRS2Iw8Vf82cVa8K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1fA7D/btsKbLLEkVh/bu2lHvRS2Iw8Vf82cVa8K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1fA7D%2FbtsKbLLEkVh%2Fbu2lHvRS2Iw8Vf82cVa8K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;155&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;227&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 웹 서버 생성하기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2는 AWS 클라우드에서 확장 가능한 컴퓨팅 용량 제공함&lt;/li&gt;
&lt;li&gt;EC2 사용 시, 하드웨어 선 투자할 필요가 없어 빠른 개발 및 배포 가능&lt;/li&gt;
&lt;li&gt;원하는 만큼 가상 서버 구축하고 보안 및 네트워크 구성과 스토리지 관리 가능&lt;/li&gt;
&lt;li&gt;EC2는 트래픽 증가와 변동 사항에도 신속하게 규모 확장 및 축소 가능하여 서버 트래픽 예측 필요성이 줄어듦&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1. 웹 서버 인스턴스 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스는 아래 단계를 통해 생성된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AMI 선택&lt;/li&gt;
&lt;li&gt;인스턴스 유형 선택&lt;/li&gt;
&lt;li&gt;인스턴스 구성&lt;/li&gt;
&lt;li&gt;스토리지 추가&lt;/li&gt;
&lt;li&gt;태그 추가&lt;/li&gt;
&lt;li&gt;보안 그룹 구성&lt;/li&gt;
&lt;li&gt;검토&lt;/li&gt;
&lt;/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;1. EC2 콘솔홈에서 인스턴스 시작을 클릭하고, 이름에 webserver 1 입력&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rlxwo/btsKecVPyc8/heHegppf1QQL7X1cKFKnVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rlxwo/btsKecVPyc8/heHegppf1QQL7X1cKFKnVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rlxwo/btsKecVPyc8/heHegppf1QQL7X1cKFKnVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frlxwo%2FbtsKecVPyc8%2FheHegppf1QQL7X1cKFKnVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;288&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;추가 태그 추가라는 것은 AWS 리소스에는 태그 형태로 메타 데이터를 지정할 수 있다. 태그를 사용하면 리소스를 쉽게 관리, 식별, 정리, 검색 및 필터링을 할 수 있다. 태그를 생성하여 용도, 소유자, 환경 또는 기타 기준으로 리소스를 분류할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 애플리케이션 및 OS 이미지에서는 Quick Start 에서 아래와 같이 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;945&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/okU0W/btsKeUNDxwk/SjGONJmduJ79TpgeZCIwuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/okU0W/btsKeUNDxwk/SjGONJmduJ79TpgeZCIwuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/okU0W/btsKeUNDxwk/SjGONJmduJ79TpgeZCIwuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FokU0W%2FbtsKeUNDxwk%2FSjGONJmduJ79TpgeZCIwuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;514&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;945&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;3. 인스턴스 유형은 t2.micro 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN8DA4/btsKcWfBbrw/3IUBAAZ7Sj2omFKxlKLpW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN8DA4/btsKcWfBbrw/3IUBAAZ7Sj2omFKxlKLpW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN8DA4/btsKcWfBbrw/3IUBAAZ7Sj2omFKxlKLpW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN8DA4%2FbtsKcWfBbrw%2F3IUBAAZ7Sj2omFKxlKLpW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;186&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;193&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;4. 키 페어에서는 새 키 페어 생성을 선택하여 아래와 같이 키 페어를 생성한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzdAC6/btsKeesCnV0/cpKmoBY4V5RxaV3iV6ZjZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzdAC6/btsKeesCnV0/cpKmoBY4V5RxaV3iV6ZjZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzdAC6/btsKeesCnV0/cpKmoBY4V5RxaV3iV6ZjZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzdAC6%2FbtsKeesCnV0%2FcpKmoBY4V5RxaV3iV6ZjZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;160&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSbxP6/btsKdobCRGu/HnYtgGOf5vnvfcAwLLbc30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSbxP6/btsKdobCRGu/HnYtgGOf5vnvfcAwLLbc30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSbxP6/btsKdobCRGu/HnYtgGOf5vnvfcAwLLbc30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSbxP6%2FbtsKdobCRGu%2FHnYtgGOf5vnvfcAwLLbc30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;510&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;554&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;5. 네트워크 설정에서 편집 버튼을 클릭하고 VPC와 서브넷, 퍼블릭 IP 자동 할당 세션에 다음 값을 입력&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkf7e0/btsKeydgJ4u/I4rzeaHk0lobm0NrOXa2z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkf7e0/btsKeydgJ4u/I4rzeaHk0lobm0NrOXa2z1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkf7e0/btsKeydgJ4u/I4rzeaHk0lobm0NrOXa2z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdkf7e0%2FbtsKeydgJ4u%2FI4rzeaHk0lobm0NrOXa2z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;549&quot; height=&quot;436&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvPHpr/btsKckVjOI7/IAO1DigFgYTK5h94HfJPSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvPHpr/btsKckVjOI7/IAO1DigFgYTK5h94HfJPSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvPHpr/btsKckVjOI7/IAO1DigFgYTK5h94HfJPSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvPHpr%2FbtsKckVjOI7%2FIAO1DigFgYTK5h94HfJPSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;253&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;291&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;6. 방화벽(보안 그룹)은 기존 보안 그룹 선택하여 아까 생성한 그룹을 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lsSQ7/btsKd1NJz2l/JkR6KqKH9wBaReODonRayk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lsSQ7/btsKd1NJz2l/JkR6KqKH9wBaReODonRayk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lsSQ7/btsKd1NJz2l/JkR6KqKH9wBaReODonRayk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlsSQ7%2FbtsKd1NJz2l%2FJkR6KqKH9wBaReODonRayk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;283&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;286&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;7. 스토리지 구성에서는 기본 값인 8 GiB gp2 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdpjFE/btsKdw1K9RO/dLuYNQUvPQIKcXOrIEadq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdpjFE/btsKdw1K9RO/dLuYNQUvPQIKcXOrIEadq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdpjFE/btsKdw1K9RO/dLuYNQUvPQIKcXOrIEadq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdpjFE%2FbtsKdw1K9RO%2FdLuYNQUvPQIKcXOrIEadq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;360&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;658&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;8. 고급 세부 정보를 펼쳐서 가장 아래로 스크롤 하면 사용자 데이터가 보인다. 그 칸에 아래 내용을 붙여넣는다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1729494668390&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh
        
# Install a LAMP stack
sudo amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2
sudo yum -y install httpd php-mbstring
sudo yum -y install git
        
# Start the web server
sudo chkconfig httpd on
sudo systemctl start httpd
        
# Install the web pages for our lab
if [ ! -f /var/www/html/aws-boarding-pass-webapp.tar.gz ]; then
    cd /var/www/html
    wget -O 'techcamp-webapp-2024.zip' 'https://ws-assets-prod-iad-r-icn-ced060f0d38bc0b0.s3.ap-northeast-2.amazonaws.com/600420b7-5c4c-498f-9b80-bc7798963ba3/techcamp-webapp-2024.zip'
    unzip techcamp-webapp-2024.zip
    sudo mv techcamp-webapp-2024/* .
    sudo rm -rf techcamp-webapp-2024
    sudo rm -rf techcamp-webapp-2024.zip
fi
        
# Install the AWS SDK for PHP
if [ ! -f /var/www/html/aws.zip ]; then
    cd /var/www/html
    sudo mkdir vendor
    cd vendor
    sudo wget https://docs.aws.amazon.com/aws-sdk-php/v3/download/aws.zip
    sudo unzip aws.zip
fi
        
# Update existing packages
sudo yum -y update&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTpO1C/btsKed8jhw2/6AguiytnTaKqBHPab0WJR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTpO1C/btsKed8jhw2/6AguiytnTaKqBHPab0WJR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTpO1C/btsKed8jhw2/6AguiytnTaKqBHPab0WJR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTpO1C%2FbtsKed8jhw2%2F6AguiytnTaKqBHPab0WJR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;404&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 이제 인스턴스를 시작하고 인스턴스 상태가 실행중으로 된다면 퍼블릭 IP 주소를 브라우저로 켜보면 아래 화면이 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTzYOY/btsKdMXAw2E/sXYhxTbl6nwSCEC03ac2yK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTzYOY/btsKdMXAw2E/sXYhxTbl6nwSCEC03ac2yK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTzYOY/btsKdMXAw2E/sXYhxTbl6nwSCEC03ac2yK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTzYOY%2FbtsKdMXAw2E%2FsXYhxTbl6nwSCEC03ac2yK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;295&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;318&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;10. 현재까지 아키텍처 구조&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;942&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rt65K/btsKdMJ1I84/5j0mKcKo0zakaQaajOVkL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rt65K/btsKdMJ1I84/5j0mKcKo0zakaQaajOVkL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rt65K/btsKdMJ1I84/5j0mKcKo0zakaQaajOVkL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frt65K%2FbtsKdMJ1I84%2F5j0mKcKo0zakaQaajOVkL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;397&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;942&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2. AMI 생성하기&lt;/h3&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)는 인스턴스를 시작하는데 필요한 정보 제공&lt;/li&gt;
&lt;li&gt;인스턴스 시작 시, AMI를 지정해야하는데 동일한 구성의 인스턴스가 여러 개 필요할 때는 한 AMI를 사용하여 여러 인스턴스 시작 가능&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;1. 앞서 생성한 인스턴스를 클릭하고 작업 메뉴에서 이미지 및 템플릿 클릭하고 이미지 생성을 클릭한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rTizZ/btsKdMQ54wn/KXsD8gpthfuev3QzmHYAFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rTizZ/btsKdMQ54wn/KXsD8gpthfuev3QzmHYAFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rTizZ/btsKdMQ54wn/KXsD8gpthfuev3QzmHYAFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrTizZ%2FbtsKdMQ54wn%2FKXsD8gpthfuev3QzmHYAFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;679&quot; height=&quot;146&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이미지 생성 페이지에서 아래와 같이 값(webserver-ami) 입력하고 우측 하단의 이미지 생성 버튼 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjMiB0/btsKdeN4F6o/LriaUv6apKTecVmIa4Fdu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjMiB0/btsKdeN4F6o/LriaUv6apKTecVmIa4Fdu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjMiB0/btsKdeN4F6o/LriaUv6apKTecVmIa4Fdu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjMiB0%2FbtsKdeN4F6o%2FLriaUv6apKTecVmIa4Fdu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;346&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;634&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;3. 왼쪽 사이드 바에서 AMI 메뉴 클릭 후, 방금 생성한 이미지 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XcpSv/btsKdTiiAIj/KVTrOdxKVsc7w6KaWwJBIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XcpSv/btsKdTiiAIj/KVTrOdxKVsc7w6KaWwJBIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XcpSv/btsKdTiiAIj/KVTrOdxKVsc7w6KaWwJBIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXcpSv%2FbtsKdTiiAIj%2FKVTrOdxKVsc7w6KaWwJBIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;84&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;150&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;4. 현재까지 아키텍처 구성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brsEKX/btsKd0VT8of/HD7fLcQ8BuxIbEbR5IEFRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brsEKX/btsKd0VT8of/HD7fLcQ8BuxIbEbR5IEFRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brsEKX/btsKd0VT8of/HD7fLcQ8BuxIbEbR5IEFRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrsEKX%2FbtsKd0VT8of%2FHD7fLcQ8BuxIbEbR5IEFRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;423&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-3. AMI 기반 인스턴스 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. AMI 메뉴에서 상태 변경이 완료되면 AMI로 인스턴스 시작 버튼 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zKizO/btsKddaECZt/JXCDPguuPxFgGPmFRZrV7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zKizO/btsKddaECZt/JXCDPguuPxFgGPmFRZrV7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zKizO/btsKddaECZt/JXCDPguuPxFgGPmFRZrV7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzKizO%2FbtsKddaECZt%2FJXCDPguuPxFgGPmFRZrV7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;155&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이름 및 태그에 이름을 webserver 2 로 지정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJTobk/btsKdOnRgoK/LxbnFtxhrf0JPQ2KI2Cwo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJTobk/btsKdOnRgoK/LxbnFtxhrf0JPQ2KI2Cwo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJTobk/btsKdOnRgoK/LxbnFtxhrf0JPQ2KI2Cwo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJTobk%2FbtsKdOnRgoK%2FLxbnFtxhrf0JPQ2KI2Cwo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;269&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;290&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;3. 키 페어(로그인)에서 예전에 생성한 키 페어인 keypair-seoul 로 지정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biu6ff/btsKdEFAOaR/I799UakkR8KSetlI7dJbe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biu6ff/btsKdEFAOaR/I799UakkR8KSetlI7dJbe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biu6ff/btsKdEFAOaR/I799UakkR8KSetlI7dJbe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbiu6ff%2FbtsKdEFAOaR%2FI799UakkR8KSetlI7dJbe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;165&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;180&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;4. 네트워크 설정에서 편집 클릭 후, 아래와 같이 값 입력하고 인스턴스 시작 버튼 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c60klh/btsKezp3gO5/DQ0FSkH0dDqe7O4qBIXiOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c60klh/btsKezp3gO5/DQ0FSkH0dDqe7O4qBIXiOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c60klh/btsKezp3gO5/DQ0FSkH0dDqe7O4qBIXiOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc60klh%2FbtsKezp3gO5%2FDQ0FSkH0dDqe7O4qBIXiOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;375&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DiGVA/btsKdnRAPeK/dk5VSWbRKXVBxTQXX33h60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DiGVA/btsKdnRAPeK/dk5VSWbRKXVBxTQXX33h60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DiGVA/btsKdnRAPeK/dk5VSWbRKXVBxTQXX33h60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDiGVA%2FbtsKdnRAPeK%2Fdk5VSWbRKXVBxTQXX33h60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;553&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;592&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;5. 왼쪽 사이드 바에서 인스턴스 메뉴를 클릭하고 방금 생성한 webserver 2 퍼블릭 IP 주소를 복사하여 웹 브라우저에 붙여 넣으면 아래 그림이 나온다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Czpu6/btsKerZ2ciP/ExqSRrOxgnA08Vs0JmElOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Czpu6/btsKerZ2ciP/ExqSRrOxgnA08Vs0JmElOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Czpu6/btsKerZ2ciP/ExqSRrOxgnA08Vs0JmElOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCzpu6%2FbtsKerZ2ciP%2FExqSRrOxgnA08Vs0JmElOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;668&quot; height=&quot;299&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnbX1B/btsKcSq0LKE/8puK7fvN6DYMmgWHm1KKP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnbX1B/btsKcSq0LKE/8puK7fvN6DYMmgWHm1KKP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnbX1B/btsKcSq0LKE/8puK7fvN6DYMmgWHm1KKP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnbX1B%2FbtsKcSq0LKE%2F8puK7fvN6DYMmgWHm1KKP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;190&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;342&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;6. 현재까지 아키텍처 구성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;968&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zkMF2/btsKeFX18s6/xHPJ0hXNGSkfbPPbsk2vC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zkMF2/btsKeFX18s6/xHPJ0hXNGSkfbPPbsk2vC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zkMF2/btsKeFX18s6/xHPJ0hXNGSkfbPPbsk2vC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzkMF2%2FbtsKeFX18s6%2FxHPJ0hXNGSkfbPPbsk2vC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;434&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;968&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 로드밸런서 구성하기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Elastic Load Balancing(ELB)는 들어오는 애플리케이션 트래픽을 Amazon EC2 인스턴스, 컨테이너, IP 주소, Lambda 함수, 가상 어플라이언스와 같은 여러 대상에 자동으로 분산시킨다&lt;/li&gt;
&lt;li&gt;단일 가용 영역 또는 여러 가용 영역에서 다양한 애플리케이션 부하를 처리할 수 있다&lt;/li&gt;
&lt;li&gt;ELB가 제공하는 네 가지 로드 밸런서는 모두 애플리케이션의 내결함성에 필요한 고가용성, 자동 조정, 강력한 보안을 갖춤
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Application Load Balancer&lt;/li&gt;
&lt;li&gt;Network Load Balancer&lt;/li&gt;
&lt;li&gt;Gateway Load Balancer&lt;/li&gt;
&lt;li&gt;Classic Load Balancer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 왼쪽 사이드 바에서 로드 밸런서 클릭 후, 생성 버튼 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnngaX/btsKezje3Q7/d1osJrNWW71mjzhV00EJUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnngaX/btsKezje3Q7/d1osJrNWW71mjzhV00EJUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnngaX/btsKezje3Q7/d1osJrNWW71mjzhV00EJUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnngaX%2FbtsKezje3Q7%2Fd1osJrNWW71mjzhV00EJUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;285&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Load Balancer 유형 중 Application Load Balancer 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;607&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DJaRc/btsKez4BdMu/yjSz7K5waXFusXqthmjC9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DJaRc/btsKez4BdMu/yjSz7K5waXFusXqthmjC9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DJaRc/btsKez4BdMu/yjSz7K5waXFusXqthmjC9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDJaRc%2FbtsKez4BdMu%2FyjSz7K5waXFusXqthmjC9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;491&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;607&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;3. Load Balancer 구성에서 아래와 같이 값을 입력한 후, 스크롤 내림&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PF53D/btsKepum0f3/UHKBJNjMkb46kZiQkdcKQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PF53D/btsKepum0f3/UHKBJNjMkb46kZiQkdcKQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PF53D/btsKepum0f3/UHKBJNjMkb46kZiQkdcKQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPF53D%2FbtsKepum0f3%2FUHKBJNjMkb46kZiQkdcKQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;397&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;리스너는 연결 요청을 확인하는 프로세스이다. 리스너는 프로토콜 및 포트로 구성되며 로드밸런서의 앞단과 뒷단을 연결하는 역할을 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. VPC에서는 생성했던 VPC-Lab 선택하고 매핑 항목에서는 public subent A와 public subnet C 선택한다. 해당 작업은 로드밸런서에서 트래픽을 라우팅할 VPC와 가용 영역을 지정하는 작업이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdtfnD/btsKdZ3I1Jj/SvUOV63bcXALgnskDchxN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdtfnD/btsKdZ3I1Jj/SvUOV63bcXALgnskDchxN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdtfnD/btsKdZ3I1Jj/SvUOV63bcXALgnskDchxN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdtfnD%2FbtsKdZ3I1Jj%2FSvUOV63bcXALgnskDchxN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;467&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;582&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;5. 보안 그룹에서 새 보안 그룹 생성 버튼을 클릭하여 아래와 같이 값 입력하고 보안 그룹을 생성한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brT4S8/btsKdw12fUk/UjRlai8KtUNMQdZAE936E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brT4S8/btsKdw12fUk/UjRlai8KtUNMQdZAE936E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brT4S8/btsKdw12fUk/UjRlai8KtUNMQdZAE936E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrT4S8%2FbtsKdw12fUk%2FUjRlai8KtUNMQdZAE936E0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;190&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMM2Uv/btsKdv9WZTl/oeBtzIMkBDZxvF7nc2Tgzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMM2Uv/btsKdv9WZTl/oeBtzIMkBDZxvF7nc2Tgzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMM2Uv/btsKdv9WZTl/oeBtzIMkBDZxvF7nc2Tgzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMM2Uv%2FbtsKdv9WZTl%2FoeBtzIMkBDZxvF7nc2Tgzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;294&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;465&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;6. 다시 로드 밸런서 화면으로 돌아와서 방금 만든 보안 그룹으로 지정한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bph8G7/btsKduiQGE2/bDQ5fPzKFLBt9Ki2KGKnnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bph8G7/btsKduiQGE2/bDQ5fPzKFLBt9Ki2KGKnnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bph8G7/btsKduiQGE2/bDQ5fPzKFLBt9Ki2KGKnnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbph8G7%2FbtsKduiQGE2%2FbDQ5fPzKFLBt9Ki2KGKnnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;164&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;175&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;7. 리스너 및 라우팅에서 대상 그룹 생성 버튼을 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs3lNF/btsKeMJlHEn/xj9KTVYWZtkhdgD6tkfa01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs3lNF/btsKeMJlHEn/xj9KTVYWZtkhdgD6tkfa01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs3lNF/btsKeMJlHEn/xj9KTVYWZtkhdgD6tkfa01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs3lNF%2FbtsKeMJlHEn%2Fxj9KTVYWZtkhdgD6tkfa01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;228&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;254&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;8. 대상 유형 선택에는 인스턴스를 선택하고 대상 그룹 이름은 webserver-tg 로 지정한다. 프로토콜은 HTTP로 선택한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;591&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8QBsC/btsKdL5LGw8/9qZhZlPKieNwCsYyINakLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8QBsC/btsKdL5LGw8/9qZhZlPKieNwCsYyINakLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8QBsC/btsKdL5LGw8/9qZhZlPKieNwCsYyINakLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8QBsC%2FbtsKdL5LGw8%2F9qZhZlPKieNwCsYyINakLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;564&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;591&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;9. VPC에는 생성했던 VPC 지정하고 대상 등록에 보이는 두 개의 인스턴스를 아래에 보류중인 것으로 포함 버튼을 통해 대상으로 등록한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFs4ZA/btsKc3eIAno/gVnVkvrZqGMAlKjFNM47e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFs4ZA/btsKc3eIAno/gVnVkvrZqGMAlKjFNM47e1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFs4ZA/btsKc3eIAno/gVnVkvrZqGMAlKjFNM47e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFs4ZA%2FbtsKc3eIAno%2FgVnVkvrZqGMAlKjFNM47e1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;212&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpx3QE/btsKdMDzE2w/ma06kmgJonceTNN46d5Mmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpx3QE/btsKdMDzE2w/ma06kmgJonceTNN46d5Mmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpx3QE/btsKdMDzE2w/ma06kmgJonceTNN46d5Mmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpx3QE%2FbtsKdMDzE2w%2Fma06kmgJonceTNN46d5Mmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;265&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;485&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;10. 대상 그룹이 생성되면 로드 밸런서 화면으로 돌아와 리스너 및 라우팅에서 대상 그룹으로 지정하고 이후 로드밸런서를 생성한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXeGt4/btsKeDTrwW5/yfLWr6HPMtsAtLQd7RIx31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXeGt4/btsKeDTrwW5/yfLWr6HPMtsAtLQd7RIx31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXeGt4/btsKeDTrwW5/yfLWr6HPMtsAtLQd7RIx31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXeGt4%2FbtsKeDTrwW5%2FyfLWr6HPMtsAtLQd7RIx31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;238&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11. 로드밸런서를 통하여 웹 애플리케이션에 접근하기 전에 생성했었던 웹 서버가 로드 밸런서의 트래픽만 받게 하기 위하여 보안 그룹 인바운드 규칙을 편집한다. 보안 그룹 메뉴에서 webserver-sg 보안 그룹 선택하여 편집을 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1wuUF/btsKeKSsqz9/rAZaW11xpb8KXtsfsfpOhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1wuUF/btsKeKSsqz9/rAZaW11xpb8KXtsfsfpOhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1wuUF/btsKeKSsqz9/rAZaW11xpb8KXtsfsfpOhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1wuUF%2FbtsKeKSsqz9%2FrAZaW11xpb8KXtsfsfpOhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;282&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;521&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;293&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjJVm2/btsKfbWkxIo/APMDhlDS3x26xWJYpYOCNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjJVm2/btsKfbWkxIo/APMDhlDS3x26xWJYpYOCNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjJVm2/btsKfbWkxIo/APMDhlDS3x26xWJYpYOCNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjJVm2%2FbtsKfbWkxIo%2FAPMDhlDS3x26xWJYpYOCNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;183&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;293&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mfugI/btsKdREPSa6/L2kNjMgP5sv6L19GLHzEdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mfugI/btsKdREPSa6/L2kNjMgP5sv6L19GLHzEdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mfugI/btsKdREPSa6/L2kNjMgP5sv6L19GLHzEdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmfugI%2FbtsKdREPSa6%2FL2kNjMgP5sv6L19GLHzEdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;232&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;370&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;12. 로드밸런서 메뉴로 들어가 방금 생성한 로드밸런서의 DNS이름을 웹 브라우저에 붙여넣으면 아래와 같이 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl6QGN/btsKesY6agF/KNN0iGJyTkpGc58KQNmSe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl6QGN/btsKesY6agF/KNN0iGJyTkpGc58KQNmSe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl6QGN/btsKesY6agF/KNN0iGJyTkpGc58KQNmSe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl6QGN%2FbtsKesY6agF%2FKNN0iGJyTkpGc58KQNmSe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;264&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;485&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VLEHW/btsKeIf6op8/XKKXoLaHPQWJVZv0INeH21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VLEHW/btsKeIf6op8/XKKXoLaHPQWJVZv0INeH21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VLEHW/btsKeIf6op8/XKKXoLaHPQWJVZv0INeH21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVLEHW%2FbtsKeIf6op8%2FXKKXoLaHPQWJVZv0INeH21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;626&quot; height=&quot;236&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;로드밸런서 웹 페이지를 새로고침하면 Application Load Balancer에서 기본으로 제공하는 라운드 로빈 알고리즘에 따라 InstanceID, Availabilty Zone 값이 변경되는 것을 확인할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;13. 현재까지 아키텍처 구성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btfR9W/btsKdZJBuac/XoVK9nWSHB5oF2193lBIVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btfR9W/btsKdZJBuac/XoVK9nWSHB5oF2193lBIVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btfR9W/btsKdZJBuac/XoVK9nWSHB5oF2193lBIVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtfR9W%2FbtsKdZJBuac%2FXoVK9nWSHB5oF2193lBIVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;432&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;946&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AWS</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/191</guid>
      <comments>https://ssdragon.tistory.com/191#entry191comment</comments>
      <pubDate>Tue, 22 Oct 2024 01:58:08 +0900</pubDate>
    </item>
    <item>
      <title>AWS 핵심 서비스로 웹 애플리케이션 구축하기 - 이론</title>
      <link>https://ssdragon.tistory.com/190</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS&amp;nbsp;핵심&amp;nbsp;서비스로&amp;nbsp;웹&amp;nbsp;애플리케이션&amp;nbsp;구축하기&amp;nbsp;-&amp;nbsp;이론&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/191&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS 핵심 서비스로 웹 애플리케이션 구축하기 - 실습&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;이번 실습으로 아래 아키텍처 설계에 따른 인프라 구축을 해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 전에 이론 부분들을 살펴보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;915&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkIl78/btsKdFEDJk9/DWOKfSLUJbqghTj7HNwsjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkIl78/btsKdFEDJk9/DWOKfSLUJbqghTj7HNwsjk/img.png&quot; data-alt=&quot;최종 아키텍처 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkIl78/btsKdFEDJk9/DWOKfSLUJbqghTj7HNwsjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkIl78%2FbtsKdFEDJk9%2FDWOKfSLUJbqghTj7HNwsjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;557&quot; height=&quot;398&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;915&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최종 아키텍처 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. AWS Region (리전)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전세계에 AWS 데이터센터가 클러스터 형태로 위치한 물리적 위치&lt;/li&gt;
&lt;li&gt;1개의 리전은 고가용성, 확장성, 내결함성을 위해 3개 이상의 AZ로 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3210&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mrgYs/btsJV4KUW7S/ssDBWCc2Ku62qEx0YAFf80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mrgYs/btsJV4KUW7S/ssDBWCc2Ku62qEx0YAFf80/img.png&quot; data-alt=&quot;AWS Region, AZ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mrgYs/btsJV4KUW7S/ssDBWCc2Ku62qEx0YAFf80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmrgYs%2FbtsJV4KUW7S%2FssDBWCc2Ku62qEx0YAFf80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;214&quot; data-origin-width=&quot;3210&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS Region, AZ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Availability Zones (가용영역, AZ)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 AWS Region은 지리적 영역 내에서 격리되고 물리적으로 분리된 최소 3개의 AZ로 구성&lt;/li&gt;
&lt;li&gt;모든 가용 영역은 AWS 리전의 중복 전력, 네트워크 및 연결이 제공되는 하나 이상의 개별 데이터 센터로 구성&lt;/li&gt;
&lt;li&gt;AZ 간에는 높은 대역폭, 10ms 이하 짧은 지연시간의 네트워크로 구성&lt;/li&gt;
&lt;li&gt;AZ 간 모든 트래픽 암호화&lt;/li&gt;
&lt;li&gt;모든 AZ는 서로 100km 이내 거리 위치&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wptos/btsJVUaIKOM/pVnN0gad5qHuliWtHK9AW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wptos/btsJVUaIKOM/pVnN0gad5qHuliWtHK9AW0/img.png&quot; data-alt=&quot;AWS Region, AZ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wptos/btsJVUaIKOM/pVnN0gad5qHuliWtHK9AW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWptos%2FbtsJVUaIKOM%2FpVnN0gad5qHuliWtHK9AW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;280&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS Region, AZ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Amazon VPC&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Virtual Private Cloud&lt;/li&gt;
&lt;li&gt;사용자가 정의한 논리적으로 &lt;b&gt;격리된 가상의 프리이빗 네트워크 환경&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;간단한 구성만으로도 AWS의 확장 가능한 인프라를 기존 데이터센터 환경과 유사하게 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;1388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JdIXK/btsJV8mdlEB/2u3Nf2YQxsdFmoUQUGTJS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JdIXK/btsJV8mdlEB/2u3Nf2YQxsdFmoUQUGTJS0/img.png&quot; data-alt=&quot;Amazon VPC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JdIXK/btsJV8mdlEB/2u3Nf2YQxsdFmoUQUGTJS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJdIXK%2FbtsJV8mdlEB%2F2u3Nf2YQxsdFmoUQUGTJS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;392&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;1388&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Amazon VPC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1. Amazon VPC 구성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3010&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UZUM8/btsJU8ncU6K/jvRw5hjKBRlAKqToLs8mN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UZUM8/btsJU8ncU6K/jvRw5hjKBRlAKqToLs8mN1/img.png&quot; data-alt=&quot;Amazon VPC 구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UZUM8/btsJU8ncU6K/jvRw5hjKBRlAKqToLs8mN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUZUM8%2FbtsJU8ncU6K%2FjvRw5hjKBRlAKqToLs8mN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;166&quot; data-origin-width=&quot;3010&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Amazon VPC 구성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. VPC 생성할 리전 선택하고 IP 주소 범위 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 하나의 큰 VPC를 용도에 맞게 퍼블릭 서브넷과 프라이빗 서브넷으로 쪼갬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 인터넷과의 통신을 위해 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 라우팅 테이블 규칙을 설정하여 해당 VPC 내의 트래픽 경로 제어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 방화벽 설정하여 접속이 허용된 요청만 접속되도록 네트워크 보호&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 5가지 단계로 VPC를 구성할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2. IP 주소 범위 설정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGVeHd/btsJWVl98vm/ja2OPIycTltvyPlLWRKbKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGVeHd/btsJWVl98vm/ja2OPIycTltvyPlLWRKbKk/img.png&quot; data-alt=&quot;IP 주소 범위 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGVeHd/btsJWVl98vm/ja2OPIycTltvyPlLWRKbKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGVeHd%2FbtsJWVl98vm%2Fja2OPIycTltvyPlLWRKbKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;361&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;1176&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IP 주소 범위 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CIDR 블록 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Classless Inter-Domain Routing&lt;/li&gt;
&lt;li&gt;IP 대역의 시작 주소와 크기(넷마스크)를 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RFC 1918의 사설 IP 대역 사용 권고
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;VPC의 네트워크 범위는 &lt;b&gt;/16 ~ /28&lt;/b&gt;까지 가능&lt;/li&gt;
&lt;li&gt;CIDR는 생성 후 변경 불가능&lt;/li&gt;
&lt;li&gt;&lt;u&gt;향후 직접 연결할 가능성이 있는 네트워크와 주소가 중복되지 않도록 할당 권고&lt;/u&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;On0premises network와 연동 고려&lt;/li&gt;
&lt;li&gt;AWS 내 리전 간 확장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-3. 서브넷 정의&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2296&quot; data-origin-height=&quot;1428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgxVzR/btsJWS31Wyr/nN5Gdtlb2CTEqIIF2CtzGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgxVzR/btsJWS31Wyr/nN5Gdtlb2CTEqIIF2CtzGK/img.png&quot; data-alt=&quot;서브넷 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgxVzR/btsJWS31Wyr/nN5Gdtlb2CTEqIIF2CtzGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgxVzR%2FbtsJWS31Wyr%2FnN5Gdtlb2CTEqIIF2CtzGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;421&quot; data-origin-width=&quot;2296&quot; data-origin-height=&quot;1428&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서브넷 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크를 잘라서 만든 작은 범위의 네트워크 대역을 의미함&lt;/li&gt;
&lt;li&gt;용도에 따른 분리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A 서비스, B 서비스 ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여러 가용영역 배포
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고가용성 확보&lt;/li&gt;
&lt;li&gt;AWS가 예약한 IP 주소들
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10.0.0.0 (네트워크 주소)&lt;/li&gt;
&lt;li&gt;10.0.0.1 (VPC 라우터)&lt;/li&gt;
&lt;li&gt;10.0.0.2 (DNS 용)&lt;/li&gt;
&lt;li&gt;10.0.0.3 (향후 대비용)&lt;/li&gt;
&lt;li&gt;10.0.0.255 (브로드캐스트 주소)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-4. Internet Gateway 설정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3030&quot; data-origin-height=&quot;1222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7aysX/btsJWV7wPO1/x5EG37h3bxEJY7seH7xkuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7aysX/btsJWV7wPO1/x5EG37h3bxEJY7seH7xkuk/img.png&quot; data-alt=&quot;Internet Gateway 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7aysX/btsJWV7wPO1/x5EG37h3bxEJY7seH7xkuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7aysX%2FbtsJWV7wPO1%2Fx5EG37h3bxEJY7seH7xkuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;281&quot; data-origin-width=&quot;3030&quot; data-origin-height=&quot;1222&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Internet Gateway 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에 있는 인터넷과 AWS 내부간에 통신을 하기 위한 연결 통로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-5. 라우팅 테이블 구성 - 로컬 통신&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3082&quot; data-origin-height=&quot;1244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNb0Mv/btsJVaL8Kp3/ZlksFE2unbFXYOp3IHnG5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNb0Mv/btsJVaL8Kp3/ZlksFE2unbFXYOp3IHnG5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNb0Mv/btsJVaL8Kp3/ZlksFE2unbFXYOp3IHnG5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNb0Mv%2FbtsJVaL8Kp3%2FZlksFE2unbFXYOp3IHnG5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;282&quot; data-origin-width=&quot;3082&quot; data-origin-height=&quot;1244&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;라우팅 테이블에서는 실제 VPC 내의 트래픽이 어떤 경로로 이동하게되는지 구성하게 됨&lt;/li&gt;
&lt;li&gt;라우팅 테이블은 서브넷마다 개별적으로 연결 가능&lt;/li&gt;
&lt;li&gt;명시적으로 연결하지 않을 경우 로컬 통신만 가능한 기본 라우팅 테이블로 연결&lt;/li&gt;
&lt;li&gt;라우팅 테이블 규칙은 Destination, Target 으로 구성되며 패킷이 이동할 때마다 Destination 과 일치하는 Target으로 이동&lt;/li&gt;
&lt;li&gt;Destination은 최종 목적지를 의미하며, Target은 패킷을 보낼 곳을 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-6. 라우팅 테이블 구성 - 인터넷 통신&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3104&quot; data-origin-height=&quot;1324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2TUZr/btsJWrlzpfO/LQIm81NnMxk4lGdCgpWMxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2TUZr/btsJWrlzpfO/LQIm81NnMxk4lGdCgpWMxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2TUZr/btsJWrlzpfO/LQIm81NnMxk4lGdCgpWMxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2TUZr%2FbtsJWrlzpfO%2FLQIm81NnMxk4lGdCgpWMxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;297&quot; data-origin-width=&quot;3104&quot; data-origin-height=&quot;1324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라우팅 테이블에서 Target을 인터넷 게이트웨이로 설정하면 해당 트래픽은 외부 인터넷으로 나갈 수 있음&lt;/li&gt;
&lt;li&gt;사진에서 1.2.3.4 패킷은 인터넷 게이트웨이를 거쳐 외부 인터넷으로 나감&lt;/li&gt;
&lt;li&gt;만약 10.0.1.1 로 패킷을 보내면 2개의 Destination에 충족하는데, 이 때는 가장 긴 접두사 매치를 따르게 됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 가장 구체적인 범위를 선택하여 포워딩한다는 의미로 local로 라우팅이 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-7. 퍼블릿 서브넷과 프라이빗 서브넷&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/echDK7/btsJVqOIUQ9/BYlVrsMJtjdzKnk3OWPavk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/echDK7/btsJVqOIUQ9/BYlVrsMJtjdzKnk3OWPavk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/echDK7/btsJVqOIUQ9/BYlVrsMJtjdzKnk3OWPavk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FechDK7%2FbtsJVqOIUQ9%2FBYlVrsMJtjdzKnk3OWPavk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;295&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라우팅 테이블 설정에 따라서 인터넷 게이트웨이를 통하여 외부 통신이 가능한 서브넷은 퍼블릭 서브넷이고, 그렇지 않으면 프라이빗 서브넷으로 분류&lt;/li&gt;
&lt;li&gt;외부 사용자가 접근해야하면 퍼블릭 서브넷, DB처럼 외부 사용자가 접근 못하면 프라이빗 서브넷
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라우팅 테이블을 활용하여 간단히 망분리 환경 구성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-8. 퍼블릭 서브넷 (Public Subnet)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1556&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKGHM2/btsJVG4VgZS/XHntry3fqkLRWFIWIbM5yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKGHM2/btsJVG4VgZS/XHntry3fqkLRWFIWIbM5yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKGHM2/btsJVG4VgZS/XHntry3fqkLRWFIWIbM5yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKGHM2%2FbtsJVG4VgZS%2FXHntry3fqkLRWFIWIbM5yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;306&quot; data-origin-width=&quot;1556&quot; data-origin-height=&quot;686&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;퍼블릭 서브넷에 EC2 구성하면 Public IP 부여받을 수 있음&lt;/li&gt;
&lt;li&gt;인터넷과 통신은 VPC의 인터넷 게이트웨이&lt;/li&gt;
&lt;li&gt;인터넷으로 라우팅은 라우팅 테이블로 설정&lt;/li&gt;
&lt;li&gt;인터넷 통신은 필요한데 그 외의 통신은 원치 않을 때는 프라이빗 서브넷을 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-9. 프라이빗 서브넷 (Private Subnet)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HTDF6/btsJXv8rB4E/XkSaWeSbFAEsx6aYHlc9nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HTDF6/btsJXv8rB4E/XkSaWeSbFAEsx6aYHlc9nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HTDF6/btsJXv8rB4E/XkSaWeSbFAEsx6aYHlc9nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHTDF6%2FbtsJXv8rB4E%2FXkSaWeSbFAEsx6aYHlc9nk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;309&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;670&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;Private IP 주소만 사용하고 Public IP 주소가 필요 없음&lt;/li&gt;
&lt;li&gt;프라이빗 리소스는 Public IP 주소가 없기에 인터넷 사용을 위해 NAT 게이트웨이 필요&lt;/li&gt;
&lt;li&gt;NAT 게이트웨이는 원웨이 아웃 지원
&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;인터넷으로부터 VPC 안의 리소스로 들어오는 인바운드 트래픽은 오직 요청에 의한 응답만 허용&lt;/li&gt;
&lt;li&gt;인터넷에서 VPC 로 요청을 보내 NAT 게이트웨이를 거치려고 하면 실패&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;퍼블릭 서브넷과 프라이빗 서브넷으로 아웃바운드 인터넷 액세스 구현이 가능함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-10. VPC 액세스 제어 : NACL 과 보안그룹&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bePz4p/btsJV87IoOd/uJ9ksILnxxywgPmRJEVKYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bePz4p/btsJV87IoOd/uJ9ksILnxxywgPmRJEVKYk/img.png&quot; data-alt=&quot;VPC에서 액세스를 제어하는 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bePz4p/btsJV87IoOd/uJ9ksILnxxywgPmRJEVKYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbePz4p%2FbtsJV87IoOd%2FuJ9ksILnxxywgPmRJEVKYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;641&quot; height=&quot;270&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VPC에서 액세스를 제어하는 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬과 인터넷 통신이 가능하다면 보안의 관점에서 VPC 리소스에 대한 액세스를 제어해야 함&lt;/li&gt;
&lt;li&gt;서브넷 단위로 제어하는 NACL(낫클), 인스턴스 단위로 트래픽 제어하는 보안그룹&lt;/li&gt;
&lt;li&gt;NACL에서 허용한 트래픽이 보안그룹에 허용되지 않으면 인스턴스 접근 불가능&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;4. Amazon EC2&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1836&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu8Frg/btsJVLyp2Oh/CDb9JXsv23jm5DMzEptcq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu8Frg/btsJVLyp2Oh/CDb9JXsv23jm5DMzEptcq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu8Frg/btsJVLyp2Oh/CDb9JXsv23jm5DMzEptcq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu8Frg%2FbtsJVLyp2Oh%2FCDb9JXsv23jm5DMzEptcq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;240&quot; data-origin-width=&quot;1836&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 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;4-1. EC2 호스트 가상화&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1922&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OupyZ/btsJWwG4Xnl/AYtaxzkKKQpw59hvopQJM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OupyZ/btsJWwG4Xnl/AYtaxzkKKQpw59hvopQJM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OupyZ/btsJWwG4Xnl/AYtaxzkKKQpw59hvopQJM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOupyZ%2FbtsJWwG4Xnl%2FAYtaxzkKKQpw59hvopQJM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;303&quot; data-origin-width=&quot;1922&quot; data-origin-height=&quot;834&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;EC2는 Virtual Machine 환경&lt;/li&gt;
&lt;li&gt;AWS 글로벌 리전에 위치한 각각의 물리적 호스트 위에 하이퍼바이저를 통해 게스트 OS로써 EC2 인스턴스가 동작&lt;/li&gt;
&lt;li&gt;EC2 인스턴스를 사용하기 위해서는 AMI에 대해 알아야 함&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;4-2. Amazon Machine Image (AMI)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스턴스 시작에 필요한 정보 제공&lt;/li&gt;
&lt;li&gt;동일한 구성으로 한 AMI에서 여러 인스턴스 시작&lt;/li&gt;
&lt;li&gt;AMI는 다음을 포함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1개 이상의 Amazon Elastic Block Store (Amazon EBS) 스냅샷 또는 루트 볼륨에 대한 템플릿(운영 체제, 애플리케이션)&lt;/li&gt;
&lt;li&gt;AMI를 사용하여 인스턴스를 시작할 수 있는 AWS 계정을 제어하는 시작 권한&lt;/li&gt;
&lt;li&gt;인스턴스에 연결할 볼륨을 지정하는 블록 디바이스 매핑&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-3. 인스턴스 유형&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2214&quot; data-origin-height=&quot;994&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KyOny/btsJVJtQf8e/8F4LWKTLfci5EkFMrrnIr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KyOny/btsJVJtQf8e/8F4LWKTLfci5EkFMrrnIr0/img.png&quot; data-alt=&quot;EC2 인스턴스 유형&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KyOny/btsJVJtQf8e/8F4LWKTLfci5EkFMrrnIr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKyOny%2FbtsJVJtQf8e%2F8F4LWKTLfci5EkFMrrnIr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;313&quot; data-origin-width=&quot;2214&quot; data-origin-height=&quot;994&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;EC2 인스턴스 유형&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;4-4. 인스턴스 유형 네이밍&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bStWEJ/btsJWhi3Gpn/ilg3rW5VYors7RmMNVuFOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bStWEJ/btsJWhi3Gpn/ilg3rW5VYors7RmMNVuFOk/img.png&quot; data-alt=&quot;인스턴스 유형 네이밍&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bStWEJ/btsJWhi3Gpn/ilg3rW5VYors7RmMNVuFOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbStWEJ%2FbtsJWhi3Gpn%2Filg3rW5VYors7RmMNVuFOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;233&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인스턴스 유형 네이밍&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-5. Elastic Load Balancing&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2024&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPcx9V/btsJWWyHdt9/PXDcZbm6PYQMPpjskOVx91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPcx9V/btsJWWyHdt9/PXDcZbm6PYQMPpjskOVx91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPcx9V/btsJWWyHdt9/PXDcZbm6PYQMPpjskOVx91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPcx9V%2FbtsJWWyHdt9%2FPXDcZbm6PYQMPpjskOVx91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;690&quot; height=&quot;239&quot; data-origin-width=&quot;2024&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-6. Amazon EC2 Auto Scaling&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uFxF1/btsJVr71UfT/ZoqDngz7DW1oXhClDzZQQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uFxF1/btsJVr71UfT/ZoqDngz7DW1oXhClDzZQQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uFxF1/btsJVr71UfT/ZoqDngz7DW1oXhClDzZQQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuFxF1%2FbtsJVr71UfT%2FZoqDngz7DW1oXhClDzZQQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;321&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;654&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;ELB와 같이 사용되는 서비스 중에 EC2 Auto Scaling이 있음&lt;/li&gt;
&lt;li&gt;인스턴스가 비정상 상태일 때 해당 인스턴스 종료하고 새 인스턴스 시작하는 역할&lt;/li&gt;
&lt;li&gt;트래픽 사용량을 통해 자동 확장하거나 축소하는 역할&lt;/li&gt;
&lt;li&gt;인스턴스가 낭비되지 않게 탄력적으로 사용&lt;/li&gt;
&lt;li&gt;변화하는 수요에 동적으로 대응하고 비용 최적화&lt;/li&gt;
&lt;li&gt;인스턴스 시작 시 필요한 정보를 제공해줘야 하는데 이 정보를 시작 템플릿에 담아 오토스케일링에 전달&lt;/li&gt;
&lt;/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;4-7. 시작 템플릿 (Launch Template)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvWDdl/btsJWt4CT3X/9e1FdrsbDfkRkEIjPtUPik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvWDdl/btsJWt4CT3X/9e1FdrsbDfkRkEIjPtUPik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvWDdl/btsJWt4CT3X/9e1FdrsbDfkRkEIjPtUPik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvWDdl%2FbtsJWt4CT3X%2F9e1FdrsbDfkRkEIjPtUPik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;293&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;646&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;시작(launch)을 간소화하고 단순화하기 위해 시작 요청을 템플릿화&lt;/li&gt;
&lt;li&gt;오토스케일링이 시작 템플릿을 기반으로 새 인스턴스 시작&lt;/li&gt;
&lt;li&gt;웹 애플리케이션을 시작할 때 필요한 구성들을 시작 템플릿에 저장하고 운영 가능&lt;/li&gt;
&lt;li&gt;수정 사항 생기면 버전 관리 쉽게 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AWS</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/190</guid>
      <comments>https://ssdragon.tistory.com/190#entry190comment</comments>
      <pubDate>Sun, 6 Oct 2024 23:47:07 +0900</pubDate>
    </item>
    <item>
      <title>AWS Serverless(서버리스)로 서버 고민 없이 웹 애플리케이션 구축하기 #2</title>
      <link>https://ssdragon.tistory.com/189</link>
      <description>&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://ssdragon.tistory.com/188&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;서버리스로 서버 고민없이 웹 애플리케이션 구축하기 #1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;서버리스로 서버 고민없이 웹 애플리케이션 구축하기 #2&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;1편에서 봤듯이 &quot;친구들의 기분 상태를 랜덤으로 매칭하는 서비스&quot;를 서버리스로 구축해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 서버리스 아키텍처 개요&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2608&quot; data-origin-height=&quot;1194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wRFhj/btsJxk7AlJT/1wFG6KHeKlklFnvglIEcV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wRFhj/btsJxk7AlJT/1wFG6KHeKlklFnvglIEcV1/img.png&quot; data-alt=&quot;3-tier serverless architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wRFhj/btsJxk7AlJT/1wFG6KHeKlklFnvglIEcV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwRFhj%2FbtsJxk7AlJT%2F1wFG6KHeKlklFnvglIEcV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;317&quot; data-origin-width=&quot;2608&quot; data-origin-height=&quot;1194&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3-tier serverless architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스 로직을 AWS Lambda(람다) 함수에 작성하고 람다가 실행되면 DynamoDB에 친구들의 기분 상태 데이터를 저장한다. API Gateway를 통해 람다 함수를 접근할 수 있는 URL을 생성하고 S3에 HTML을 업로드하여 S3 정적 웹 사이트 호스팅 기능으로 웹 사이트를 호스팅한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(자세한 내용은 1편에)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. DynamoDB&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전관리형 AWS의 NoSQL 데이터서비스를 이용하여 데이터를 저장하기 위해 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스에 연결할 DynamoDB 테이블을 만들면 멤버의 이름과 성격을 저장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hello-member 이름으로 테이블을 생성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 테이블 생성 누르기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1605&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mlnhB/btsJvmlwcQD/XuRWLbThnhPwysCYpILAPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mlnhB/btsJvmlwcQD/XuRWLbThnhPwysCYpILAPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mlnhB/btsJvmlwcQD/XuRWLbThnhPwysCYpILAPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmlnhB%2FbtsJvmlwcQD%2FXuRWLbThnhPwysCYpILAPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;155&quot; data-origin-width=&quot;1605&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 테이블 이름은 &lt;b&gt;hello-member&lt;/b&gt; 로, 파티션 키는 &lt;b&gt;name&lt;/b&gt; 으로 작성한다. 작성 후 페이지 아래 테이블 생성을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpvIxF/btsJwSjr7Fo/LP1Zxski2VSLY5E1TDmork/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpvIxF/btsJwSjr7Fo/LP1Zxski2VSLY5E1TDmork/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpvIxF/btsJwSjr7Fo/LP1Zxski2VSLY5E1TDmork/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpvIxF%2FbtsJwSjr7Fo%2FLP1Zxski2VSLY5E1TDmork%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;459&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;588&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;3. 아래처럼 활성 상태인 DynamoDB 테이블을 생성했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cr2rR/btsJvcpTA7q/BSKKhRsH6v5XAqmfdjuUyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cr2rR/btsJvcpTA7q/BSKKhRsH6v5XAqmfdjuUyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cr2rR/btsJvcpTA7q/BSKKhRsH6v5XAqmfdjuUyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCr2rR%2FbtsJvcpTA7q%2FBSKKhRsH6v5XAqmfdjuUyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;144&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;239&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;3. Lambda&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 대표적인 서버리스 서비스로 많은 요청이 발생할 때에도 람다는 자동 확장되고 관리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 기능이나 간단한 서비스 만들 때 람다를 이용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e.g. 사진 이미지 썸네일 만드는 기능 추가, 간단한 데이터 분석, AWS 이벤트 서비스들과 연동해서 이벤트 처리 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 함수 생성 누르기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1131&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIpsz9/btsJvx8CEkB/KMziH7I825K7orwknzKHF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIpsz9/btsJvx8CEkB/KMziH7I825K7orwknzKHF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIpsz9/btsJvx8CEkB/KMziH7I825K7orwknzKHF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIpsz9%2FbtsJvx8CEkB%2FKMziH7I825K7orwknzKHF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;240&quot; data-origin-width=&quot;1131&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;&amp;nbsp;&lt;/p&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. 새로 작성(Author from scratch) 를 선택한다. 이는 람다의 모든 설정을 처음부터 만든다는 의미다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0TWG9/btsJwYwYj5h/RYHZFHxGwsTyOeLxJSHuqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0TWG9/btsJwYwYj5h/RYHZFHxGwsTyOeLxJSHuqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0TWG9/btsJwYwYj5h/RYHZFHxGwsTyOeLxJSHuqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0TWG9%2FbtsJwYwYj5h%2FRYHZFHxGwsTyOeLxJSHuqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;194&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;235&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;3. 함수 이름(Function name)은 &lt;b&gt;api-service-create&lt;/b&gt; 를 입력하고, 런타임(Runtime)으로는 &lt;b&gt;python 3.12&lt;/b&gt; 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ui9Wq/btsJwTJpLQl/4zzDegaSuz7YvNYZkLoJZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ui9Wq/btsJwTJpLQl/4zzDegaSuz7YvNYZkLoJZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ui9Wq/btsJwTJpLQl/4zzDegaSuz7YvNYZkLoJZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fui9Wq%2FbtsJwTJpLQl%2F4zzDegaSuz7YvNYZkLoJZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;478&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;559&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;4. 권한(Permission)을 직접 생성한다. 기본 실행 역할 변경(Change default execution role)을 누르고 실행 역할(Execution role)의 &lt;b&gt;AWS 정책 템플릿에서 새 역할 생성(Create a new role from AWS policy templates)&lt;/b&gt;를 선택한다. 역할 이름(Role name)으로는 &lt;b&gt;my-lambda-role&lt;/b&gt; 으로 작성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dh1Rd6/btsJwzqVHhc/1HcZFijjU53a7cmOipqkzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dh1Rd6/btsJwzqVHhc/1HcZFijjU53a7cmOipqkzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dh1Rd6/btsJwzqVHhc/1HcZFijjU53a7cmOipqkzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdh1Rd6%2FbtsJwzqVHhc%2F1HcZFijjU53a7cmOipqkzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;395&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;460&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;5. NoSQL 데이터베이스 서비스인 DynamoDB와 연결하거나 이용하거나 호출하기 위해 권한이 필요하다. &lt;b&gt;단순 마이크로서비스 권한(Simple microservice permissions DynamoDB)&lt;/b&gt;를 선택한다. 그리고 아래 함수 생성을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byZI4Z/btsJwqHIzEJ/wZLbVkLBiEphOYJuqYDAy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byZI4Z/btsJwqHIzEJ/wZLbVkLBiEphOYJuqYDAy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byZI4Z/btsJwqHIzEJ/wZLbVkLBiEphOYJuqYDAy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyZI4Z%2FbtsJwqHIzEJ%2FwZLbVkLBiEphOYJuqYDAy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;237&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 람다 화면에서 코드 소스(Code source) 부분에 아래 코드를 넣고 Deploy 버튼을 누른다.&lt;/p&gt;
&lt;pre id=&quot;code_1725889730919&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
import boto3
import random
import json

def lambda_handler(event, context):
    
    member_name = ['Ama','Jone','Zon','Penny','Jessie']
    member_status = ['Happy','Sad','Serious','Satisfied','Free']
    
    dynamodb = boto3.resource('dynamodb',endpoint_url='http://dynamodb.ap-northeast-2.amazonaws.com')
    member_table = dynamodb.Table('hello-member')
    
    name = member_name[random.randint(0,4)]
    status = member_status[random.randint(0, 4)]
    
    member_table.put_item(
       Item={
            'name': name,
            'status': status,
        }
    )
    
    documents = {'name':name,'status':status}
    
    print(documents)
    
    return {
        'statusCode': 200,
        'headers': {'Access-Control-Allow-Origin': '*'},
        'body': json.dumps(documents)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YAS7A/btsJxELw9jc/DXgRrKtVVD3nZQCOd9Zti0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YAS7A/btsJxELw9jc/DXgRrKtVVD3nZQCOd9Zti0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YAS7A/btsJxELw9jc/DXgRrKtVVD3nZQCOd9Zti0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYAS7A%2FbtsJxELw9jc%2FDXgRrKtVVD3nZQCOd9Zti0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;421&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;678&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;7. Test 버튼을 누르면 테스트 이벤트 구성(Configure test event)가 나온다. 이벤트 이름(Event name)으로는 &lt;b&gt;my-api-test&lt;/b&gt; 를 입력하고, 템플릿(Template)은 &lt;b&gt;hello-world&lt;/b&gt; 를 선택한다. 그리고 저장을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;921&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PEFFx/btsJv9zrurR/xQ2HjzwsH6PWwcFDlnVdf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PEFFx/btsJv9zrurR/xQ2HjzwsH6PWwcFDlnVdf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PEFFx/btsJv9zrurR/xQ2HjzwsH6PWwcFDlnVdf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPEFFx%2FbtsJv9zrurR%2FxQ2HjzwsH6PWwcFDlnVdf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;784&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;921&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;8. Test 버튼을 누르면 테스트가 진행된다. 이제 아래와 같이 결과가 나오면 된다. 출력된 값들은 랜덤하게 선택된 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IM9Fx/btsJxhC371I/yUHDOLuhoBy2nkFNbpKEqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IM9Fx/btsJxhC371I/yUHDOLuhoBy2nkFNbpKEqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IM9Fx/btsJxhC371I/yUHDOLuhoBy2nkFNbpKEqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIM9Fx%2FbtsJxhC371I%2FyUHDOLuhoBy2nkFNbpKEqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;422&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;676&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;9. 테스트 결과가 DynamoDB에 잘 저장됐는지 확인을 해본다. DynamoDB 콘솔창에 들어가서 왼쪽 탐색창에서 테이블을 선택한다. 그리고 기존에 만들었던 hello-member 테이블을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tlmCy/btsJu9z0sNh/15JonMyNe3nOfovlgt1Zp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tlmCy/btsJu9z0sNh/15JonMyNe3nOfovlgt1Zp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tlmCy/btsJu9z0sNh/15JonMyNe3nOfovlgt1Zp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtlmCy%2FbtsJu9z0sNh%2F15JonMyNe3nOfovlgt1Zp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;230&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. 표 항목 탐색(Explore table items) 버튼을 누르면 화면 아래쪽에 람다를 실행해서 얻은 값이 저장된 걸 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;707&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWAQVm/btsJvvwbXJp/XUy30tq4TcqJmI4JXcjrh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWAQVm/btsJvvwbXJp/XUy30tq4TcqJmI4JXcjrh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWAQVm/btsJvvwbXJp/XUy30tq4TcqJmI4JXcjrh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWAQVm%2FbtsJvvwbXJp%2FXUy30tq4TcqJmI4JXcjrh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;702&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;707&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;4. API Gateway&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS의 API 관리 서비스를 이용하여 람다를 실행해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API Gateway는 API 를 관리해주고 API 를 통해 외부에서의 호출이 왔을 때 대문 역할을 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;API 란 외부에서 기업의 서비스를 이용하려고 할 때 규격을 정해주는 것을 의미한다. 일종의 형식을 정해놓고, 이 형식대로 기업의 서비스를 호출하면 기업은 서비스를 제공해 주는 약속이라고 생각하면 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. API Gateway 콘솔창에 들어간다. 왼쪽 탐색창에서 API 를 선택하고 HTTP API 보다 API 관리 기능이 더 추가되어 있는 REST API 로 생성하기 위해 구축 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1113&quot; data-origin-height=&quot;819&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bto7xD/btsJvReAh1e/EPKxtTzg06iPhAn2DI7Np0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bto7xD/btsJvReAh1e/EPKxtTzg06iPhAn2DI7Np0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bto7xD/btsJvReAh1e/EPKxtTzg06iPhAn2DI7Np0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbto7xD%2FbtsJvReAh1e%2FEPKxtTzg06iPhAn2DI7Np0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;515&quot; data-origin-width=&quot;1113&quot; data-origin-height=&quot;819&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;2. API 세부 정보에서 새 API 선택하고, API 이름은 &lt;b&gt;my-api&lt;/b&gt; 지정한다. 그 후 API 생성 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pPq19/btsJxncbkY1/JANKluzSN00R3qeiHCDzh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pPq19/btsJxncbkY1/JANKluzSN00R3qeiHCDzh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pPq19/btsJxncbkY1/JANKluzSN00R3qeiHCDzh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpPq19%2FbtsJxncbkY1%2FJANKluzSN00R3qeiHCDzh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;626&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;748&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;3. API를 생성하면 바로 리소스 화면이 나온다. 여기서 메서드 생성 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3f5oY/btsJwyZRfRc/ABQ8rmaIAYj0oMFCkgvTzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3f5oY/btsJwyZRfRc/ABQ8rmaIAYj0oMFCkgvTzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3f5oY/btsJwyZRfRc/ABQ8rmaIAYj0oMFCkgvTzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3f5oY%2FbtsJwyZRfRc%2FABQ8rmaIAYj0oMFCkgvTzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;314&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;522&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;4. 메서드 유형은 &lt;b&gt;GET&lt;/b&gt; 선택한다. 통합 유형은 &lt;b&gt;Lambda 함수&lt;/b&gt;를 선택하고, &lt;b&gt;Lambda 프록시 통합&lt;/b&gt; 토글 버튼을 클릭하여 활성화한다. Lambda 함수에는 &lt;b&gt;api-service-create&lt;/b&gt; 이름을 작성한다. 이후 메서드 생성 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;757&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6IIoN/btsJxiBYCVz/hYJWLYAPte3tVSQAPdDZvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6IIoN/btsJxiBYCVz/hYJWLYAPte3tVSQAPdDZvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6IIoN/btsJxiBYCVz/hYJWLYAPte3tVSQAPdDZvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6IIoN%2FbtsJxiBYCVz%2FhYJWLYAPte3tVSQAPdDZvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;642&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;757&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;5. 생성한 API의 리소스 메뉴에서 방금 생성한 GET Method를 누르면 오른쪽에 해당 Method 관련된 정보가 보인다. TEST 탭을 선택하여 테스트를 준비한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/18tof/btsJxFp8FxH/b0OoU9EZ5WmCsLr7nu72hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/18tof/btsJxFp8FxH/b0OoU9EZ5WmCsLr7nu72hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/18tof/btsJxFp8FxH/b0OoU9EZ5WmCsLr7nu72hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F18tof%2FbtsJxFp8FxH%2Fb0OoU9EZ5WmCsLr7nu72hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;363&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;589&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;6. 테스트 버튼을 눌러 테스트를 진행한다. 성공했다면 상태(Status)가 200이 나오며, 응답 본문에 랜덤한 값이 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPGFTS/btsJvalmPjy/XsKjqxVhKeWtAaTnyIM6R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPGFTS/btsJvalmPjy/XsKjqxVhKeWtAaTnyIM6R0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPGFTS/btsJvalmPjy/XsKjqxVhKeWtAaTnyIM6R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPGFTS%2FbtsJvalmPjy%2FXsKjqxVhKeWtAaTnyIM6R0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;582&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;687&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;7. API Gateway를 만들었지만 바로 호출하면 CORS 에러가 발생한다. 호출이 정상적으로 이루어지기 위한 추가 설정을 한다. 생성한 API의 리소스 메뉴에서 &lt;b&gt;CORS 활성화&lt;/b&gt; 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EP8Mk/btsJwcpetXH/63jnfXN3vCgvDvxEtaGriK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EP8Mk/btsJwcpetXH/63jnfXN3vCgvDvxEtaGriK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EP8Mk/btsJwcpetXH/63jnfXN3vCgvDvxEtaGriK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEP8Mk%2FbtsJwcpetXH%2F63jnfXN3vCgvDvxEtaGriK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;168&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;279&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;8. Access-Control-Allow-Methods 중 방금 생성한 &lt;b&gt;GET&lt;/b&gt; Method 선택한 후 &lt;b&gt;저장&lt;/b&gt; 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;755&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7NssP/btsJwLrnTqb/v6RPljT8NvpFzpkXmqk5SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7NssP/btsJwLrnTqb/v6RPljT8NvpFzpkXmqk5SK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7NssP/btsJwLrnTqb/v6RPljT8NvpFzpkXmqk5SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7NssP%2FbtsJwLrnTqb%2Fv6RPljT8NvpFzpkXmqk5SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;630&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;755&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;9. 이제 api를 실제로 사용할 수 있게 &lt;b&gt;API 배포&lt;/b&gt; 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wUXB0/btsJwqAYtHu/Lwm80cocSfY0YC8NBF2h8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wUXB0/btsJwqAYtHu/Lwm80cocSfY0YC8NBF2h8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wUXB0/btsJwqAYtHu/Lwm80cocSfY0YC8NBF2h8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwUXB0%2FbtsJwqAYtHu%2FLwm80cocSfY0YC8NBF2h8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;393&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. 팝업 창이 뜨는데 스테이지에서는 &lt;b&gt;*새 스테이지*&lt;/b&gt; 를 선택하고 스테이지 이름은 &lt;b&gt;dev&lt;/b&gt; 입력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDrC7Z/btsJvayRA9Z/n42ToHZgnSwkEykeKcjqp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDrC7Z/btsJvayRA9Z/n42ToHZgnSwkEykeKcjqp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDrC7Z/btsJvayRA9Z/n42ToHZgnSwkEykeKcjqp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDrC7Z%2FbtsJvayRA9Z%2Fn42ToHZgnSwkEykeKcjqp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;569&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11. 완료되면 URL 호출이 나오게 된다. URL은 사람마다 다르며 바로 브라우저를 통해 접속해본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;535&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceNDqH/btsJw4qo9EX/d9xenFxk6vQq7J1wzXGFDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceNDqH/btsJw4qo9EX/d9xenFxk6vQq7J1wzXGFDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceNDqH/btsJw4qo9EX/d9xenFxk6vQq7J1wzXGFDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceNDqH%2FbtsJw4qo9EX%2Fd9xenFxk6vQq7J1wzXGFDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;330&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;535&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;12. URL을 브라우저에 붙여넣어 접속하면 아래와 같이 나온다. 이 작업은 URL을 통해 람다를 호출한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;79&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ELcRC/btsJxnpHZgw/WkBFqw6r1gwSmS7kgwzev0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ELcRC/btsJxnpHZgw/WkBFqw6r1gwSmS7kgwzev0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ELcRC/btsJxnpHZgw/WkBFqw6r1gwSmS7kgwzev0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FELcRC%2FbtsJxnpHZgw%2FWkBFqw6r1gwSmS7kgwzev0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;177&quot; height=&quot;79&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;79&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. AWS S3&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어디서나 원하는 양의 데이터를 검색할 수 있도록 구축된 객체 스토리지이다. 거의 모든 사용 사례를 지원할 수 있고, 비용 효율적인 스토리지 클래스와 사용이 쉬운 관리 기능을 통해 비용을 최적화하고, 데이터를 정리하고, 세분화된 액세스 제어를 구성하여 특정 비즈니스, 조직 및 규정 준수 요구 사항을 충족할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. S3 콘솔창에 들어와서 버킷 만들기 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;323&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B3VMN/btsJwQeSmQy/KkdK3c1Dwe5gxNdcPevbrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B3VMN/btsJwQeSmQy/KkdK3c1Dwe5gxNdcPevbrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B3VMN/btsJwQeSmQy/KkdK3c1Dwe5gxNdcPevbrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB3VMN%2FbtsJwQeSmQy%2FKkdK3c1Dwe5gxNdcPevbrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;201&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;323&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. 버킷 이름에 my-bucket-[랜덤 숫자]를 입력한다. [랜덤 숫자]는 무작위 숫자로 넣으면 되며 대괄호를 빼야한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;버킷 이름은 전세계에서 유일해야 한다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRZRhu/btsJvQfAxSJ/Oc8CYwIIt5reyTCh42Dimk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRZRhu/btsJvQfAxSJ/Oc8CYwIIt5reyTCh42Dimk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRZRhu/btsJvQfAxSJ/Oc8CYwIIt5reyTCh42Dimk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRZRhu%2FbtsJvQfAxSJ%2FOc8CYwIIt5reyTCh42Dimk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;414&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 아래에 &lt;b&gt;이 버킷의 퍼블릭 액세스 차단 설정&lt;/b&gt;이 나오는데 여기서 &lt;b&gt;모든 퍼블릭 액세스 차단&lt;/b&gt; 선택을 해제한다. 그리고 나오는 위험 문구에서 &lt;b&gt;현재 설정으로 인해 이 버킷과 그 안에 포함된 객체가 퍼블릭 상태가 될 수 있음을 알고 있습니다.&lt;/b&gt; 를 선택한다. 그리고 아래 버킷 만들기 버튼을 누른다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;전세계 사람들이 접근하기 때문에 &quot;정적 웹 사이트 호스팅&quot;과 같은 구체적으로 확인된 사용 사례에서 퍼블릭 액세스가 필요한 경우가 아니라면 차단을 활성화하는 것이 좋다&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;631&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbMPnb/btsJvge1S6P/KEKF9QElwe5YKGAbaOKZe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbMPnb/btsJvge1S6P/KEKF9QElwe5YKGAbaOKZe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbMPnb/btsJvge1S6P/KEKF9QElwe5YKGAbaOKZe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbMPnb%2FbtsJvge1S6P%2FKEKF9QElwe5YKGAbaOKZe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;540&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;631&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;4. 아래 HTML 파일을 텍스트 에디터에 붙여 넣고 index.html 파일로 저장한다. 그리고 ajax 요청의 url 부분에 &lt;b&gt;URL을 입력하세요&lt;/b&gt;&amp;nbsp;를 API Gateway의 URL로 변경한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 index.html 의 버튼을 누르면 앞에서 만든 람다가 실행된다&lt;/blockquote&gt;
&lt;pre id=&quot;code_1725892462796&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot; name=&quot;viewport&quot;
        content=&quot;width=device-width, height=device-height, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;title&amp;gt;Hello World!&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        #title {
            font-family: arial;
            font-size: 2em;
            color: #eb971a;
            margin-top: 50px;
            text-align: center;
        }

        button {
            background-color: #eb971a;
            border: none;
            color: white;
            border-radius: 5px;
            width: 40%;
            height: 35px;
            font-size: 13pt;
            margin-top: 30px;
            text-align: center;
        }

        #sentence {
            font-size: 17pt;
            margin-top: 30px;
            font-weight: bold;
            color: #eb971a;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
    &amp;lt;p id=&quot;title&quot;&amp;gt;Hello World From &amp;lt;b&amp;gt;Lambda&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;hr id=&quot;lambda-line&quot; width=&quot;800px&quot; align=&quot;center&quot; color=&quot;#eb971a;&quot;&amp;gt;
    &amp;lt;center&amp;gt;&amp;lt;button onclick=&quot;checkEvent();&quot;&amp;gt;Who are you?&amp;lt;/button&amp;gt;&amp;lt;/center&amp;gt;
    &amp;lt;center&amp;gt;
        &amp;lt;div id=&quot;sentence&quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/center&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
    function checkEvent() {
        $.ajax({
            type: &quot;GET&quot;,
            url: &quot;URL을입력하세요&quot;,
            dataType: 'json',
            success: function (data) {
                document.getElementById('sentence').innerHTML = data.status + &quot;&amp;amp;nbsp;&amp;amp;nbsp;&quot; + data.name
            },
            error: function (error) {
                alert('ERROR::');
                console.log(error)
            }
        });
    }
&amp;lt;/script&amp;gt;

&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 앞서 만든 버킷에서 index.html 파일을 업로드한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pi2Ww/btsJwZWYmvW/rP1zGMCqhK4kEZjp6F33rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pi2Ww/btsJwZWYmvW/rP1zGMCqhK4kEZjp6F33rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pi2Ww/btsJwZWYmvW/rP1zGMCqhK4kEZjp6F33rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpi2Ww%2FbtsJwZWYmvW%2FrP1zGMCqhK4kEZjp6F33rk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;619&quot; height=&quot;320&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;426&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;6. 업로드를 하면 버킷에 아래처럼 html 파일이 보인다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnxptX/btsJvfmTPCu/EcM4Jk17Cx5LWMB6kZIuwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnxptX/btsJvfmTPCu/EcM4Jk17Cx5LWMB6kZIuwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnxptX/btsJvfmTPCu/EcM4Jk17Cx5LWMB6kZIuwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnxptX%2FbtsJvfmTPCu%2FEcM4Jk17Cx5LWMB6kZIuwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;208&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;342&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;7. 버킷의 속성(Properties) 탭에 들어가서 아래쪽으로 내려가면 &lt;b&gt;정적 웹 사이트 호스팅&lt;/b&gt;이 나오며 &lt;b&gt;편집&lt;/b&gt; 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R9DcD/btsJw1ganEK/rtFiM4ctJok6VK8y4Krl60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R9DcD/btsJw1ganEK/rtFiM4ctJok6VK8y4Krl60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R9DcD/btsJw1ganEK/rtFiM4ctJok6VK8y4Krl60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR9DcD%2FbtsJw1ganEK%2FrtFiM4ctJok6VK8y4Krl60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;120&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;197&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;8. 인덱스 문서에 &lt;b&gt;index.html&lt;/b&gt; 입력한다. 처음 주소로 읽어 들일 html 파일을 지정해주는 부분이다. &lt;b&gt;변경 사항 저장&lt;/b&gt; 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qxV5B/btsJwrs6E7P/s4uq8wz7y0uwnRDkfPlk3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qxV5B/btsJwrs6E7P/s4uq8wz7y0uwnRDkfPlk3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qxV5B/btsJwrs6E7P/s4uq8wz7y0uwnRDkfPlk3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqxV5B%2FbtsJwrs6E7P%2Fs4uq8wz7y0uwnRDkfPlk3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;709&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;824&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;9. 설정이 잘 됐다면 정적 웹 사이트 호스팅 기능이 활성화 되며 아래와 같이 주소가 생성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgf6PV/btsJwNbFwZp/217l93OAN1VA5LUGSlIHO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgf6PV/btsJwNbFwZp/217l93OAN1VA5LUGSlIHO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgf6PV/btsJwNbFwZp/217l93OAN1VA5LUGSlIHO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgf6PV%2FbtsJwNbFwZp%2F217l93OAN1VA5LUGSlIHO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;226&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;364&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;10. 아직 외부에서 접근할 수 있는 권한이 없어서 사이트가 차단되어 있다. 외부에서 파일을 읽을 수 있도록 권한을 부여한다. 아래와 같이 &lt;b&gt;권한&lt;/b&gt; 탭으로 이동하여 &lt;b&gt;버킷 정책&lt;/b&gt;에서 &lt;b&gt;편집&lt;/b&gt; 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1123&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JlMnv/btsJvQfAA8B/HDUe2ir9JbAKte2Zsuo2kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JlMnv/btsJvQfAA8B/HDUe2ir9JbAKte2Zsuo2kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JlMnv/btsJvQfAA8B/HDUe2ir9JbAKte2Zsuo2kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJlMnv%2FbtsJvQfAA8B%2FHDUe2ir9JbAKte2Zsuo2kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;389&quot; data-origin-width=&quot;1123&quot; data-origin-height=&quot;630&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;11. 아래 내용을 복사하여 붙여넣는다. 이 때 11,12번째 줄에 &lt;b&gt;[본인버킷번호] &lt;/b&gt;부분을 본인의 버킷 랜덤 숫자를 넣고 &lt;b&gt;저장&lt;/b&gt; 버튼을 누른다. 권한의 내용은 본인 버킷의 내용을 모든 사람이 다운로드할 수 있게 하겠다는 내용이다.&lt;/p&gt;
&lt;pre id=&quot;code_1725893115206&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Sid&quot;: &quot;Stmt1709405011428&quot;,
      &quot;Action&quot;: [
        &quot;s3:GetObject&quot;
      ],
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Resource&quot;: [
        &quot;arn:aws:s3:::my-bucket-[본인버킷번호]&quot;,
        &quot;arn:aws:s3:::my-bucket-[본인버킷번호]/*&quot;
      ],
      &quot;Principal&quot;: &quot;*&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12. 정적 웹사이트 호스팅 주소를 브라우저에 넣으면 아래 화면처럼 나온다. 버튼을 누르면 웹사이트에 사람이름과 기분이 표시된다. 버튼을 누르면 람다가 실행되는데 DynamoDB에 기록이 잘 됐는지 확인하면 끝이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxieoI/btsJxnpIlw2/nwOFreKZZgNXjQa3cop0Qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxieoI/btsJxnpIlw2/nwOFreKZZgNXjQa3cop0Qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxieoI/btsJxnpIlw2/nwOFreKZZgNXjQa3cop0Qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxieoI%2FbtsJxnpIlw2%2FnwOFreKZZgNXjQa3cop0Qk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;198&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;245&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;</description>
      <category>AWS</category>
      <category>api gateway</category>
      <category>AWS</category>
      <category>AWS Serverless</category>
      <category>aws 서버리스</category>
      <category>DynamoDB</category>
      <category>lambda</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/189</guid>
      <comments>https://ssdragon.tistory.com/189#entry189comment</comments>
      <pubDate>Mon, 9 Sep 2024 23:50:34 +0900</pubDate>
    </item>
    <item>
      <title>AWS Serverless(서버리스)로 서버 고민 없이 웹 애플리케이션 구축하기 #1</title>
      <link>https://ssdragon.tistory.com/188</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버리스로 서버 고민없이 웹 애플리케이션 구축하기 #1&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/189&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;서버리스로 서버 고민없이 웹 애플리케이션 구축하기 #2&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;AWS 대표 서비스인 Lambda(람다)를 사용하여 서버 고민 없이 간단한 웹 애플리케이션을 구축하는 방법을 다뤄본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 만들 웹 서비스는 &quot;친구들의 기분 상태를 랜덤으로 매칭하는 서비스&quot;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DtKCs/btsJsxAgrlg/YZikZlxKpJtCEJl2sMPrPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DtKCs/btsJsxAgrlg/YZikZlxKpJtCEJl2sMPrPk/img.png&quot; data-alt=&quot;https://static.us-east-1.prod.workshops.aws/public/a09c2a3d-2abf-401d-accc-2e749ca0c294/static/images/110-others/architecture-1.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DtKCs/btsJsxAgrlg/YZikZlxKpJtCEJl2sMPrPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDtKCs%2FbtsJsxAgrlg%2FYZikZlxKpJtCEJl2sMPrPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;323&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://static.us-east-1.prod.workshops.aws/public/a09c2a3d-2abf-401d-accc-2e749ca0c294/static/images/110-others/architecture-1.png&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. Lambda와 DynamoDB를 활용한 데이터 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스 로직을 AWS Lambda 함수에 작성하고 이 Lambda가 실행될 때마다 친구들의 기분 상태 데이터를 랜덤하게 생성하여 DynamoDB에 저장한다. Lambda는 서버를 직접 관리할 필요 없이 이벤트가 발생할 때 자동 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. API Gateway를 통한 인터넷 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lambda 함수를 API Gateway와 연결하여 인터넷에서 접근할 수 있는 URL을 생성한다. 이 URL을 통해 사용자는 Lambda 함수를 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. S3를 이용한 정적 웹사이트 호스팅&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드는 간단한 HTML로 구현한다. HTML에 버튼을 추가해 사용자가 버튼을 누르면 Lambda가 실행되도록 한다. 이 HTML 파일은 S3 버킷에 업로드하여 S3 정적 웹사이트 호스팅 기능을 사용하여 전 세계에서 접근가능하게 한다. 즉, 웹 서버를 따로 두지 않고 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 그럼 해당 서버리스 웹 애플리케이션을 구축하기 전에 해당 서비스들의 개념을 살펴보고 해당 웹 애플리케이션 구축은 2번째 게시글에서 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 서버리스란?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UQSl3/btsJshqSQQH/r9PXkpswS9K6FJz22mL2uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UQSl3/btsJshqSQQH/r9PXkpswS9K6FJz22mL2uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UQSl3/btsJshqSQQH/r9PXkpswS9K6FJz22mL2uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUQSl3%2FbtsJshqSQQH%2Fr9PXkpswS9K6FJz22mL2uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;342&quot; height=&quot;371&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 없다는 의미가 아닌 서버 관리가 불필요하다는 의미&lt;/li&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 패러다임의 전환 (paradigm shift)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGbQOo/btsJsfGCuOw/DDD02M0cj0VL5UtDa8v60K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGbQOo/btsJsfGCuOw/DDD02M0cj0VL5UtDa8v60K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGbQOo/btsJsfGCuOw/DDD02M0cj0VL5UtDa8v60K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGbQOo%2FbtsJsfGCuOw%2FDDD02M0cj0VL5UtDa8v60K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;237&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기에는 개발자들이 물리적인 서버를 직접 구축하고 관리&lt;/li&gt;
&lt;li&gt;클라우드 기술이 발전하면서 가상 머신을 통해 이 작업을 단순화하였고, 대표적인 가상머신으로 AWS EC2가 있음&lt;/li&gt;
&lt;li&gt;현재는 컨테이너 기술이 발전하면서 인프라를 컨테이너로 구축하여 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사항들은 여전히 개발자가 전부 관리해야 한다는 단점이 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버리스를 사용하게 된다면 AWS가 인프라를 관리해주기 때문에 이런 부담이 줄어들게 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 서버리스의 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 관리 필요 없음&lt;/li&gt;
&lt;li&gt;사용한 만큼만 지불&lt;/li&gt;
&lt;li&gt;요청에 맞게 스케일링&lt;/li&gt;
&lt;li&gt;높은 보안수준
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분 AWS가 책임지므로 덜 신경써도 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 다양한 범주의 서버리스 서비스&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z9kl3/btsJrFMB2zh/oDoPbPJ7SZoFcXkoapHSGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z9kl3/btsJrFMB2zh/oDoPbPJ7SZoFcXkoapHSGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z9kl3/btsJrFMB2zh/oDoPbPJ7SZoFcXkoapHSGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz9kl3%2FbtsJrFMB2zh%2FoDoPbPJ7SZoFcXkoapHSGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;264&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;704&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;Lambda : 이벤트에 의해 구동되는 이벤트 드리븐 아키텍처에서 많이 사용&lt;/li&gt;
&lt;li&gt;Fargate : 컨테이너 노드를 위한 서버리스 서비스&lt;/li&gt;
&lt;li&gt;S3 : 객체 오브젝트 저장소&lt;/li&gt;
&lt;li&gt;Aurora Serverless : 관계형 데이터베이스&lt;/li&gt;
&lt;li&gt;DynamoDB : NoSQL&lt;/li&gt;
&lt;li&gt;EventBridge : 이벤트 리스너 또는 라우터 역할&lt;/li&gt;
&lt;li&gt;API Gateway : api 엔드포인트 역할&lt;/li&gt;
&lt;li&gt;SQS, SNS : 메시징과 관련&lt;/li&gt;
&lt;li&gt;Step Functions : 워크 플로우 제어를 위한 것&lt;/li&gt;
&lt;li&gt;AppSync : GraphQL 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 3-Tier 서버리스 아키텍처 구성&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KSBlE/btsJsRSMiRD/t20tNhZ1ZimO1hQ1MjFfQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KSBlE/btsJsRSMiRD/t20tNhZ1ZimO1hQ1MjFfQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KSBlE/btsJsRSMiRD/t20tNhZ1ZimO1hQ1MjFfQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKSBlE%2FbtsJsRSMiRD%2Ft20tNhZ1ZimO1hQ1MjFfQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;306&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;720&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;S3 : 웹 서버 역할 담당하며 사용자가 가장 먼저 접속하는 웹페이지 제공&lt;/li&gt;
&lt;li&gt;API Gateway : Lambda 실행을 위한 API 정의하고 인터넷 주소(엔드포인트) 생성&lt;/li&gt;
&lt;li&gt;Lambda : API Gateway에서 호출된 후 비즈니스 로직 처리&lt;/li&gt;
&lt;li&gt;DynamoDB : 데이터베이스 역할을 하여 데이터를 저장하고 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. AWS Lambda&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 대표적인 서버리스 서비스&lt;/li&gt;
&lt;li&gt;많은 요청이 발생할 때에도 Lambda는 자동 확장 및 관리됨&lt;/li&gt;
&lt;li&gt;추가하고 싶은 기능이나 간단한 서비스를 만들 때도 쉽게 사용 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사진 이미지 썸네일 버전 만드는 기능&lt;/li&gt;
&lt;li&gt;간단하게 데이터 분석하고 싶을 때&lt;/li&gt;
&lt;li&gt;AWS 의 이벤트 서비스들과 연동해서 이벤트 발생 시, Lambda 기능을 이용해서 이벤트 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6-1. AWS Lambda 특성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 프로비저닝하거나 관리하지 않고도 코드를 실행해주는 컴퓨팅 서비스&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;불필요한 서버 관리&lt;/li&gt;
&lt;li&gt;자동 확장&lt;/li&gt;
&lt;li&gt;고가용성 및 보안&lt;/li&gt;
&lt;li&gt;사용한 만큼만 지불&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6-2. AWS Lambda를 사용한 아키텍처&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwSI2a/btsJrCbicIu/AsFssEKeK5L4RlBtx9vSf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwSI2a/btsJrCbicIu/AsFssEKeK5L4RlBtx9vSf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwSI2a/btsJrCbicIu/AsFssEKeK5L4RlBtx9vSf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwSI2a%2FbtsJrCbicIu%2FAsFssEKeK5L4RlBtx9vSf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;704&quot; height=&quot;338&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이벤트 소스로부터 람다가 호출되면 함수가 구동&lt;/li&gt;
&lt;li&gt;여러 언어로 함수의 코드 작성 가능&lt;/li&gt;
&lt;li&gt;여러 언어로 애플리케이션을 만들어서 코드만 실행하면 됨&lt;/li&gt;
&lt;li&gt;함수는 함수를 호출하거나 DB, 인터넷 등 통신 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6-3. AWS Lambda 사용 사례&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bie119/btsJs54krGB/nxysC0OskeaZ97Xmu0hEk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bie119/btsJs54krGB/nxysC0OskeaZ97Xmu0hEk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bie119/btsJs54krGB/nxysC0OskeaZ97Xmu0hEk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbie119%2FbtsJs54krGB%2FnxysC0OskeaZ97Xmu0hEk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;294&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;788&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;우리가 만드는 대부분의 서비스는 Lambda로 구축 가능&lt;/li&gt;
&lt;li&gt;웹서버, 백엔드 서비스, 데이터 처리, 챗봇, 자동화 등 다양한 사례 존재&lt;/li&gt;
&lt;li&gt;AWS에서는 자동화를 위해 Lambda 많이 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. API Gateway&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS의 API 관리 서비스&lt;/li&gt;
&lt;li&gt;API 를 관리해주고 API 를 통해 외부에서의 호출이 왔을 때 대문 역할을 하는 서비스&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;API 란 외부에서 기업의 서비스를 이용하려고 할 때 규격을 정해주는 것을 의미한다. 일종의 형식을 정하고 이 형식대로 기업 서비스를 호출하면 기업은 서비스를 제공해 주는 약속이라고 생각하면 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7-1. API Gateway 는 API 기반 아키텍처의 관문&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zDuou/btsJs6B8Lty/hnQEsaCYk5XKaUMFfrC8IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zDuou/btsJs6B8Lty/hnQEsaCYk5XKaUMFfrC8IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zDuou/btsJs6B8Lty/hnQEsaCYk5XKaUMFfrC8IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzDuou%2FbtsJs6B8Lty%2FhnQEsaCYk5XKaUMFfrC8IK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;264&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;634&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;어떤 규모에서도 개발자가 API 를 손쉽게 생성, 개시, 유지, 관리, 모니터링 등 할 수 있는 완전관리형 서비스&lt;/li&gt;
&lt;li&gt;애플리케이션이 백엔드 서비스에 데이터, 비즈니스 로직, 기능에 접근할 수 있는 정문 역할&lt;/li&gt;
&lt;li&gt;사용자로부터 요청을 받으면 Lambda 또는 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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7-2. 다양한 API 유형 지원&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGe1nA/btsJtIACrlE/6B8d7pjyVpHjT1weq1YcA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGe1nA/btsJtIACrlE/6B8d7pjyVpHjT1weq1YcA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGe1nA/btsJtIACrlE/6B8d7pjyVpHjT1weq1YcA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGe1nA%2FbtsJtIACrlE%2F6B8d7pjyVpHjT1weq1YcA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;217&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;570&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;일반적으로 RESTful API 생성 가능&lt;/li&gt;
&lt;li&gt;채팅앱, 스트리밍 대시보드와 같은 실시간 양방향 통신은 WebSocket APIs 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Amazon DynamoDB&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대규모 성능에 최적화된 완전 관리형 NoSQL 데이터베이스 서비스&lt;/li&gt;
&lt;li&gt;RDB에서는 데이터를 보관하는 형태인 스키마를 정의하고 이를 이용하여 데이터를 저장하지만, DynamoDB와 같은 NoSQL은 스키마 없이 데이터를 원하는 형태로 자유롭게 저장 가능&lt;/li&gt;
&lt;li&gt;AWS의 완전관리형 서비스이기에 서버 관리를 신경쓰지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8-1. Dynamo 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;서버리스&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유지관리 불필요&lt;/li&gt;
&lt;li&gt;오토 스케일링&lt;/li&gt;
&lt;li&gt;고가용성 및 내결함성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;Known access pattern 이 있는 애플리케이션에서 이상적 성능&lt;/li&gt;
&lt;li&gt;초당 수백만 요청 처리 및 짧은 지연시간&lt;/li&gt;
&lt;li&gt;자동화된 글로벌 복제&lt;/li&gt;
&lt;li&gt;다른 AWS 서비스와 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;보안 및 액세스&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전송 중 및 저장 시 암호화&lt;/li&gt;
&lt;li&gt;API/ORM 을 통한 세부적인 액세스 제어&lt;/li&gt;
&lt;li&gt;IAM을 통한 승인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8-2. 데이터베이스 확장&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qlxM7/btsJtgq1pih/S6RDMZXoKu6tCQHOOjhmlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qlxM7/btsJtgq1pih/S6RDMZXoKu6tCQHOOjhmlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qlxM7/btsJtgq1pih/S6RDMZXoKu6tCQHOOjhmlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqlxM7%2FbtsJtgq1pih%2FS6RDMZXoKu6tCQHOOjhmlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;246&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;510&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;SQL은 하드웨어의 성능을 높이는 방법으로 한계가 있음&lt;/li&gt;
&lt;li&gt;NoSQL은 수평 확장 가능하도록 데이터가 설계되었기에 매우 유연함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8-3. Core Concepts - Tables, Items, Attributes, Indexes&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M49Dh/btsJtI8qCMQ/iCm255rNyK5fTdfaFHw6m0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M49Dh/btsJtI8qCMQ/iCm255rNyK5fTdfaFHw6m0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M49Dh/btsJtI8qCMQ/iCm255rNyK5fTdfaFHw6m0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM49Dh%2FbtsJtI8qCMQ%2FiCm255rNyK5fTdfaFHw6m0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;298&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;650&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;RDB 와 달리 서버 사이즈 등 데이터베이스 설정 필요 없음&lt;/li&gt;
&lt;li&gt;필요한 키 이외의 스키마를 따로 지정 X, 데이터를 넣으면 넣는대로 들어가기에 따로 저장할 필요 X, 정규화 과정 필요 X&lt;/li&gt;
&lt;li&gt;key-value 형태의 스키마 지원&lt;/li&gt;
&lt;li&gt;RDB와 동일하게 테이블 생성 가능&lt;/li&gt;
&lt;li&gt;partition key라는 것을 만들어야 하며, 이는 유니크한 값이고 정렬이 필요하면 sort key 사용하며, 이 전부가 primary key에 해당함&lt;/li&gt;
&lt;li&gt;column 은 attribute&lt;/li&gt;
&lt;li&gt;row는 item&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. Amazon S3&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Amazon Simple Storage Service&lt;/li&gt;
&lt;li&gt;어디서나 원하는 양의 데이터를 저장하고 검색할 수 있도록 구축된 객체 스토리지&lt;/li&gt;
&lt;li&gt;높은 확장성, 데이터 가용성, 보안, 대용량 데이터 처리 성능 갖춤&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9-1. 정적 웹 사이트 호스팅&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nOuFL/btsJtsx4jNz/JXpkxCDlvev9jbIxz6dhQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nOuFL/btsJtsx4jNz/JXpkxCDlvev9jbIxz6dhQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nOuFL/btsJtsx4jNz/JXpkxCDlvev9jbIxz6dhQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnOuFL%2FbtsJtsx4jNz%2FJXpkxCDlvev9jbIxz6dhQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;151&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;302&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;S3가 마치 웹서버처럼 동작&lt;/li&gt;
&lt;li&gt;S3에 권한을 주어 미리 업로드한 파일(HTML, CSS, JS 등)을 읽을 수 있게 함&lt;/li&gt;
&lt;li&gt;정적 웹 사이트 호스팅
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 측 스크립팅(서버 사이드 렌더링) 지원 X&lt;/li&gt;
&lt;li&gt;클라이언트 렌더링만 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;버킷 구성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 사이트 호스팅&lt;/li&gt;
&lt;li&gt;퍼블릭 읽기 액세스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리전별 웹 사이트 엔드포인트
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 사이트 엔드포인트&lt;/li&gt;
&lt;li&gt;https://[bucketname].s3-website-[Region].amazonaws.com&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;9-1-1. 차단되는 퍼블릭 액세스의 유형&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KV8Lb/btsJttwTknx/IjitDJsI65DktGZqSKAvc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KV8Lb/btsJttwTknx/IjitDJsI65DktGZqSKAvc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KV8Lb/btsJttwTknx/IjitDJsI65DktGZqSKAvc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKV8Lb%2FbtsJttwTknx%2FIjitDJsI65DktGZqSKAvc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;622&quot; height=&quot;282&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;698&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;S3에 데이터를 올리면 오브젝트 형태로 저장되며, 이 공간을 버킷이라고 함&lt;/li&gt;
&lt;li&gt;버킷은 기본적으로 모든 접근 차단되며, 특히 퍼블릭 접근 차단&lt;/li&gt;
&lt;li&gt;퍼블릭으로 열면 클라우드 특성상 누구든지 접근 가능하기에 위험하며, S3에 데이터 전송 비용도 추가됨&lt;/li&gt;
&lt;li&gt;접근을 꼭 해야하는 경우에만 제한적으로 풀기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9-2. S3 버킷 정책&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;534&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFGn1y/btsJtNhB1tK/FseM74UjHF2ZwhFN8Zsc11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFGn1y/btsJtNhB1tK/FseM74UjHF2ZwhFN8Zsc11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFGn1y/btsJtNhB1tK/FseM74UjHF2ZwhFN8Zsc11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFGn1y%2FbtsJtNhB1tK%2FFseM74UjHF2ZwhFN8Zsc11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;641&quot; height=&quot;363&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;534&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;S3 버킷 정책은 익명 사용자에게 읽기 전용 권한 부여&lt;/li&gt;
&lt;li&gt;Effect : 허용 및 불가 설정&lt;/li&gt;
&lt;li&gt;Action : 권한&lt;/li&gt;
&lt;li&gt;Resource : 자원&lt;/li&gt;
&lt;li&gt;Principal : 누가 이 적용을 받는지 의미&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AWS</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/188</guid>
      <comments>https://ssdragon.tistory.com/188#entry188comment</comments>
      <pubDate>Fri, 6 Sep 2024 00:15:27 +0900</pubDate>
    </item>
    <item>
      <title>AWS TechCamp란?</title>
      <link>https://ssdragon.tistory.com/187</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qI4rV/btsJrHhXzyL/knh2Kj958FRNT8Ptg1SGy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qI4rV/btsJrHhXzyL/knh2Kj958FRNT8Ptg1SGy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qI4rV/btsJrHhXzyL/knh2Kj958FRNT8Ptg1SGy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqI4rV%2FbtsJrHhXzyL%2Fknh2Kj958FRNT8Ptg1SGy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;142&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 AWS Builders Korea Program이 새롭게 변화한 AWS TechCamp에 참여하면서 느낀 점을 공유하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 프로그램에서는 이론과 간단한 실습을 중심으로 진행되었지만, 이번 테크캠프는 한층 더 강화된 학습 경험을 제공했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간으로 과외를 받는 듯한 몰입감이 느껴졌고, AWS 서비스를 깊이 이해하고 실전에서 바로 적용해보고 싶다는 열정이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UVzqy/btsJqnE3tM4/isGRiqHbmiPYXVDH8ZJ830/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UVzqy/btsJqnE3tM4/isGRiqHbmiPYXVDH8ZJ830/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UVzqy/btsJqnE3tM4/isGRiqHbmiPYXVDH8ZJ830/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUVzqy%2FbtsJqnE3tM4%2FisGRiqHbmiPYXVDH8ZJ830%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1164&quot; height=&quot;427&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TechCamp는 분기별로 3일간의 세션으로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매년 3월(Modern App), 6월(AI/ML), 9월(Data), 11월(Every App)을 주제로 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 두 번, 오전 9~12시, 오후 2~5시까지 강의가 열린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강의는 기초 과정 (레벨 100), 기본 과정 (레벨 100 - 200) , 심화 과정 (레벨 200 - 300)으로 구성되어 있으며, 이론 강의와 실시간 실습을 진행한다. 궁금한 점이 생기면 실시간으로 질문할 수 있어 이론만 듣고 끝내는 것이 아니라 실제 적용에 큰 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;직접 체험해보니 어떤 느낌인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인별 전용 웨비나 URL로 접속해 온라인 강의를 들었다.&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;실제로 AWS 서비스를 구축하면서 어떻게 활용해야 할지에 대한 감을 잡을 수 있었고, 만족도는 20000% 이다.&lt;/p&gt;
&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;강의자료와 다시보기는 1개월간 제공되며, 유튜브에 올라올 가능성도 있어 복습하기 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습 비용도 대부분 프리티어로 진행되며, 필요한 경우 추후 50달러 크레딧을 받을수 있어 비용 부담이 크지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단점은 없나요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 단점은 평일에 진행했다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직장인, 학생 등 시간적 여유가 부족한 분들에게는 오전 9시~12시, 오후 2시~5시 일정이 다소 부담스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시보기가 제공되지만 이 경우 50달러 크레딧을 못받기에 개인돈을 쪼끔 써야한다.&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;결론&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxbqHv/btsJqHJWhjv/dcJpkbaMwftKcH8OA44KO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxbqHv/btsJqHJWhjv/dcJpkbaMwftKcH8OA44KO0/img.png&quot; data-alt=&quot;이번에 들었던 세션들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxbqHv/btsJqHJWhjv/dcJpkbaMwftKcH8OA44KO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxbqHv%2FbtsJqHJWhjv%2FdcJpkbaMwftKcH8OA44KO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;382&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이번에 들었던 세션들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 AWS TechCamp는 이론과 실습을 통해 AWS 서비스를 직접 적용해보는 경험을 선사했다. 특히 새로운 AWS 서비스(AWS Clean Rooms, AWS DataZone ...)를 접하고 활용할 수 있었던 점이 가장 좋았다. 평소에는 접해보지 못한 서비스들을 깊이 있게 다뤄보며, AWS에 대한 이해를 넓힐 수 있는 기회였다. 새로운 관점과 새로운 서비스를 계속해서 경험하고 이해하고 싶은 분들에게 적극 추천한다.&lt;/p&gt;</description>
      <category>AWS</category>
      <category>AWS</category>
      <category>aws builders korea program</category>
      <category>aws techcamp</category>
      <category>aws 테크캠프</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/187</guid>
      <comments>https://ssdragon.tistory.com/187#entry187comment</comments>
      <pubDate>Wed, 4 Sep 2024 21:47:14 +0900</pubDate>
    </item>
    <item>
      <title>빅데이터의 파일 형식은 무엇으로 해야할까? (feat. Parquet 란?)</title>
      <link>https://ssdragon.tistory.com/186</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 엔지니어 프로젝트를 진행하면서 컬럼이 수십개에 레코드는 수십만개의 데이터를 다루면서 데이터 유형에 대해 고민하게 되었다. 단순히 보기 편하고 다루기 쉬운 행 기반 형식의 CSV 파일만을 생각했는데 읽고 쓰는 I/O 성능이 느려서 새로운 파일 유형을 찾게 되었다. 그것이 parquet 같은 빅데이터 파일 형식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 고민해야할 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 기하급수적으로 증가함에 따라 효율적인 데이터 저장방법은 무엇일까?&lt;/li&gt;
&lt;li&gt;데이터를 행과 열로 구성하는 것의 차이점과 이것이 저장 및 쿼리 효율성에 어떤 영향을 미칠까?&lt;/li&gt;
&lt;li&gt;행 기반 형식으로 데이터를 저장하는 것의 장단점과 쓰기 작업이 많은 작업에 대한 적합성은 무엇일까?&lt;/li&gt;
&lt;li&gt;빠른 쿼리 속도와 효율적인 압축 옵션 등 읽기 작업이 많은 작업을 위한 열(컬럼) 기반 저장소의 이점은?&lt;/li&gt;
&lt;li&gt;Parquet와 ORC 파일 형식의 차이점과 각 형식의 최적 사용 사례가 무엇일까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Parquet 파일 형식이란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 분석 쿼리를 위한 무료 오픈소스 스토리지 형식&lt;/li&gt;
&lt;li&gt;효율적인 데이터 압축 및 인코딩 체계를 제공&lt;/li&gt;
&lt;li&gt;복잡한 쿼리 실행하고 대용량 데이터를 처리하는데 이상적인 파일 형식&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/ko/about-aws/whats-new/2019/12/announcing-amazon-redshift-data-lake-export/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Announcing Amazon Redshift data lake export&lt;/a&gt; 에서 &quot;Parquet 형식은 텍스트 형식에 비해 UNLOAD 속도가 최대 2배 빠르고, AWS S3 에서 스토리지 사용량이 최대 6배 적습니다&quot; 라고 설명함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;또한, 하나 이상의 파티션 열을 지정하여 언로드된 데이터를 S3 버킷 폴더로 자동 분할 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. 연도, 월, 일 별로 분할하도록 선택 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;파티션 잘라내기 기능과 관련없는 파티션 검색 건너뛰기 기능 활용하여 쿼리 성능 개선 및 비용 최소화 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터를 Parquet 또는 ORC 같은 열 형식으로 변환하는 것도 &lt;a href=&quot;https://docs.aws.amazon.com/athena/latest/ug/columnar-storage.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS Athena 성능 개선하는 수단으로 권장&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요한 블록만 fetch 하여 쿼리 성능 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Apache Parquet 는 Data Lake 사용할 때 시스템 성능에 중요한 역할을 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. &lt;a href=&quot;https://docs.aws.amazon.com/athena/latest/ug/columnar-storage.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Parquet vs. ORC (Optimized Row Columnar)&lt;/a&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parquet 와 ORC 는 빅데이터 애플리케이션에 적합한 선택이기에 요구사항을 고려하여 적합한 형식을 선택하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;쿼리 성능 (Queery Performance)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parquet가 더 광범위하고 복잡한 쿼리 유형 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;복합 데이터 유형 (Complex Data Types)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ORC 가 더 광범위한 복합 데이터 유형 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;쓰기 효율성 (Write Efficiency)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ORC 는 Hive에서 ACID 트랜잭션 지원하므로 쓰기 작업이 많을 경우 선택&lt;/li&gt;
&lt;li&gt;Parquet는 일반적으로 분석 작업 부하와 크고 복잡한 데이터 구조 작업할 때 좋음&lt;/li&gt;
&lt;li&gt;ORC 는 쓰기 작업과 데이터 수정이 필요할 때 더 이상적이며, Parquet에 비해 더 나은 쓰기 속도 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;읽기 효율성 (Read Efficiency)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parquet 는 한 번 쓰고 여러 번 읽는 분석 시나리오에서 탁월하며, 매우 효율적인 데이터 압축 및 압축 해제 제공&lt;/li&gt;
&lt;li&gt;Parquet 는 데이터 건너뛰기를 지원하며, 쿼리 전체 데이터 행을 건너뛰면서 특정 열 값 반환이 가능하므로 I/O 최소화&lt;/li&gt;
&lt;li&gt;데이터 세트에 많은 수의 컬럼이 있고, 특정 데이터 하위 집합에만 접근해야 하는 시나리오에서 ORC 가 유용할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;호환성 (Compatibility)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ORC 는 Hive 생태계와 호환되며 Apache Hive 와 함께 작업 시, ACID 트랜잭션 지원&lt;/li&gt;
&lt;li&gt;Parquet는 Java, Python, C++ 같은 여러 프로그래밍 언어 지원하며 거의 모든 빅데이터 설정에서 사용할 수 있도록 광범위한 접근성 제공하며, AWS Athena, AWS Redshift Spectrum, Qubole, Google BigQuery 등 여러 쿼리 엔진에서 사용됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;파일 크기 (File Size)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디스크 공간이 문제인 경우, ORC 는 일반적으로 더 작은 파일을 생성하여 저장 비용 줄임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;압축 (Compression)&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;그러나 압축이 주요 기준이면 Parquet 를 선택함. 이는 매우 효율적인 압축 및 인코딩 체계로 더 작은 파일 크기를 제공하기 때문이며 컬럼별로 특정 압축 체계를 지원하여 저장된 데이터를 더욱 최적화 가능하기 때문임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;진화 (Evolution)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;둘 다 스키마 진화(Schema Evolution) 지원&lt;/li&gt;
&lt;li&gt;즉, 시간이 지남에 따라 열(column) 추가, 제거 및 수정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parquet와 다른 파일 형식 비교를 보려면 &lt;a href=&quot;https://www.upsolver.com/blog/the-file-format-fundamentals-of-big-data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Parquet, ORC, and Avro: The File Format Fundamentals of Bic Data&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;4. Parquet 특징&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsvg8h/btsJkT36Krx/KoN8md6nskiGZZJ6lxPHZ1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsvg8h/btsJkT36Krx/KoN8md6nskiGZZJ6lxPHZ1/img.webp&quot; data-alt=&quot;https://www.upsolver.com/wp-content/uploads/2020/05/Screen-Shot-2020-05-26-at-17.52.58.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsvg8h/btsJkT36Krx/KoN8md6nskiGZZJ6lxPHZ1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdsvg8h%2FbtsJkT36Krx%2FKoN8md6nskiGZZJ6lxPHZ1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;270&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.upsolver.com/wp-content/uploads/2020/05/Screen-Shot-2020-05-26-at-17.52.58.png&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;열 기반 (Columnar)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CSV 또는 Avro 같은 행 기반 형식과 달리 열 기반&lt;/li&gt;
&lt;li&gt;즉, 각 레코드 값이 아닌 각 테이블 열의 값이 나란히 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;오픈소스 (Open-source)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parquet 는 Apache Hadoop 라이센스에 따라 무료 사용 가능한 오픈소스&lt;/li&gt;
&lt;li&gt;대부분의 Hadoop 데이터 처리 프레임워크와 호환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자체 설명 (Self-describing)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parquet 파일에는 데이터 외에도 스키마와 구조를 포함한 메타 데이터 포함&lt;/li&gt;
&lt;li&gt;각 파일은 데이터와 각 레코드에 액세스하는 데 사용되는 표준을 모두 저장하므로 Parquet 파일을 쓰고, 저장하고, 읽는 서비스를 쉽게 분리 가능&lt;/li&gt;
&lt;li&gt;각 파일은 각 레코드에 액세스하는 데 사용되는 데이터와 표준을 모두 저장하므로 Parquet 파일을 쓰고, 저장하고, 읽는 서비스를 분리하기가 더 쉬움&lt;/li&gt;
&lt;li&gt;데이터 타입도 메타데이터에 저장되기에 CSV 와 달리 따로 데이터 유형을 명시하지 않아도 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Parquet 장점&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-1. 압축 (Compression)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parquet 에서 압축은 열 단위로 수행되며 데이터 유형별로 유연한 압축 옵션과 확장 가능한 인코딩 스키마를 지원하도록 구축됨&lt;/li&gt;
&lt;li&gt;e.g. 정수 및 문자열 데이터를 압축하는 데 서로 다른 인코딩 사용&lt;/li&gt;
&lt;li&gt;사전 인코딩 (Dictionary Encoding)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고유 값이 적은 데이터에 대해 자동으로 동적 활성화&lt;/li&gt;
&lt;li&gt;반복되는 값을 고유한 코드로 매핑하여 저장하는 방식이며, 반복되는 값이 많은 열에서 효과적&lt;/li&gt;
&lt;li&gt;고유 값이 많을 경우에는 사전 크기가 커지면서 오히려 메모리 사용량이 늘어날 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;비트 패킹 (Bit Packing)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정수 저장은 일반적으로 정수당 전용 32bit 또는 64bit로 이루어짐&lt;/li&gt;
&lt;li&gt;이를 통해 작은 정수를 보다 효율적으로 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실행 길이 인코딩 (RLE, Run Length Encoding)
&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;Parquet 는 비트 패킹과 RLE 의 결합 버전을 구현하며, 어떤 인코딩이 최상의 압축 결과를 생성하는지에 따라 인코딩이 전환됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-2. 성능 (Performance)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CSV 와 같은 행 기반 파일과 달리 Parquet 는 성능을 위해 최적화 됨&lt;/li&gt;
&lt;li&gt;파일 시스템에서 쿼리 실행 시, 매우 빠르게 관련 데이터에만 집중 가능&lt;/li&gt;
&lt;li&gt;스캔되는 데이터 양이 훨씬 적어 I/O 사용량 최소화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-3. 스키마 진화 (Schema evolution)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parquet 와 같은 열 형식의 파일 사용하는 경우, 사용자는 간단한 스키마로 시작하여 필요에 따라 점차적으로 스키마에 열(컬럼) 추가 가능&lt;/li&gt;
&lt;li&gt;이러한 방식으로 사용자는 서로 다르지만 상호 호환되는 스키마를 가진 여러 개의 Parquet 파일을 가질 수 있음&lt;/li&gt;
&lt;li&gt;이러한 경우 Parquet 는 이러한 파일 간의 자동 스키마 병합을 지원함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-4. 오픈소스 및 비독점적 (Open source and non-proprietary)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apache Parquet 는 오픈소스 Apache Hadoop 생태계 일부&lt;/li&gt;
&lt;li&gt;활발하게 개발 진행 중이며 사용자 및 개발자 커뮤니티에 의해 지속적으로 개선 및 유지관리&lt;/li&gt;
&lt;li&gt;데이터를 오픈 포맷으로 저장하면 공급업체에 묶이지 않고 유연성 높일 수 있음&lt;/li&gt;
&lt;li&gt;많은 최신 고성능 DB에서 사용하는 독점 파일 포맷과 비교 가능&lt;/li&gt;
&lt;li&gt;즉, 특정 DB 공급업체에 묶이지 않고도 동일한 데이터 레이크 아키텍처 내에서 AWS Athena, AWS Redshift, Qubole 과 같은 다양한 쿼리 엔진 사용 가능
&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://www.upsolver.com/blog/four-principles-data-lake-architecture&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;4 Guiding Principles for Modern Data Lake Architecture&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Parquet 구조&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자체 설명 형식으로 각 파일에는 데이터와 메타데이터 모두 포함&lt;/li&gt;
&lt;li&gt;파일은 행 그룹(row groups), 헤더(header) 및 푸터(footer)로 구성&lt;/li&gt;
&lt;li&gt;각 행 그룹에는 동일한 열 데이터 포함&lt;/li&gt;
&lt;li&gt;동일한 열은 각 행 그룹에 함께 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eBjWTa/btsJkxfW1vz/czBxxmrGOm9LmtVPekIc7k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eBjWTa/btsJkxfW1vz/czBxxmrGOm9LmtVPekIc7k/img.webp&quot; data-alt=&quot;https://www.upsolver.com/wp-content/uploads/2020/05/Screen-Shot-2020-05-26-at-17.53.13.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eBjWTa/btsJkxfW1vz/czBxxmrGOm9LmtVPekIc7k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeBjWTa%2FbtsJkxfW1vz%2FczBxxmrGOm9LmtVPekIc7k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;679&quot; height=&quot;353&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.upsolver.com/wp-content/uploads/2020/05/Screen-Shot-2020-05-26-at-17.53.13.png&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 구조는 빠른 쿼리 성능과 낮은 I/O (스캔되는 데이터 양 최소화)를 위해 최적화 되어 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. 열이 1000개인 테이블이 있는데 일반적으로 열의 작은 하위 집합만 사용하여 쿼리한다고 가정한다면
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parquet 파일을 사용하면 필요한 열과 해당 값만 가져와서 메모리에 로드하고 쿼리 응답 가능&lt;/li&gt;
&lt;li&gt;CSV 와 같은 행 기반 파일의 경우에는 전체 테이블을 메모리에 로드해야 하므로 I/O 증가 및 성능 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 분석 쿼리를 위한 열 기반 스토리지 vs. 행 기반 스토리지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Column-Oriented vs. Row-Based Storage for Analytic Querying)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터는 종종 행으로 생성되고 쉽게 개념화 된다. 우리는 엑셀(Excel) 스프레드 시트 관점에서 생각하는 것이 익숙하며, 특정 레코드와 관련된 모든 데이터를 깔끔하고 체계적으로 정리된 한 행에서 볼 수 있다. 그러나 &lt;u&gt;&lt;b&gt;대규모 분석 쿼리의 경우 열 기반 스토리지는 비용과 성능 측면에서 상당한 이점이 있다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.upsolver.com/blog/log-analytics-architecture-data-lake&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Logs and Event Streams&lt;/a&gt; 와 같은 복잡한 데이터는 수백 또는 수천 개의 컬럼과 수백만 개의 행이 있는 표로 표현된다. 이 표를 CSV 같은 행 기반 형식으로 저장하면 아래와 같다.&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;CSV 는 Parquet 만큼 효율적으로 압축되지 않기에 스토리지 비용이 더 많이 듦&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열 형식은 더 나은 압축률과 향상된 성능을 기본적으로 제공하며 데이터를 열 단위로 수직으로 쿼리 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Apache Parquet 는 언제 사용해야 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&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;Parquet 는 성능과 효과적인 압축을 위해 만들어짐&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.matthewrathbone.com/2019/12/20/parquet-or-bust.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여러 벤치마킹 테스트&lt;/a&gt;에서 Parquet의 SQL 쿼리 실행 시간과 Avro 또는 CSV 같은 형식과 비교한 결과 Parquet 쿼리가 훨씬 더 빠르게 수행되는 것을 알 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;기록하는 비즈니스 데이터가 점점 더 복잡해지면서 각 데이터 이벤트에 대해 20개의 필드를 수집하는 대신 100개 이상의 필드를 수집하고 있을 수 있음&lt;/li&gt;
&lt;li&gt;이러한 데이터는 데이터 레이크에 저장하기는 쉽지만, 행 기반 형식으로 저장된 경우 쿼리하려면 상당한 양의 데이터를 스캔해야 함.&lt;/li&gt;
&lt;li&gt;Parquet 의 열 형식과 자체 설명 기능을 사용하면 특정 쿼리에 응답하는 데 필요한 필수 컬럼만 가져올 수 있으므로 처리되는 데이터의 양을 줄일 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;Oracle 또는 Snowflake 와 같은 데이터베이스 공급업체는 자사 도구만 읽을 수 있는 독점 형식으로 데이터를 저장하는 것을 선호하지만, 최신 데이터 아키텍처는 스토리지와 컴퓨팅을 분리하는 쪽으로 편향되어 있음&lt;/li&gt;
&lt;li&gt;여러 분석 서비스를 사용하여 다양한 사용 사례에 대한 답을 찾으려면 데이터를 Parquet에 저장해야 함 (&lt;a href=&quot;https://www.upsolver.com/blog/data-pipeline-architecture-building-blocks-diagrams-and-patterns&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Read more about data pipeline architecture&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;9. AWS 와의 호환성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redshift 쿼리 결과를 분석을 위한 효율적인 개방형 열 기반 스토리지인 Apache Parquet로 AWS S3 데이터 레이크에 UNLOAD(언로드) 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redshift 쿼리 결과를 parquet 파일로 S3에 저장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하나 이상의 파티션 열을 지정하여 언로드 된 데이터를 S3 폴더로 자동 분할 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연도, 월, 일별로 분할하도록 선택 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;컬럼형 parquet 스토리지는 분석 쿼리에 매우 효율적이며, AWS Athena 또는 AWS Redshift Spectrum과 같은 다양한 서비스에서 액세스 가능&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;10. AWS Redshift에서 대용량 데이터를 AWS S3에 CSV와 Parquet 파일로 저장하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redshift 쿼리편집기에 들어가서 진행함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;naver_real_estate 테이블에는 약 42만건의 데이터와 약 50개의 컬럼이 있음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1003&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8VQ7b/btsJjjwftgt/glJcTVXCKSwmaLLdLckm20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8VQ7b/btsJjjwftgt/glJcTVXCKSwmaLLdLckm20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8VQ7b/btsJjjwftgt/glJcTVXCKSwmaLLdLckm20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8VQ7b%2FbtsJjjwftgt%2FglJcTVXCKSwmaLLdLckm20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;351&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1003&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 전체 SELECT(조회) 하는데 데이터가 너무 커서 UNLOAD 명령어를 사용하라고 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;697&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bU7anO/btsJjvQImgF/bmIfcEtzDFQNwDkDHAQnLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bU7anO/btsJjvQImgF/bmIfcEtzDFQNwDkDHAQnLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bU7anO/btsJjvQImgF/bmIfcEtzDFQNwDkDHAQnLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbU7anO%2FbtsJjvQImgF%2FbmIfcEtzDFQNwDkDHAQnLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;282&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;697&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1724942516907&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UNLOAD ('SELECT * FROM raw_data.naver_real_estate')
TO 's3://S3버킷이름'
IAM_ROLE 'IAM ROLE 입력'
CSV;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;42만건을 UNLOAD 명령어로 S3에 CSV 파일로 저장하는데 19.2초 소요됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SFlNK/btsJkTXmxD2/q3r4vKIPROwF4AVMolVfA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SFlNK/btsJkTXmxD2/q3r4vKIPROwF4AVMolVfA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SFlNK/btsJkTXmxD2/q3r4vKIPROwF4AVMolVfA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSFlNK%2FbtsJkTXmxD2%2Fq3r4vKIPROwF4AVMolVfA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;228&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 적절한 크기로 분할되는데 &lt;a href=&quot;https://stackoverflow.com/questions/20323919/how-to-unload-a-table-on-redshift-to-a-single-csv-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;단일 파일로 저장하려면 PARALLEL OFF 명령어&lt;/a&gt;를 붙이면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, &lt;a href=&quot;https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Redshift 명령어로 생성된 출력 파일 크기 제한은 UNLOAD의 경우 6.2GB 정도이다.&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gzip 명령어도 사용하면 압축되기에 용량이 더욱 작아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rRtnr/btsJkzkzwrf/dlFoJDbNncAD23eYkQ5W5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rRtnr/btsJkzkzwrf/dlFoJDbNncAD23eYkQ5W5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rRtnr/btsJkzkzwrf/dlFoJDbNncAD23eYkQ5W5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrRtnr%2FbtsJkzkzwrf%2FdlFoJDbNncAD23eYkQ5W5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;360&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;657&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1724942832710&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UNLOAD ('SELECT * FROM raw_data.naver_real_estate')
TO 's3://S3버킷명'
IAM_ROLE 'IAM ROLE 값'
CSV
PARALLEL OFF;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 분할하지 않으니 28.6초가 소요되며 파일 크기도 합쳐진 452MB가 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1724942894997&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UNLOAD ('SELECT * FROM raw_data.naver_real_estate')
TO 's3://S3버킷명'
IAM_ROLE 'iam role 값'
CSV
PARALLEL OFF
gzip;&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;단일 파일에서 gzip으로 압축하면 22.7초가 소요됐으며 용량은 452MB에서 132MB로 1/3으로 줄었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축파일을 풀면 CSV 파일이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCV2w4/btsJjYkIhVk/n77yRFcOKpbcO8TATrx5S1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCV2w4/btsJjYkIhVk/n77yRFcOKpbcO8TATrx5S1/img.png&quot; data-alt=&quot;UNLOAD 명령어로 Parquet 파일 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCV2w4/btsJjYkIhVk/n77yRFcOKpbcO8TATrx5S1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCV2w4%2FbtsJjYkIhVk%2Fn77yRFcOKpbcO8TATrx5S1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;358&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UNLOAD 명령어로 Parquet 파일 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1724943032701&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UNLOAD ('SELECT * FROM raw_data.naver_real_estate')
TO 's3://S3버킷명'
IAM_ROLE 'iam role 값'
PARQUET
PARALLEL OFF;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ignn9/btsJkSRGZ2l/4BZTcWpdO1Zuu1XCTVRAak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ignn9/btsJkSRGZ2l/4BZTcWpdO1Zuu1XCTVRAak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ignn9/btsJkSRGZ2l/4BZTcWpdO1Zuu1XCTVRAak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIgnn9%2FbtsJkSRGZ2l%2F4BZTcWpdO1Zuu1XCTVRAak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;705&quot; height=&quot;209&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Parquet 파일로 저장해보니 16.7초가 소요되며 CSV 파일을 압축한 것과 비슷한 용량으로 나오는 것을 알 수 있다.&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;11. Airflow DAG를 통해서 원하는 스키마의 모든 테이블을 S3에 저장하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dev 데이터베이스에서 raw_data 스키마와 analytics 스키마 안에 있는 모든 테이블을 Parquet로 저장하는 코드를 작성해본다.&lt;/p&gt;
&lt;pre id=&quot;code_1724943148066&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@task
def unload():
    conn = get_redshift_connection()
    cur = conn.cursor()
    cur.execute(&quot;SHOW TABLES FROM SCHEMA dev.raw_data;&quot;)
    tables = cur.fetchall()
    for table in tables:
        table_name = table[2]
        unload_to_s3_parquet('raw_data', table_name, f's3://{BUCKET_NAME}/unload/raw_data/{table_name}/')
        
    cur.execute(&quot;SHOW TABLES FROM SCHEMA dev.analytics;&quot;)
    tables = cur.fetchall()
    for table in tables:
        table_name = table[2]
        unload_to_s3_parquet('analytics', table_name, f's3://{BUCKET_NAME}/unload/analytics/{table_name}/')&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1724943200434&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def unload_to_s3_parquet(schema: str, table: str, s3_path: str, parallel: bool = False):
    iam_role = Variable.get(&quot;aws_iam_role&quot;)
    conn = None
    cur = None
    try:
        conn = get_redshift_connection()
        cur = conn.cursor()

        parallel_option = 'ON' if parallel else 'OFF'
        cur.execute(f&quot;&quot;&quot;
            UNLOAD ('SELECT * FROM {schema}.{table}')
            TO '{s3_path}'
            IAM_ROLE '{iam_role}'
            PARQUET
            PARALLEL {parallel_option};
        &quot;&quot;&quot;)
        logging.info(f&quot;SUCCESS AWS Redshift unloaded {schema}.{table} to {s3_path} as Parquet&quot;)
    except Exception as e:
        logging.error(f&quot;Error Failed to UNLOAD {schema}.{table} to {s3_path}: {e}&quot;)
        raise
    finally:
        if cur is not None:
            cur.close()
        if conn is not None:
            conn.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. SQL DB 툴 (DataGrip)로 CSV파일 저장하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 관리도구를 통해서도 테이블을 쉽게 다운로드 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이(IntelliJ)의 DB 툴인 DataGrip 에서도 아래와 같이 간단하게 파일로 저장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 Parquet 파일 형식은 없고 CSV로는 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 CSV 파일 저장 할 때 데이터 양이 많으면 제대로 다운로드가 안되며, 대략 400MB부터 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 JVM 메모리를 더 높이라고 나오는데 아무래도 자체 메모리 부족으로 안되는 듯 하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cajXmn/btsJjWApGNp/lujHwkAhp8XJ1FBCMM5OBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cajXmn/btsJjWApGNp/lujHwkAhp8XJ1FBCMM5OBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cajXmn/btsJjWApGNp/lujHwkAhp8XJ1FBCMM5OBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcajXmn%2FbtsJjWApGNp%2FlujHwkAhp8XJ1FBCMM5OBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;943&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2701&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9QozM/btsJlp9a4PE/bsSc4RDUVO5miwbz1xIvZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9QozM/btsJlp9a4PE/bsSc4RDUVO5miwbz1xIvZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9QozM/btsJlp9a4PE/bsSc4RDUVO5miwbz1xIvZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9QozM%2FbtsJlp9a4PE%2FbsSc4RDUVO5miwbz1xIvZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;466&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1469&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://www.upsolver.com/blog/apache-parquet-why-use&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.upsolver.com/blog/apache-parquet-why-use&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <category>apache parquet</category>
      <category>AWS</category>
      <category>columnar storage</category>
      <category>parquet</category>
      <category>parquet란?</category>
      <category>Redshift</category>
      <category>unload</category>
      <category>대용량 데이터</category>
      <category>빅데이터</category>
      <category>열 기반 형식 파일</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/186</guid>
      <comments>https://ssdragon.tistory.com/186#entry186comment</comments>
      <pubDate>Thu, 29 Aug 2024 23:58:02 +0900</pubDate>
    </item>
    <item>
      <title>Airflow 란 무엇인가?</title>
      <link>https://ssdragon.tistory.com/185</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Airflow 소개&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이썬으로 작성된 데이터 파이프라인(ETL) 프레임워크
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Airbnb에서 시작한 아파치 오픈소스 프로젝트&lt;/li&gt;
&lt;li&gt;가장 많이 사용되는 데이터 파이프라인 관리 및 작성 프레임워크&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 파이프라인 스케줄링 지원
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정해진 시간에 ETL 실행 or 한 ETL의 실행이 끝나면 다음 ETL 실행&lt;/li&gt;
&lt;li&gt;웹 UI 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 파이프라인(ETL)을 쉽게 만들 수 있게 해줌
&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;a href=&quot;https://airflow.apache.org/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://airflow.apache.org/docs/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 파이프라인을 DAG(Directed Acyclic Graph)라고 부름
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1개의 DAG는 1개 이상의 태스크(task)로 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Airflow 구성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 서버(Web Server)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 UI는 스케줄러와 DAG의 실행 상황을 시각화해줌&lt;/li&gt;
&lt;li&gt;Python Flask로 구현되어 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스케줄러 (Scheduler)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DAG들을 워커들에게 배정하는 역할 수행&lt;/li&gt;
&lt;li&gt;스케줄러와 각 DAG의 실행 결과는 별도 DB에 저장됨&lt;/li&gt;
&lt;li&gt;정해진 시간에 실행되게 할 수 있으며, 순차적 태스크 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;워커 (Worker)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DAG를 실행하는 역할 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메타 데이터 데이터베이스 (Metadata Database)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SQLite 가 기본 설치&lt;/li&gt;
&lt;li&gt;실제 프로덕션에서는 MySQL 이나 Postgres 를 사용해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;큐 (Queue)
&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;이 경우 Executor가 달라짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Airflow 구조 : 서버 한대&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKvF0V/btsHDN4GriD/Tho5nHHWaYYXhCdxT3XKV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKvF0V/btsHDN4GriD/Tho5nHHWaYYXhCdxT3XKV1/img.png&quot; data-alt=&quot;Airflow 서버 한대 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKvF0V/btsHDN4GriD/Tho5nHHWaYYXhCdxT3XKV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKvF0V%2FbtsHDN4GriD%2FTho5nHHWaYYXhCdxT3XKV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;304&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Airflow 서버 한대 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Airflow 구조 : 다수 서버&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7MZZi/btsHBt1F7eQ/ff1CfF0DTEYELzmkOj0lb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7MZZi/btsHBt1F7eQ/ff1CfF0DTEYELzmkOj0lb1/img.png&quot; data-alt=&quot;Airflow 서버 다수 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7MZZi/btsHBt1F7eQ/ff1CfF0DTEYELzmkOj0lb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7MZZi%2FbtsHBt1F7eQ%2Fff1CfF0DTEYELzmkOj0lb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;280&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Airflow 서버 다수 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스케일링 할 때는 워커를 별도의 서버에서 돌리고, 워커가 있는 서버의 수를 늘리는 형태로 용량 증대&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Airflow 구조&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJQGKj/btsHDeaAZTz/ReYJyYErnz2zaqw9mvbGLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJQGKj/btsHDeaAZTz/ReYJyYErnz2zaqw9mvbGLK/img.png&quot; data-alt=&quot;Airflow 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJQGKj/btsHDeaAZTz/ReYJyYErnz2zaqw9mvbGLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJQGKj%2FbtsHDeaAZTz%2FReYJyYErnz2zaqw9mvbGLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;308&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Airflow 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자는 UI 를 통해서 웹 서버와 통신&lt;/li&gt;
&lt;li&gt;Airflow 서버 안에는 DAG Directory 가 있고, 여기에 파이썬으로 작성된 데이터 파이프라인 코드가 있음&lt;/li&gt;
&lt;li&gt;위 다렉토리를 Airflow가 주기적으로 파싱해서 Metadata DB에 기록&lt;/li&gt;
&lt;li&gt;스케줄러가 Executor 를 통해서 워커들에게 일을 주는데, Executor 특성에 따라서 Queue 가 있기도 하고 없기도 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Airflow 개발 장단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 파이프라인 세밀하게 제어 가능&lt;/li&gt;
&lt;li&gt;다양한 데이터 소스와 데이터 웨어하우스 지원&lt;/li&gt;
&lt;li&gt;백필(Backfill) 쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배우기 쉽지 않음&lt;/li&gt;
&lt;li&gt;상대적으로 개발환경 구성하기 쉽지 않음&lt;/li&gt;
&lt;li&gt;직접 운영이 쉽지 않으며, 클라우드 버전 사용이 선호됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GCP provides &quot;Cloud Composer&quot;&lt;/li&gt;
&lt;li&gt;AWS provides &quot;Managed Workflows for Apache Airflow&quot;&lt;/li&gt;
&lt;li&gt;Azure provides &quot;Data Factory Managed Airflow&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.&amp;nbsp; DAG 란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Directed Acyclic Graph&lt;/li&gt;
&lt;li&gt;Airflow 에서 ETL 을 부르는 명칭&lt;/li&gt;
&lt;li&gt;DAG는 태스크로 구성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. 3개의 태스크로 구성된다면 Extract, Transform, Load 로 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 태스크(task)란?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2OvjE/btsHDOCw7V2/l0v65PlgH5K2pOf3k6gSP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2OvjE/btsHDOCw7V2/l0v65PlgH5K2pOf3k6gSP0/img.png&quot; data-alt=&quot;https://magpienote.tistory.com/193&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2OvjE/btsHDOCw7V2/l0v65PlgH5K2pOf3k6gSP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2OvjE%2FbtsHDOCw7V2%2Fl0v65PlgH5K2pOf3k6gSP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;415&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;798&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://magpienote.tistory.com/193&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Airflow의 기본 실행 단위&lt;/li&gt;
&lt;li&gt;Airflow 의 오퍼레이터(Operator)로 만들어짐&lt;/li&gt;
&lt;li&gt;Airflow 에서 이미 다양한 종류의 오퍼레이터 제공&lt;/li&gt;
&lt;li&gt;경우에 맞게 사용하며, 오퍼레이터를 결정하거나 필요하다면 직접 개발&lt;/li&gt;
&lt;li&gt;e.g. Redshift writing, Postgres query, S3 Read/Write, Hive query, Spark job, shell script&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. DAG 구성 예제&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6cq18/btsHDxA0sww/jgIYhk6IKPpl87gcRRtpyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6cq18/btsHDxA0sww/jgIYhk6IKPpl87gcRRtpyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6cq18/btsHDxA0sww/jgIYhk6IKPpl87gcRRtpyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6cq18%2FbtsHDxA0sww%2FjgIYhk6IKPpl87gcRRtpyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;554&quot; height=&quot;152&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;340&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;3개의 Task로 구성된 DAG&lt;/li&gt;
&lt;li&gt;먼저 t1 실행되고 t2,t3 순으로 순차적 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zoONr/btsHBVDhipH/Kk0ufrGtQl6ImmRHY75cgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zoONr/btsHBVDhipH/Kk0ufrGtQl6ImmRHY75cgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zoONr/btsHBVDhipH/Kk0ufrGtQl6ImmRHY75cgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzoONr%2FbtsHBVDhipH%2FKk0ufrGtQl6ImmRHY75cgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;374&quot; height=&quot;304&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;750&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;3개의 Task로 구성된 DAG&lt;/li&gt;
&lt;li&gt;먼저 t1 실행되고 t2와 t3가 병렬 실행&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <category>Airflow</category>
      <category>airflow 구조</category>
      <category>airflow란</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/185</guid>
      <comments>https://ssdragon.tistory.com/185#entry185comment</comments>
      <pubDate>Sun, 26 May 2024 15:45:45 +0900</pubDate>
    </item>
    <item>
      <title>Airflow 설치 - Docker</title>
      <link>https://ssdragon.tistory.com/184</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html#initializing-environment&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Apache Airflow 공식 홈페이지 - Running Airflow in Docker&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;1. 시작 전 (Before you begin)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/installation/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker Community Edition(CE)&lt;/a&gt;을 본인 컴퓨터에 설치합니다. OS에 따라 Airflow 컨테이너가 제대로 실행되려면 최소 4.00GB에서 권장 8.00GB 메모리를 사용하도록 Docker를 구성해야 합니다. 자세한 내용은 &lt;a href=&quot;https://docs.docker.com/docker-for-windows/#resources&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Windows Docker&lt;/a&gt; 또는 &lt;a href=&quot;https://docs.docker.com/docker-for-mac/#resources&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Mac Docker&lt;/a&gt; 를 참고하세요.&lt;/li&gt;
&lt;li&gt;컴퓨터에 &lt;a href=&quot;https://docs.docker.com/compose/install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker Compose&lt;/a&gt; v2.14.0 이상 설치해야 합니다. 버전을 확인하려면 `docker compose version` 명령어를 실행하세요.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;% docker compose version&lt;br /&gt;Docker Compose version v2.26.1-desktop.1&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. docker-compose.yaml 가져오기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;airflow를 설치할 폴더로 이동하여 docker-compose.yaml 파일을 다운로드합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;curl -LfO 'https://airflow.apache.org/docs/apache-airflow/2.9.1/docker-compose.yaml'&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 환경설정 초기화 (Initializing Environment)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Airflow 를 처음 시작하기 전에 환경을 준비해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 필요한 파일과 디렉토리를 생성하고 DB를 초기화해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Airflow 사용자 설정 (Setting the right Airflow user)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux(리눅스)에서 빠른 시작은 호스트 사용자 아이디를 알아야하고 그룹 아이디가 0으로 설정되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않으면 dags, logs, plugins 에서 생성되는 파일이 루트 사용자 소유권으로 생성됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;mkdir -p ./dags ./logs ./plugins ./config &lt;br /&gt;echo -e &quot;AIRFLOW_UID=$(id -u)&quot; &amp;gt; .env&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 저는 디렉토리만 생성하였고, echo 명령어는 실행하지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스가 아닌 다른 운영체제의 경우 AIRFLOW_UID 가 설정되지 않았다는 경고가 표시될 수 있지만 무시해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용이 포함된 .env 파일을 docker-compose.yaml 과 같은 폴더에 수동으로 생성하여 경고를 제거할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;AIRFLOW_UID=50000&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AIRFLOW_UID 의 값은 기본적으로 50000 이 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/omV2I/btsHz7JKOTG/ZSac3ykxrXzvepqPZRkuz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/omV2I/btsHz7JKOTG/ZSac3ykxrXzvepqPZRkuz1/img.png&quot; data-alt=&quot;지금까지 실행한 명령어 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/omV2I/btsHz7JKOTG/ZSac3ykxrXzvepqPZRkuz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FomV2I%2FbtsHz7JKOTG%2FZSac3ykxrXzvepqPZRkuz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;436&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;지금까지 실행한 명령어 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터베이스 초기화 (Initialize the database)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 운영체제에서 데이터베이스 마이그레이션을 실행하고 첫 번째 사용자 계정을 만들어야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;docker compose up airflow-init&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화가 완료되면 다음과 같은 메시지가 표시됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;airflow-init_1 | Upgrades done &lt;br /&gt;airflow-init_1 | Admin user airflow created&lt;br /&gt;airflow-init_1 | 2.9.1 &lt;br /&gt;start_airflow-init_1 exited with code 0&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 생성된 계정 아이디 : airflow , 비밀번호 : airflow 가 만들어집니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2608&quot; data-origin-height=&quot;998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZiO9Q/btsHAgTY5bw/YzcHhgmbAIviEmnWN0KzY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZiO9Q/btsHAgTY5bw/YzcHhgmbAIviEmnWN0KzY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZiO9Q/btsHAgTY5bw/YzcHhgmbAIviEmnWN0KzY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZiO9Q%2FbtsHAgTY5bw%2FYzcHhgmbAIviEmnWN0KzY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;886&quot; height=&quot;339&quot; data-origin-width=&quot;2608&quot; data-origin-height=&quot;998&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;4. Airflow 시작 (Running Airflow)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 아래 명령어로 Airflow를 시작합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;docker compose up&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`docker ps` 명령어로 모든 프로세스가 동작하는지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 `http://localhost:8080/` 으로 접근하여 로그인페이지로 이동하는지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 생성된 계정으로 로그인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAk853/btsHAqPQOh5/Q4Rv5KHy3N6p5hVWuu9WrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAk853/btsHAqPQOh5/Q4Rv5KHy3N6p5hVWuu9WrK/img.png&quot; data-alt=&quot;docker ps&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAk853/btsHAqPQOh5/Q4Rv5KHy3N6p5hVWuu9WrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAk853%2FbtsHAqPQOh5%2FQ4Rv5KHy3N6p5hVWuu9WrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2550&quot; height=&quot;290&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker ps&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1934&quot; data-origin-height=&quot;1428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dklMoh/btsHy0rl9ay/a3EobSHQzWiEQxArGwjk5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dklMoh/btsHy0rl9ay/a3EobSHQzWiEQxArGwjk5k/img.png&quot; data-alt=&quot;Airflow 로그인페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dklMoh/btsHy0rl9ay/a3EobSHQzWiEQxArGwjk5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdklMoh%2FbtsHy0rl9ay%2Fa3EobSHQzWiEQxArGwjk5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;515&quot; data-origin-width=&quot;1934&quot; data-origin-height=&quot;1428&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Airflow 로그인페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2524&quot; data-origin-height=&quot;1442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0IoiZ/btsHyZFWtYv/qi5FzBCd8SKXO4ARM575j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0IoiZ/btsHyZFWtYv/qi5FzBCd8SKXO4ARM575j0/img.png&quot; data-alt=&quot;airflow 접속&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0IoiZ/btsHyZFWtYv/qi5FzBCd8SKXO4ARM575j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0IoiZ%2FbtsHyZFWtYv%2Fqi5FzBCd8SKXO4ARM575j0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;399&quot; data-origin-width=&quot;2524&quot; data-origin-height=&quot;1442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;airflow 접속&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Airflow 컨테이너로 로그인하는 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. `docker ps` 실행하여 airflow-scheduler-1 의 CONTAINER ID 를 추출한다. 아래 이미지에서는 80d7a9eb7b56 이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2482&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9ttr9/btsHyEoxH8B/sJx6QkYIMLxf4dPKzYV7hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9ttr9/btsHyEoxH8B/sJx6QkYIMLxf4dPKzYV7hk/img.png&quot; data-alt=&quot;docker ps&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9ttr9/btsHyEoxH8B/sJx6QkYIMLxf4dPKzYV7hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9ttr9%2FbtsHyEoxH8B%2FsJx6QkYIMLxf4dPKzYV7hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2482&quot; height=&quot;276&quot; data-origin-width=&quot;2482&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker ps&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Container ID (&quot;80d7a9eb7b56&quot;)로 `docker exec -it 80d7a9eb7b56 sh` 를 실행하면 쉘 모드로 들어갈 수 있고, 거기서 airflow 명령어를 실행할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wJbf3/btsHArA0p0X/bpzMmkChUodmV9ZGiF6vL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wJbf3/btsHArA0p0X/bpzMmkChUodmV9ZGiF6vL1/img.png&quot; data-alt=&quot;airflow 컨테이너 접속&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wJbf3/btsHArA0p0X/bpzMmkChUodmV9ZGiF6vL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwJbf3%2FbtsHArA0p0X%2FbpzMmkChUodmV9ZGiF6vL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;465&quot; height=&quot;152&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;airflow 컨테이너 접속&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. root 계정으로 로그인하려면 `docker exec -u 0 -it&amp;nbsp; 80d7a9eb7b56 sh` 를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Sangyong-Jeon/practice_airflow/wiki/Airflow-%EC%84%A4%EC%B9%98-%E2%80%90-Docker&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Sangyong-Jeon/practice_airflow/wiki/Airflow-%EC%84%A4%EC%B9%98-%E2%80%90-Docker&lt;/a&gt;&lt;/p&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <category>Airflow</category>
      <category>airflow docker</category>
      <category>airflow docker compose</category>
      <category>airflow 도커</category>
      <category>docker</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/184</guid>
      <comments>https://ssdragon.tistory.com/184#entry184comment</comments>
      <pubDate>Thu, 23 May 2024 19:01:10 +0900</pubDate>
    </item>
    <item>
      <title>CodeDeploy - CodePipeline 만드는 도중 실패 및 설명</title>
      <link>https://ssdragon.tistory.com/183</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 실패 이유&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk4jBS/btsHccELrY1/rKhKVgvkQhqMkw7p2tOxXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk4jBS/btsHccELrY1/rKhKVgvkQhqMkw7p2tOxXK/img.png&quot; data-alt=&quot;CodeDeploy 실패 로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk4jBS/btsHccELrY1/rKhKVgvkQhqMkw7p2tOxXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk4jBS%2FbtsHccELrY1%2FrKhKVgvkQhqMkw7p2tOxXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1538&quot; height=&quot;388&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CodeDeploy 실패 로그&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링부트 프로젝트 최상단에 appspec.yml 파일을 만들지 않아서 Deploy가 실패했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;appspec.yml은 CodeDeploy에서 배포 관리하는데 사용하는 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에 정의된 일련의 수명 주기(lifecycle) 이벤트 후크로 각 배포를 관리하는데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/application-specification-files.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS 공식문서 - CodeDeploy 애플리케이션 사양 (AppSpec) 파일&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;2. appspec.yml 생성&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2358&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k1cXe/btsHcJ3cKFf/VvDbhHmh8lzhFMRC6VA92K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k1cXe/btsHcJ3cKFf/VvDbhHmh8lzhFMRC6VA92K/img.png&quot; data-alt=&quot;https://www.youtube.com/watch?v=Dofh_X0ta2g&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k1cXe/btsHcJ3cKFf/VvDbhHmh8lzhFMRC6VA92K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk1cXe%2FbtsHcJ3cKFf%2FVvDbhHmh8lzhFMRC6VA92K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;319&quot; data-origin-width=&quot;2358&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.youtube.com/watch?v=Dofh_X0ta2g&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 규칙대로 정의를 하면 되고, 나는 크게 정의할 필요가 없기에 간단하게 적어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M89cw/btsHb6YSnVk/NxnVVRCuBr323QdBb9r0G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M89cw/btsHb6YSnVk/NxnVVRCuBr323QdBb9r0G0/img.png&quot; data-alt=&quot;스프링부트 프로젝트 최상단 appspec.yml&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M89cw/btsHb6YSnVk/NxnVVRCuBr323QdBb9r0G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM89cw%2FbtsHb6YSnVk%2FNxnVVRCuBr323QdBb9r0G0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;218&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스프링부트 프로젝트 최상단 appspec.yml&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성 후 Github에 추가하면 자동으로 우리가 만들었던 CodePipeline이 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 Source -&amp;gt; Build -&amp;gt; Deploy 가 자동으로 실행하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 성공&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRJXH6/btsHedJsZZI/uPEA8xjg4PSS11KSEwFdP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRJXH6/btsHedJsZZI/uPEA8xjg4PSS11KSEwFdP1/img.png&quot; data-alt=&quot;CodePipeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRJXH6/btsHedJsZZI/uPEA8xjg4PSS11KSEwFdP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRJXH6%2FbtsHedJsZZI%2FuPEA8xjg4PSS11KSEwFdP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;357&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;793&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CodePipeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 codepipeline의 source를 Github 개인 리포지토리로 연결했기에 수정된 사항을 main에 push하면 자동으로 파이프라인이 동작하게 되어 CI/CD가 자동화되는 것이다. 설정하는 단계에서는 복잡했지만 만들고나면 엄청나게 편하다는 것을 느끼게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Code Deploy에 대해서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서 제공하는 Code Deploy는 3가지 배포 방식을 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2/온프레미스&lt;/li&gt;
&lt;li&gt;Lambda&lt;/li&gt;
&lt;li&gt;ECS&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;a href=&quot;https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html#appspec-hooks-server&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS CodeDeploy 라이프사이클과 'hooks' 섹션&lt;/a&gt; 에 나와있는 내용이지만 학습을 위해 간단하게 적어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AppSpec 파일 'hooks' 섹션의 내용은 위에 적었던 3가지 배포 방식(컴퓨팅 플랫폼)에 따라 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2는 인 플레이스(in-place) 배포와 Blue/Green 배포를 선택할 수 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lambda와 ECS는 Blue/Green 배포만 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 EC2를 사용했기에 간단하게 EC2의 Blue/Green(블루/그린) 배포 이벤트 후크를 설명해본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6T2P3/btsHcTY0q5h/P5UjAYvJD1PoSFakcsgfKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6T2P3/btsHcTY0q5h/P5UjAYvJD1PoSFakcsgfKk/img.png&quot; data-alt=&quot;https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/images/lifecycle-event-order-blue-green.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6T2P3/btsHcTY0q5h/P5UjAYvJD1PoSFakcsgfKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6T2P3%2FbtsHcTY0q5h%2FP5UjAYvJD1PoSFakcsgfKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;663&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/images/lifecycle-event-order-blue-green.png&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측이 원본 인스턴스 환경이고, 우측이 새로 만들어지는 인스턴스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;배포 단계에서 `Start`, `DownloadBundle`, `Install`, `AllowTraffic` , `BlockTraffic` , `End` 처럼 다이어그램이 회색인 단계는 스크립팅(scripting)이 불가능하다. 하지만 appspec.yml의 'files' 섹션을 편집하여 `Install` 이벤트 중에 설치되는 항목을 지정할 수는 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ApplicationStop&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이벤트에 대한 스크립트를 지정하여 애플리케이션을 정상적으로 중지하거나 배포를 준비하기 위해 현재 설치된 패키지를 제거할 수 있음. (인스턴스에 처음 배포할 때는 동작하지 않음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DownloadBundle&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신규 생성된 파일(application revision files)을 임시 폴더(위치)로&amp;nbsp; 다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;BeforeInstall&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 암호 해독 및 현재 버전의 백업 생성 등의 사전 설치 작업하는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Install&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임시 폴더에서 최종 대상 폴더로 생성된 파일(revision files)을 복사함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AfterInstall&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 구성 or 파일 권한 변경과 같은 작업하는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ApplicationStart&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ApplicationStop 중에 중지된 서비스를 다시 시작함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ValidateService&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 배포 수명 주기 이벤트로써, 배포가 성공적으로 완료되었는지 확인함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;BeforeBlockTraffic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드 밸런서에서 등록이 취소되기 전에 인스턴스에서 작업을 실행할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계에서 실패한 배포의 문제를 해결하려면 &lt;a href=&quot;https://docs.aws.amazon.com/codedeploy/latest/userguide/troubleshooting-deployments.html#troubleshooting-deployments-lifecycle-event-failures&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;실패한 ApplicationStop, BeforeBlockTraffic 또는 AfterBlockTraffic 배포 수명주기 이벤트 문제해결을 참조&lt;/a&gt;해야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;BlockTraffic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷 트래픽이 현재 트래픽을 서비스 중인 인스턴스에 액세스하지 못하도록 차단함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AfterBlockTraffic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이벤트를 사용하여 해당 로드밸런서에서 인스턴스 등록이 취소된 후 작업을 실행할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 실패한 배포의 문제를 해결하려면 이 &lt;a href=&quot;https://docs.aws.amazon.com/codedeploy/latest/userguide/troubleshooting-deployments.html#troubleshooting-deployments-lifecycle-event-failures&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;h4 data-ke-size=&quot;size20&quot;&gt;BeforeAllowTraffic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서에 등록되기 전에 인스턴스에서 작업을 실행할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AllowTraffic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 후 인터넷 트래픽이 인스턴스에 액세스할 수 있도록 허용함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AfterAllowTraffic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드 밸런서에 인스턴스가 등록된 후 인스턴스에서 작업을 실행할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AWS</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/183</guid>
      <comments>https://ssdragon.tistory.com/183#entry183comment</comments>
      <pubDate>Sun, 5 May 2024 18:09:04 +0900</pubDate>
    </item>
    <item>
      <title>CodeBuild - CodePipeline 만드는 도중 build 실패</title>
      <link>https://ssdragon.tistory.com/182</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 실패 이유&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E1gGn/btsHbgOKJSH/KrCmGKAGFk5XypWBetvq00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E1gGn/btsHbgOKJSH/KrCmGKAGFk5XypWBetvq00/img.png&quot; data-alt=&quot;CodeBuild 실패 로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E1gGn/btsHbgOKJSH/KrCmGKAGFk5XypWBetvq00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE1gGn%2FbtsHbgOKJSH%2FKrCmGKAGFk5XypWBetvq00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;398&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CodeBuild 실패 로그&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: ./gradlew bootJar. Reason: exit status 1&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 깃액션(Github Actions)를 사용했을 때와 비슷한 에러여서 금방 찾기도 했고, 메시지에도 적혀져 있어서 바로 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 빌드 사양에서 런타임 버전이 선택되지 않아서 기본값(default) 버전으로 java compile을 시도했지만 &lt;u&gt;&lt;b&gt;버전이 달라 실패했다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 빌드하려는 스프링부트 프로젝트는 JDK 21 버전을 사용하고 있는데, AWS에서는 기본값으로 다른 버전을 사용하나 보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 `./graldew bootJar` 가 성공적으로 종료되지 않았다고 적혀있는걸 보고 버전을 명시해줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. CodeBuild 의 Buildspec 수정&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IiCFx/btsHefHg1sU/6IazAyHKcnlM9kKLlMJPd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IiCFx/btsHefHg1sU/6IazAyHKcnlM9kKLlMJPd1/img.png&quot; data-alt=&quot;CodeBuild Buildspec&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IiCFx/btsHefHg1sU/6IazAyHKcnlM9kKLlMJPd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIiCFx%2FbtsHefHg1sU%2F6IazAyHKcnlM9kKLlMJPd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;472&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CodeBuild Buildspec&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/codebuild/latest/userguide/runtime-versions.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS 공식문서의 Runtime versions&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;3. 수정 후 성공&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1287&quot; data-origin-height=&quot;819&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7TYf8/btsHbBdZ7mA/0V0XE2CjetSyGrp662HNh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7TYf8/btsHbBdZ7mA/0V0XE2CjetSyGrp662HNh1/img.png&quot; data-alt=&quot;CodeBuild Log내용 - 성공&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7TYf8/btsHbBdZ7mA/0V0XE2CjetSyGrp662HNh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7TYf8%2FbtsHbBdZ7mA%2F0V0XE2CjetSyGrp662HNh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;443&quot; data-origin-width=&quot;1287&quot; data-origin-height=&quot;819&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CodeBuild Log내용 - 성공&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적인 로그 기록을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T3wAH/btsHbmIbFQ0/XgHkUlhFadfLmnlILbcyMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T3wAH/btsHbmIbFQ0/XgHkUlhFadfLmnlILbcyMK/img.png&quot; data-alt=&quot;Code Pipeline 성공&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T3wAH/btsHbmIbFQ0/XgHkUlhFadfLmnlILbcyMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT3wAH%2FbtsHbmIbFQ0%2FXgHkUlhFadfLmnlILbcyMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;320&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Code Pipeline 성공&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AWS</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/182</guid>
      <comments>https://ssdragon.tistory.com/182#entry182comment</comments>
      <pubDate>Sun, 5 May 2024 16:44:52 +0900</pubDate>
    </item>
    <item>
      <title>장고 활용한 API 서버 만들기(2)</title>
      <link>https://ssdragon.tistory.com/181</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;뷰와 템플릿&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SQL order by desc 및 limit 구현 예시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Question.objects.order_by('-pub_date')[:5]&lt;/li&gt;
&lt;li&gt;order_by 컬럼명 앞 &quot;-&quot;이 있으면 내림차순(desc), 없으면 오름차순(asc)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;model에 있는 내용 화면(view)에 출력하는 과정
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;templates/polls/index.html 생성&lt;/li&gt;
&lt;li&gt;views.py 에서 context 변수에 dict 형태로 선언
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;context = {'first_question' : latest_question_list[0] }
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'first_question' 이라는 키와 latest_question_list[0] 이라는 값을&amp;nbsp; index.html에 전달할 변수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;index.html에 전달하고자 view.py 에 from django.shortcuts import render 선언&lt;/li&gt;
&lt;li&gt;index 함수 내 `return render(request, 'polls/index.html', context)` 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;템플릿에서 제어문 사용하기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;{% if 조건 %} 내용 {% elif 조건 %} 내용 {% else %} 내용 {% endif %}&lt;/li&gt;
&lt;li&gt;{% for e in elements %} 내용 {% endfor %}&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;404 에러 처리하기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;try-except 처리&lt;/li&gt;
&lt;li&gt;get_object_or_404() 처리&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;폼(Form)과 커스터마이징&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;form&amp;gt; 옵션 action과 method 속성 지정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;action=&quot;{% url 'polls:vote' question.id %}&quot;&lt;/li&gt;
&lt;li&gt;method=&quot;post&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;urls.py 에 polls:vote 에 대한 처리 로직 추가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;path('&amp;lt;int:question_id&amp;gt;/vote/', views.vote, name='vote'),&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;views.py 에 응답 항목에 대한 save 및 예외 처리 로직 추가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;def vote(request, question_id): 추가&lt;/li&gt;
&lt;li&gt;request.POST('choice'] 를 통해 선택한 항목 가져오기&lt;/li&gt;
&lt;li&gt;예외처리 : 선택하지 않을 경우 try-except-else
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KeyError 또는 Choice.DoesNotExist 예외 발생시 render의 context에 error_message 추가 전달
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KeyError 예외 : 선택하지 않은 채 제출&lt;/li&gt;
&lt;li&gt;Choice.DoesNotExist 예외 : choice id가 제출되는 시점에서 존재하지 않는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정상 요청인 경우 votes += 1 및 save() 이후 Redirect 수행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HttpREsponseRedirect(reverse('polls:index'))&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Python 문법 - *args, **kwargs&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://docs.python.org/3/glossary.html#term-parameter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;설명 링크&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;*args : 여러 개의 인자를 묶어서 하나의 튜플로 묵어주고 이를 args에 할당한다고 생각하자&lt;/li&gt;
&lt;li&gt;**kwargs : 여러 개의 keyword arguments 들을 묶어서 딕셔너리로 만들어준다고 생각하자&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/181</guid>
      <comments>https://ssdragon.tistory.com/181#entry181comment</comments>
      <pubDate>Tue, 23 Apr 2024 23:08:39 +0900</pubDate>
    </item>
    <item>
      <title>장고 활용한 API 서버 만들기</title>
      <link>https://ssdragon.tistory.com/180</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Django Project 생성하기&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$ django-admin startproject 프로젝트이름&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Manage.py&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Django 프로젝트를 터미널에서 관리할 수 있도록 명령어를 제공하는 기능의 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Django 서버 실행 명령어&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$&amp;nbsp;python manage.py runserver&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Django App 생성하기&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$&amp;nbsp;python manage.py startapp polls&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정한 기능을 수행하는 웹 어플리케이션으로 App들이 모여 하나의 Project가 됨&lt;/li&gt;
&lt;li&gt;App은 하나의 웹 사이트만 종속되는 것이 아닌, 여러 사이트에서 그 기능을 할 수 있음&lt;/li&gt;
&lt;li&gt;`urls.py` 에서 앱 경로를 지정할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모델&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장고의 모델은 RDB에서 엔티티(테이블)라고 보면 됨&lt;/li&gt;
&lt;li&gt;모델을 통해 데이터를 편하게 관리 가능&lt;/li&gt;
&lt;li&gt;별도의 SQL 쿼리문 작성 없이 프로그래밍 언어로 데이터의 CRUD 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모델 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1712625035383&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Question(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 테이블(엔티티)를 위 코드처럼 만듦.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본키는 자동으로 AUTOINCREMENT 하는 정수형을 만들어줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;migration 적용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 생성했으면 스키마에도 구조가 변경된 것을 적용해야함.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$ python manage.py makemigrations polls&lt;br /&gt;$ python manage.py sqlmigrate polls 0001&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DB 내용 확인하기&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$ sqlite3 db.sqlite3&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고의 DB는 기본적으로 sqlite3을 지원하기에 db.sqlite3 파일을 열어 확인 가능함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;migration 롤백&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$ python manage.py migrate polls 0001&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Django Admin&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관리자들이 데이터를 추가하거나 수정하는 페이지&lt;/li&gt;
&lt;li&gt;장고에서는 생성한 모델에 대해 웹 페이지에서 데이터를 CRUD하는 기능 지원&lt;/li&gt;
&lt;li&gt;`http://127.0.0.1/admin` 으로 접속 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;관리자 계정 생성하기&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$ python manage.py createsuperuser&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모델 등록하기&lt;/h4&gt;
&lt;pre id=&quot;code_1712625315724&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.contrib import admin
from .models import Question, Choice

# Register your models here.
admin.site.register(Question)
admin.site.register(Choice)&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;admin이 접근할 수 있도록 코드 추가함&lt;/li&gt;
&lt;li&gt;App 폴더의 admin.py에 위 코드를 추가함&lt;/li&gt;
&lt;li&gt;이후 admin 페이지에서 손쉽게 등록 가능&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;Django shell&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$ python manage.py shell&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고의 쉘로 접속하려면 위 명령어를 터미널에서 입력함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모델 사용하기&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;gt;&amp;gt;&amp;gt; from polls.models import *&lt;br /&gt;&lt;br /&gt;#모든 Question,Choice 오브젝트 가져오기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; Question.objects.all() &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; Choice.objects.all()&lt;br /&gt;&lt;br /&gt;#첫번째 Choice 오브젝트 가져오기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice = Choice.objects.all()[0] &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.id &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.choice_text &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.votes&lt;br /&gt;&lt;br /&gt;#첫번째 Choice와 연결된 Question 가져오기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.question &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.question.pub_date &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.question.id&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Django로 웹서비스를 하고, 글로벌 서비스를 수행한다면 파이썬에서 제공하는 `datetime` 모듈보다는 `timezone` 을 사용하자&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;새로운 모델 생성하기&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;#create() 메서드를 활용하여 q3와 연결된 새로운 Choice 오브젝트를 생성하고, choice_text 필드에 값을 넣어주기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; q3.choice_set.create(choice_text = &quot;b&quot;) &lt;br /&gt;&lt;br /&gt;#새로운 Choice 오브젝트를 생성하고 question 필드에 q3 값을 넣어 연결하기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice_c = Choice(choice_text='c', question=q3) &lt;br /&gt;&lt;br /&gt;#새로운 Choice 오브젝트를 데이터베이스에 저장하기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice_c.save()&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;레코드 수정 및 삭제하기&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;gt;&amp;gt;&amp;gt; from polls.models import * &lt;br /&gt;&lt;br /&gt;#Question 오브젝트 중 가장 마지막으로 만들어진 것을 가져오기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; q = Question.objects.last() &lt;br /&gt;&lt;br /&gt;#해당 오브젝트의 question_text에 새로운 내용을 더해 수정하기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; q.question_text = q.question_text + '???' &lt;br /&gt;&lt;br /&gt;# DB에 수정내용 적용하기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; q1.save() #Choice 오브젝트 중 가장 마지막으로 만들어진 것을 가져오기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice = Question.objects.last() &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.queston.choice_set.all() &lt;br /&gt;&amp;lt;QuerySet [&amp;lt;Choice: a&amp;gt;, &amp;lt;Choice: b&amp;gt;, &amp;lt;Choice: c&amp;gt;, &amp;lt;Choice: d&amp;gt;]&amp;gt;&lt;br /&gt;&lt;br /&gt;#해당 오브젝트를 삭제하기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.delete() &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; choice.queston.choice_set.all() &lt;br /&gt;&amp;lt;QuerySet [&amp;lt;Choice: a&amp;gt;, &amp;lt;Choice: b&amp;gt;, &amp;lt;Choice: c&amp;gt;&amp;gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모델 필터링&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;gt;&amp;gt;&amp;gt; Question.objects.get(id=1) &lt;br /&gt;&amp;lt;Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2024-04-07 14:34:00+00:00&amp;gt; &lt;br /&gt;&lt;br /&gt;# '휴가를' 로 시작하는 행 찾기 &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; q = Question.objects.get(question__startswith='휴가를 ') &lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; q &lt;br /&gt;&amp;lt;Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2024-04-07 14:34:00+00:00&amp;gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 개만 가져올 때는 `.get()` 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;gt;&amp;gt;&amp;gt; Question.objects.filter(pub_date__year=2024) &lt;br /&gt;&amp;lt;QuerySet [&amp;lt;Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2024-04-07 14:34:00+00:00&amp;gt;, &amp;lt;Question: 제목: 가장 좋아하는 디저트는?, 날짜: 2024-04-07 14:34:22+00:00&amp;gt;, &amp;lt;Question: 제목: 커피 vs 녹차, 날짜: 2024-04-08 05:43:35.350925+00:00&amp;gt;, &amp;lt;Question: 제목: abc???, 날짜: 2024-04-08 05:45:30.479683+00:00&amp;gt;]&amp;gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개가 반환된다면 `.filter()` 사용&lt;/li&gt;
&lt;li&gt;이 경우, QuerySet 이라는 집합으로 결과를 반환함&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/180</guid>
      <comments>https://ssdragon.tistory.com/180#entry180comment</comments>
      <pubDate>Tue, 9 Apr 2024 10:22:47 +0900</pubDate>
    </item>
    <item>
      <title>Seaborn 시각화 라이브러리, 기상청 날씨 정보 시각화, 해시코드 질문태그 빈도 시각화, 단어구름 시각화</title>
      <link>https://ssdragon.tistory.com/179</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Seaborn 시각화 라이브러리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python의 데이터 시각화 라이브러리&lt;/li&gt;
&lt;li&gt;이를 활용하여 그래프 시각화&lt;/li&gt;
&lt;li&gt;%pip install seaborn 으로 설치&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;Seaborn Essentials&lt;/h2&gt;
&lt;pre id=&quot;code_1712395639269&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 시각화에 필요한 라이브러리를 불러와봅시다.
import seaborn as sns&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;꺾은선 그래프 (Line Plot)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 변수의 값에 따른 추이를 선으로 이은 그래프&lt;/li&gt;
&lt;li&gt;`.lienplot()` 를 이용해서 그릴 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1712395699252&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Scatterplot을 직접 그려봅시다
sns.lineplot(x=[1, 3, 2, 4], y=[0.7,0.2,0.1,0.05])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HLjZp/btsGpJRRHlT/g0blVDPl4vDA4DWRW2KTJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HLjZp/btsGpJRRHlT/g0blVDPl4vDA4DWRW2KTJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HLjZp/btsGpJRRHlT/g0blVDPl4vDA4DWRW2KTJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHLjZp%2FbtsGpJRRHlT%2Fg0blVDPl4vDA4DWRW2KTJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;387&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;막대 그래프 (Bar Plot)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;범주형 데이터의 값과 그 값의 크기를 직사각형으로 나타낸 그림&lt;/li&gt;
&lt;li&gt;`.bar()` 를 이용해서 그릴 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1712395851855&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Barplot을 직접 그려봅시다
sns.barplot(x=[&quot;amy&quot;,&quot;bob&quot;,&quot;cat&quot;,&quot;dog&quot;],y=[0.7,0.2,0.1,0.05])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XTT6W/btsGqyIHIAw/QsyRCHPZMSdlCpJXSOTSJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XTT6W/btsGqyIHIAw/QsyRCHPZMSdlCpJXSOTSJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XTT6W/btsGqyIHIAw/QsyRCHPZMSdlCpJXSOTSJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXTT6W%2FbtsGqyIHIAw%2FQsyRCHPZMSdlCpJXSOTSJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;479&quot; height=&quot;411&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;482&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;Plot 속성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;seaborn 은 파이썬 시각화 라이브러리 matplotlib 기반으로 만들어짐&lt;/li&gt;
&lt;li&gt;matplotlib.pyplot 의 속성을 변경해서 그래프에 다양한 요소를 변경 및 추가 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그래프 제목 추가하기&lt;/h4&gt;
&lt;pre id=&quot;code_1712395897446&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# matplotlib.pyplot을 불러오기
import matplotlib.pyplot as plt

sns.barplot(x=[1,2,3,4],y=[0.7,0.2,0.1,0.05])

# 그래프 제목 추가하기
plt.title(&quot;Bar Plot&quot;)

# 보여주기
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TAykj/btsGqZTlHiH/XJTEnUKwV2gdRK6NW6TlIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TAykj/btsGqZTlHiH/XJTEnUKwV2gdRK6NW6TlIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TAykj/btsGqZTlHiH/XJTEnUKwV2gdRK6NW6TlIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTAykj%2FbtsGqZTlHiH%2FXJTEnUKwV2gdRK6NW6TlIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;369&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;X, Y Label 추가하기&lt;/h4&gt;
&lt;pre id=&quot;code_1712395964679&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sns.barplot(x=[1,2,3,4],y=[0.7,0.2,0.1,0.05])

# xlabel과 ylabel 추가하기
plt.xlabel(&quot;X Label&quot;)
plt.ylabel(&quot;Y Label&quot;)

plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1004&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uQkkI/btsGp2wOICA/Wbg0ktxn4yDbz8KY1KGXi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uQkkI/btsGp2wOICA/Wbg0ktxn4yDbz8KY1KGXi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uQkkI/btsGp2wOICA/Wbg0ktxn4yDbz8KY1KGXi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuQkkI%2FbtsGp2wOICA%2FWbg0ktxn4yDbz8KY1KGXi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;360&quot; data-origin-width=&quot;1004&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그래프 축 범위 지정하기&lt;/h4&gt;
&lt;pre id=&quot;code_1712396012279&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sns.lineplot(x=[1,3,2,4],y=[4,3,2,1])

# lineplot에서 ylim을 2~3으로 제한해봅시다.
plt.ylim(0, 10)

plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czyOgS/btsGqs9FdcK/EET0CBLKKRFYneDC80u7D0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czyOgS/btsGqs9FdcK/EET0CBLKKRFYneDC80u7D0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czyOgS/btsGqs9FdcK/EET0CBLKKRFYneDC80u7D0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczyOgS%2FbtsGqs9FdcK%2FEET0CBLKKRFYneDC80u7D0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;521&quot; height=&quot;395&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그래프 크기 지정하기&lt;/h4&gt;
&lt;pre id=&quot;code_1712396070329&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 크기를 (20, 10)으로 지정해봅시다.
plt.figure(figsize=(20, 10))
sns.lineplot(x=[1,3,2,4],y=[4,3,2,1])
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZEOwe/btsGqhgccBs/uq0DbSK8EWKM0Lg1LqEgP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZEOwe/btsGqhgccBs/uq0DbSK8EWKM0Lg1LqEgP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZEOwe/btsGqhgccBs/uq0DbSK8EWKM0Lg1LqEgP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZEOwe%2FbtsGqhgccBs%2Fuq0DbSK8EWKM0Lg1LqEgP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;272&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기상청 날씨 정보 시각화&lt;/h2&gt;
&lt;pre id=&quot;code_1712396163595&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스크래핑에 필요한 라이브러리를 불러와봅시다.

from selenium import webdriver
from selenium.webdriver import ActionChains
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver import Keys, ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

# driver를 이용해 기상청 날씨 데이터를 가져오기
driver = webdriver.Chrome()
driver.get(&quot;https://www.weather.go.kr/w/weather/forecast/short-term.do&quot;)
driver.implicitly_wait(2)
temps = driver.find_element(By.ID, &quot;my-tchart&quot;).text

# 온도에서 &quot;℃&quot; 제거하여 숫자 배열 만들기
temps = [int(i) for i in temps.replace(&quot;℃&quot;, &quot;&quot;).split(&quot;\n&quot;)]

# 받아온 데이터를 통해 꺾은선 그래프 그려보기
import seaborn as sns

sns.lineplot(
    x = [i for i in range(len(temps))],
    y = temps
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnW90m/btsGq1czNCU/SkGlCjZvVbdFZ18Dp2qKVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnW90m/btsGq1czNCU/SkGlCjZvVbdFZ18Dp2qKVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnW90m/btsGq1czNCU/SkGlCjZvVbdFZ18Dp2qKVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnW90m%2FbtsGq1czNCU%2FSkGlCjZvVbdFZ18Dp2qKVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;443&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1712396194779&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 받아온 데이터를 통해 꺾은선 그래프를 그리고, 그래프 축 범위와 제목 지정하기
import matplotlib.pyplot as plt

plt.ylim(min(temps)-2, max(temps) + 2)
plt.title(&quot;Expected Temperature from now on&quot;)

sns.lineplot(
    x = [i for i in range(len(temps))],
    y = temps
)

plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w1IRu/btsGreir4xs/TgGO6QDG5uTVzVVJ5itM50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w1IRu/btsGreir4xs/TgGO6QDG5uTVzVVJ5itM50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w1IRu/btsGreir4xs/TgGO6QDG5uTVzVVJ5itM50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw1IRu%2FbtsGreir4xs%2FTgGO6QDG5uTVzVVJ5itM50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;497&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해시코드 질문태그 빈도 시각화&lt;/h2&gt;
&lt;pre id=&quot;code_1712396231312&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# User-Agent 추가
user_agent = {&quot;User-Agent&quot;: &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36&quot;}

# 필요한 라이브러리를 불러온 후, 요청을 진행
import time
import requests
from bs4 import BeautifulSoup

# 질문의 빈도를 체크하는 dict를 만든 후, 빈도를 체크
frequency = {}

for i in range(1, 11):
    res = requests.get(&quot;https://hashcode.co.kr/?page={}&quot;.format(i), user_agent)
    soup = BeautifulSoup(res.text, &quot;html.parser&quot;)

    # 1. ul 태그를 모두 찾기
    # 2. 1번 안에 있는 li 태그의 text 추출

    ul_tags = soup.find_all(&quot;ul&quot;, &quot;question-tags&quot;)
    for ul in ul_tags:
        li_tags = ul.find_all(&quot;li&quot;)
        for li in li_tags:
            tag = li.text.strip()
            if tag not in frequency:
                frequency[tag] = 1
            else:
                frequency[tag] += 1
    time.sleep(0.5)

# Counter를 사용해 가장 빈도가 높은 value들을 추출
from collections import Counter

counter = Counter(frequency)

# Seaborn을 이용해 이를 Barplot으로 그리기
import seaborn as sns

x = [elem[0] for elem in counter.most_common(10)]
y = [elem[1] for elem in counter.most_common(10)]

# figure, xlabel, ylabel, title을 적절하게 설정해서 시각화 완성
import matplotlib.pyplot as plt

plt.figure(figsize=(20, 10))
plt.title(&quot;Frequency of question in Hashcode&quot;)
plt.xlabel(&quot;Tag&quot;)
plt.ylabel(&quot;Frequency&quot;)

sns.barplot(x=x, y=y)
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlEw2Y/btsGrfhltoU/LJ0PK3lyOMFuCpk3XlUBjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlEw2Y/btsGrfhltoU/LJ0PK3lyOMFuCpk3XlUBjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlEw2Y/btsGrfhltoU/LJ0PK3lyOMFuCpk3XlUBjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlEw2Y%2FbtsGrfhltoU%2FLJ0PK3lyOMFuCpk3XlUBjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;348&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;뭉게뭉게 단어구름 시각화&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;wordcloud 라이브러리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python의 텍스트 클라우드 라이브러리&lt;/li&gt;
&lt;li&gt;이를 기반으로 텍스트 구름 그릴 수 있음&lt;/li&gt;
&lt;li&gt;konlpy : 한국어 형태소 분석기 라이브러리로, 주어진 문장에서 명사 등을 뽑아 내는 데에 사용 가능&lt;/li&gt;
&lt;li&gt;`%pip install wordcloud`&lt;/li&gt;
&lt;li&gt;`%pip install konlpy`&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712396366607&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 텍스트 구름을 그리기 위해 필요한 라이브러리를 불러오기

# 시각화에 쓰이는 라이브러리
import matplotlib.pyplot as plt
from wordcloud import WordCloud

# 횟수를 기반으로 딕셔너리 생성
from collections import Counter

# 문장에서 명사를 추출하는 형태소 분석 라이브러리
from konlpy.tag import Hannanum

# 워드클라우드를 만드는 데 사용할 애국가 가사
national_anthem = &quot;&quot;&quot;
동해물과 백두산이 마르고 닳도록
하느님이 보우하사 우리나라 만세
무궁화 삼천리 화려 강산
대한 사람 대한으로 길이 보전하세
남산 위에 저 소나무 철갑을 두른 듯
바람 서리 불변함은 우리 기상일세
무궁화 삼천리 화려 강산
대한 사람 대한으로 길이 보전하세
가을 하늘 공활한데 높고 구름 없이
밝은 달은 우리 가슴 일편단심일세
무궁화 삼천리 화려 강산
대한 사람 대한으로 길이 보전하세
이 기상과 이 맘으로 충성을 다하여
괴로우나 즐거우나 나라 사랑하세
무궁화 삼천리 화려 강산
대한 사람 대한으로 길이 보전하세
&quot;&quot;&quot;

# 형태소 분석기 객체를 사용하여 주어진 문자열에서 명사 추출하기
# Hannanum 객체를 생성한 후, .nouns()를 통해 명사를 추출
hannanum = Hannanum()
nouns = hannanum.nouns(national_anthem)
words = [noun for noun in nouns if len(noun) &amp;gt; 1]

# counter를 이용해 각 단어의 개수를 세기
counter = Counter(words)

# WordCloud를 이용해 텍스트 구름을 만들기
# 이 때, font_path는 한글을 그려주기 위한 폰트를 지정해야해서 각 컴퓨터의 폰트경로 지정
wordcloud = WordCloud(
    font_path=&quot;/Users/yong/Library/Fonts/함초롱돋움R.ttf&quot;,
    background_color=&quot;white&quot;,
    width=1000,
    height=1000
)

img = wordcloud.generate_from_frequencies(counter)
plt.imshow(img)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;942&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pex7q/btsGqjSCrRT/nkssiD72fGpLDiTZtAND5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pex7q/btsGqjSCrRT/nkssiD72fGpLDiTZtAND5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pex7q/btsGqjSCrRT/nkssiD72fGpLDiTZtAND5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpex7q%2FbtsGqjSCrRT%2FnkssiD72fGpLDiTZtAND5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;569&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;942&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;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/179</guid>
      <comments>https://ssdragon.tistory.com/179#entry179comment</comments>
      <pubDate>Sat, 6 Apr 2024 18:40:09 +0900</pubDate>
    </item>
    <item>
      <title>Selenium, WebDriver, Implicit / Explicit Wait, Mouse Event, Keyboard Event</title>
      <link>https://ssdragon.tistory.com/178</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Selenium 라이브러리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python을 이용해서 웹 브라우저를 조작할 수 있는 자동화 프레임워크&lt;/li&gt;
&lt;li&gt;%pip install selenium 으로 설치
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;% 를 이용해서 노트북(.ipynb) 환경에서 터미널 코드 실행 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Web Driver&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 브라우저를 제어할 수 있는 자동화 프레임워크&lt;/li&gt;
&lt;li&gt;%pip install webdriver-manager 으로 설치&lt;/li&gt;
&lt;li&gt;Chrome (크롬) 브라우저를 사용하기에 크롬 설치 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712393792392&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# selenium 으로부터 webdriver 모듈 불러오기
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# webdriver에서 Chrome() 객체 생성
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
# driver = webdriver.Chrome()

# http://www.example.com 으로 요청 보내기
driver.get(&quot;http://www.example.com&quot;)

# page_source 속성을 통해 Response의 HTML 문서 확인하기
print(driver.page_source)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 실행 시, Chrome 창이 계속 켜져있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 해당 창을 끄라고 명령을 내린 적이 없기 때문임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712393817219&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# with-as 를 사용해서 창을 끄도록 해보자
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get(&quot;http://www.example.com&quot;)
    print(driver.page_source)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;with 안의 코드가 실행되면 자동으로 Chrome 창이 닫아짐&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;Driver 에서 특정 요소 추출하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;selenium 은 받아온 응답으로부터 특정 요소 추출 가능&lt;/li&gt;
&lt;li&gt;요소 하나 찾기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.find_element(by, target)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;by : 대상을 찾는 기준 ( ID, TAG_NAME, CLASS_NAME &amp;hellip;)&lt;/li&gt;
&lt;li&gt;target : 대상의 속성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;요소 여러개 찾기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.find_elements(by, target)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;by : 대상을 찾는 기준 ( ID, TAG_NAME, CLASS_NAME &amp;hellip;)&lt;/li&gt;
&lt;li&gt;target : 대상의 속성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712393856053&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# By 를 import 하기
from selenium.webdriver.common.by import By

# &amp;lt;p&amp;gt; 해당하는 요소 하나를 찾아보기
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get(&quot;http://www.example.com&quot;)
    print(driver.find_element(By.TAG_NAME, &quot;p&quot;).text)
    
# &amp;lt;p&amp;gt; 해당하는 요소 여러개 찾아보기
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get(&quot;http://www.example.com&quot;)
    for element in driver.find_elements(By.TAG_NAME, &quot;p&quot;):
        print(&quot;Text:&quot;, element.text)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Implicit Wait (암시적 기다림)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 요소에 대한 제약을 통한 기다림&lt;/li&gt;
&lt;li&gt;로딩이 다 될 때까지의 한계 시간의 의미를 가지는 것&lt;/li&gt;
&lt;li&gt;e.g. 이 태그를 가져올 수 있을 때까지 기다리기&lt;/li&gt;
&lt;li&gt;`.implicitly_wait()` 를 활용하여 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Explicit Wait&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다 로딩일 될 때까지 지정한 시간동안 기다리기&lt;/li&gt;
&lt;li&gt;`WebDriverWait()` 와 아래 메서드를 활용하여 사용 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`until()` : 인자의 조건이 만족될 때까지&lt;/li&gt;
&lt;li&gt;`until_not()` : 인자의 조건이 만족되지 않을 때까지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;e.g. 다 로딩이 될 때까지 5초동안 기다리기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;XPath 란? &lt;br /&gt;- XML, HTML 문서 등의 요소의 위치를 경로로 표현하는 것&lt;br /&gt;- 마치`데스크탑/폴더1/폴더2/음악.mp3` 같은 경로&lt;/blockquote&gt;
&lt;pre id=&quot;code_1712393901207&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스크래핑에 필요한 라이브러리를 불러와봅시다.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

# 예시 사이트에 요청을 진행하고, 예시 사이트의 첫 번째 이벤트의 제목을 가져와봅시다.
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get(&quot;https://indistreet.com/live?sortOption=startDate%3AASC&quot;)

# XPath를 이용해서 특정 위치의 값 가져오기
# 이 때, 동적 페이지이기 때문에 스크래핑 시 데이터가 없어서 오류가 날 수 있음
# 따라서 Wait를 넣어줘야함
driver.find_element(By.XPATH, '//*[@id=&quot;__next&quot;]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]').text

# Implicit Wait를 사용하기 위해 import하기
from selenium.webdriver.support.ui import WebDriverWait

# 10초동안 Implicit Wait를 진행하여 스크래핑이 잘 이루어지도록 수정
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get(&quot;https://indistreet.com/live?sortOption=startDate%3AASC&quot;)
    driver.implicitly_wait(10)
    print(driver.find_element(By.XPATH, '//*[@id=&quot;__next&quot;]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]').text)
    
# EC는 selenium에서 정의된 조건들
# EC의 expected_conditions는 '~가 존재하면' 이라는 의미
from selenium.webdriver.support import expected_conditions as EC

with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get(&quot;https://indistreet.com/live?sortOption=startDate%3AASC&quot;)
    # explicit wait으로 변경하여, 특정 요소가 존재할 때까지 10초동안 기다리기
    element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@id=&quot;__next&quot;]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]')))
    print(element.text)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mouse Event (마우스 이벤트) 처리하기&lt;/h2&gt;
&lt;pre id=&quot;code_1712393937385&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스크래핑에 필요한 라이브러리를 불러와봅시다.

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

# 주어진 웹사이트를 누른 후, 우리가 원하는 버튼 요소를 찾은 후 마우스 이벤트를 실행시켜봅시다.
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get(&quot;https://hashcode.co.kr&quot;)
driver.implicitly_wait(2)

# 로그인 버튼 찾기
button = driver.find_element(By.CLASS_NAME, 'UtilMenustyle__Link-sc-2sjysx-4.ewJwEL')
# 로그인 버튼 클릭하기
ActionChains(driver).click(button).perform()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 실행 시, hashcode 홈페이지가 켜지고, 그 페이지에 있는 로그인 버튼 자동 클릭&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;Keyboard Event (키보드 이벤트) 처리하기&lt;/h2&gt;
&lt;pre id=&quot;code_1712393972355&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스크래핑에 필요한 라이브러리를 불러와봅시다.

from selenium import webdriver
from selenium.webdriver import ActionChains
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver import Keys, ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

import time

driver = webdriver.Chrome()
driver.get(&quot;https://hashcode.co.kr&quot;)
time.sleep(0.5)

# 내비게이션 바에서 &quot;로그인&quot; 버튼을 찾아서 눌리기
button = driver.find_element(By.CLASS_NAME, 'UtilMenustyle__Link-sc-2sjysx-4.ewJwEL')
ActionChains(driver).click(button).perform()
time.sleep(0.5)

# &quot;아이디&quot; input 요소에 아이디 입력하기
id_input = driver.find_elements(By.CLASS_NAME, &quot;FymRFM681OjzOdzor5nk&quot;)[0]
ActionChains(driver).send_keys_to_element(id_input, &quot;아이디@gmail.com&quot;).perform()
time.sleep(0.5)

# &quot;패스워드&quot; input 요소에 비밀번호 입력하기
pw_input = driver.find_elements(By.CLASS_NAME, &quot;FymRFM681OjzOdzor5nk&quot;)[1]
ActionChains(driver).send_keys_to_element(pw_input, &quot;비밀번호&quot;).perform()
time.sleep(0.5)

# &quot;로그인&quot; 버튼을 눌러서 로그인 완료하기
login_button = driver.find_element(By.CLASS_NAME, &quot;itAWTII94uCyf9uUgREi&quot;)
ActionChains(driver).click(login_button).perform()
time.sleep(0.5)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 실행 시, 홈페이지 켜서 로그인 버튼 눌려서 로그인 화면 들어가고, 아이디와 비밀번호 입력하여 로그인 완료시킴&lt;/p&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/178</guid>
      <comments>https://ssdragon.tistory.com/178#entry178comment</comments>
      <pubDate>Sat, 6 Apr 2024 17:59:54 +0900</pubDate>
    </item>
    <item>
      <title>Python - Web Scraping 기초 (BeautifulSoup4 라이브러리)</title>
      <link>https://ssdragon.tistory.com/177</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;BeautifulSoup4 라이브러리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML 코드를 분석해주는 HTML Parser 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712149730169&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pip를 사용해 라이브러리 설치
%pip install bs4

# 사이트에 요청한 후 응답 받기
import requests

res = requests.get(&quot;http://www.example.com&quot;)

# BeautifulSoup4 - bs4 불러오기
from bs4 import BeautifulSoup

# BeautifulSoup 객체 생성하기. 
# 첫번째 인자는 response의 body를 텍스트
# 두번째 인자는 &quot;html&quot;로 분석한다는 것을 명시
soup = BeautifulSoup(res.text, &quot;html.parser&quot;)

# 객체 soup의 .prettify()를 활용하여 분석된 HTML을 보기 편하게 반환하기
print(soup.prettify())&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2264&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDCL1I/btsGmB5KckZ/gvG9RVAbx4kHPPxxeNgXf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDCL1I/btsGmB5KckZ/gvG9RVAbx4kHPPxxeNgXf0/img.png&quot; data-alt=&quot;위 코드의 마지막 실행 내역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDCL1I/btsGmB5KckZ/gvG9RVAbx4kHPPxxeNgXf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDCL1I%2FbtsGmB5KckZ%2FgvG9RVAbx4kHPPxxeNgXf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;262&quot; data-origin-width=&quot;2264&quot; data-origin-height=&quot;854&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 코드의 마지막 실행 내역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위 코드의 soup로 조작하여 HTML에 쉽게 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712150494971&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# title 가져오기
soup.title

# head 가져오기
soup.head

# body 가져오기
soup.body

# &amp;lt;h1&amp;gt; 태그로 감싸진 요소 찾기
h1 = soup.find(&quot;h1&quot;)

# 태그 이름 가져오기
h1.name

# 태그 내용 가져오기
h1.text

# &amp;lt;h3&amp;gt; 태그로 감싸진 모든 요소 찾기
h3_result = soup.find_all(&quot;h3&quot;)

# &amp;lt;h3&amp;gt; 태그로 감싸진 모든 요소의 내용 추출하기
for t in h3_result:
	print(t.text)
    
# id가 results인 div 태그 찾기
soup.find(&quot;div&quot;, id=&quot;results&quot;)

# class가 &quot;page-header&quot;인 div 태그 찾기
find_result = soup.find(&quot;div&quot;, &quot;page-header&quot;)

# Pagination이 되어있는 hashcode 질문 리스트의 제목을 모두 가져오기
# 과도한 요청 방지로 1초마다 요청을 보냄
import time

for i in range(1, 6):
    res = requests.get(&quot;https://hashcode.co.kr/?page={}&quot;.format(i), user_agent)
    soup = BeautifulSoup(res.text, &quot;html.parser&quot;)
    questions = soup.find_all(&quot;li&quot;, &quot;question-list-item&quot;)
    for question in questions:
        print(question.find(&quot;div&quot;, &quot;question&quot;).find(&quot;div&quot;, &quot;top&quot;).h4.text)
    time.sleep(0.5)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/177</guid>
      <comments>https://ssdragon.tistory.com/177#entry177comment</comments>
      <pubDate>Wed, 3 Apr 2024 22:26:44 +0900</pubDate>
    </item>
    <item>
      <title>네트워크, HTTP, Web Scraping, robots.txt, DOM</title>
      <link>https://ssdragon.tistory.com/176</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;네트워크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크란 컴퓨터끼리 연결해서 데이터를 교환하는 시스템&lt;/li&gt;
&lt;li&gt;네트워크를 묶어 근거리 지역 네트워크(Local Area Network, LAN) 탄생&lt;/li&gt;
&lt;li&gt;범지구적으로 연결된 네트워크인 Inter Network - 인터넷(Internet) 탄생
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터넷 : 여러 컴퓨터끼리 네트워크를 연결한 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인터넷에서 정보를 교환할 수 있는 환경 WWW(World Wide Web, Web) 탄생&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;Web 상에서 정보를 주고받는 방법?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRMRex/btsGiA71Eef/4S4TCDGKFCarwcwfzc6en0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRMRex/btsGiA71Eef/4S4TCDGKFCarwcwfzc6en0/img.png&quot; data-alt=&quot;클라이언트-서버 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRMRex/btsGiA71Eef/4S4TCDGKFCarwcwfzc6en0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRMRex%2FbtsGiA71Eef%2F4S4TCDGKFCarwcwfzc6en0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;221&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;클라이언트-서버 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트(브라우저)와 서버가 HTTP를 통해 서로 통신을 함&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;클라이언트(Client) : 정보를 요청하는 곳&lt;br /&gt;서버(Server) : 정보를 제공하는 곳&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP는 웹에서 클라이언트와 서버 사이에 필요한 정보는 헤더로 주고받고, HTML 또는 Image 같은 실제 데이터는 페이로드 부분에 받을 수 있도록 설계됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ygsTp/btsGhGumfPH/Aeb7w7hf4y5l4wMYceXd7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ygsTp/btsGhGumfPH/Aeb7w7hf4y5l4wMYceXd7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ygsTp/btsGhGumfPH/Aeb7w7hf4y5l4wMYceXd7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FygsTp%2FbtsGhGumfPH%2FAeb7w7hf4y5l4wMYceXd7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;151&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 브라우저는 HTML 요청을 보내고, HTTP 응답에 담긴 HTML 문서를 화면에 그려주는 역할 담당&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;HTTP (HyperText Transfer Protocol)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  HTTP의 자세한 내용은 &lt;a href=&quot;https://ssdragon.tistory.com/133&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 글&lt;/a&gt;을 참고하자&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y6c7Q/btsGidSLxVg/oIWDqiC3cGvdAKMFO2KXZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y6c7Q/btsGidSLxVg/oIWDqiC3cGvdAKMFO2KXZk/img.png&quot; data-alt=&quot;HTTP 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y6c7Q/btsGidSLxVg/oIWDqiC3cGvdAKMFO2KXZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy6c7Q%2FbtsGidSLxVg%2FoIWDqiC3cGvdAKMFO2KXZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;248&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HTTP 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 상에서 정보를 주고 받을 수 있는 프로토콜&lt;/li&gt;
&lt;li&gt;웹은 HTTP 프로토콜을 통해 전달되므로 HTTP 성능 개선하면 웹 성능도 향상&lt;/li&gt;
&lt;li&gt;기본적으로 HTTP는 애플리케이션 계층으로써 웹 서비스 통신에 사용&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;HTTP Request(요청)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 서버로 보내는 요청&lt;/li&gt;
&lt;li&gt;HTTP Method : GET, POST, PUT, DELETE 등 주로 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;form&amp;gt; 에는 GET, POST만 사용 가능&lt;/li&gt;
&lt;li&gt;RESTful 서버의 특징은 위 메서드를 다양하게 활용하여 리소스를 조작하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP Body : 데이터 (string, jsonstring, binaryfile 등)&lt;/li&gt;
&lt;li&gt;HTTP Header : 보안, 브라우저 정보 등&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;HTTP Response(응답)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Status Code
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;200 : 성공&lt;/li&gt;
&lt;li&gt;404 : 리소스 찾을 수 없음&lt;/li&gt;
&lt;li&gt;400 : 실패&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP Body : 데이터 (주로 HTML 또는 JSON, File)&lt;/li&gt;
&lt;li&gt;HTTP Header : Content-Type, Content-Encoding 등&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;HTTP GET&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FJcnP/btsGixQZpBN/QtUydcpQhR1cuKqXTCVzYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FJcnP/btsGixQZpBN/QtUydcpQhR1cuKqXTCVzYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FJcnP/btsGixQZpBN/QtUydcpQhR1cuKqXTCVzYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFJcnP%2FbtsGixQZpBN%2FQtUydcpQhR1cuKqXTCVzYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;435&quot; height=&quot;128&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주로 리소스 조회로 사용&lt;/li&gt;
&lt;li&gt;서버에 전달하고 싶은 데이터는 query(쿼리 파라미터, 쿼리 스트링)를 통해 전달&lt;/li&gt;
&lt;li&gt;메시지 바디를 사용해서 데이터 전달할 수 있지만, 지원하지 않는 곳이 많아서 권장 X&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;HTTP POST&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/17PjV/btsGjlbxULS/l0m0vWcaiqSfVlUzp9bOS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/17PjV/btsGjlbxULS/l0m0vWcaiqSfVlUzp9bOS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/17PjV/btsGjlbxULS/l0m0vWcaiqSfVlUzp9bOS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F17PjV%2FbtsGjlbxULS%2Fl0m0vWcaiqSfVlUzp9bOS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;359&quot; height=&quot;176&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주로 요청 데이터 처리지만, 범용적으로 쓰임
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PUT, PATCH 등과 같은 메서드를 사용하지 않고 오직 GET, POST만 사용하는 경우도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메시지 바디를 통해 서버로 요청 데이터 전달&lt;/li&gt;
&lt;li&gt;주로 전달된 데이터로 신규 리소스 등록, 프로세스 처리에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Web Scraping 기초&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 스크래핑 : 특정 목적으로 특정 웹 페이지에서 &lt;b&gt;데이터를 추출하는 것&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. 날씨 데이터 가져오기, 주식 데이터 가져오기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;웹 크롤링 : URL을 타고 다니며 반복적으로 데이터를 가져오는 과정 (데이터 색인)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. 검색 엔진의 웹 크롤러&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;고려해야 할 점&lt;br /&gt;1. 웹 스크래핑/크롤링을 통해 어떤 목적을 달성하고자 하는가?&lt;br /&gt;2. 나의 웹 스크래핑/크롤링이 서버에 영향을 미치지 않는가?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;robots.txt 란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 사이트 및 웹 페이지를 수집하는 로봇들의 무단 접근을 방지하기 위해 만들어진 로봇 배제 표준(robots exclusion standard)이자 국제 권고안&lt;/li&gt;
&lt;li&gt;접근 제어 : 웹 사이트에 대한 크롤러의 접근 제한 또는 허용 및 금지 가능&lt;/li&gt;
&lt;li&gt;크롤러 지침 제공 : 특정 경로에 대한 크롤링을 어떻게 처리해야하는지 지시 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;브라우저는 사용자 에이전트(user agent) 정보를 받아서 사용자인지 로봇인지 판별함&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;User-Agnet : 규칙이 적용되는 대상 사용자 에이전트&lt;/li&gt;
&lt;li&gt;Disallow : 크롤링을 금지할 웹 페이지&lt;/li&gt;
&lt;li&gt;Allow : 크롤링을 허용할 웹 페이지&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;DOM (Document Object Model)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DCFWD/btsGjW99QjT/7mQGsxwljhpCkRPMkbYC4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DCFWD/btsGjW99QjT/7mQGsxwljhpCkRPMkbYC4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DCFWD/btsGjW99QjT/7mQGsxwljhpCkRPMkbYC4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDCFWD%2FbtsGjW99QjT%2F7mQGsxwljhpCkRPMkbYC4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;393&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 페이지의 구조화된 표현을 나타내는 브라우저의 프로그래밍 인터페이스&lt;/li&gt;
&lt;li&gt;HTML, XML 등 문서의 구조를 표현하며, 이를 사용하여 JS 같은 스크립트 언어를 사용하여 문서의 내용, 구조 및 스타일을 동적으로 조작 가능&lt;/li&gt;
&lt;li&gt;DOM은 계층적인 트리 구조로 표현됨&lt;/li&gt;
&lt;li&gt;웹 브라우저는 HTML 문서를 받아와서 이를 파싱하여 DOM 트리를 생성하고, 이 DOM 트리를 사용하여 렌더링 엔진을 통해 화면에 웹 페이지를 표시함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;브라우저는 왜 HTML을 DOM으로 변경할까?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원하는 요소를 동적으로 쉽게 변경하려고&lt;/li&gt;
&lt;li&gt;원하는 요소를 쉽게 찾을 수 있으므로&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/176</guid>
      <comments>https://ssdragon.tistory.com/176#entry176comment</comments>
      <pubDate>Tue, 2 Apr 2024 19:06:02 +0900</pubDate>
    </item>
    <item>
      <title>HTML</title>
      <link>https://ssdragon.tistory.com/175</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTML 기본 문법&lt;/h3&gt;
&lt;pre id=&quot;code_1712026964376&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;콘텐츠를 가지는 태그
&amp;lt;div&amp;gt; 콘텐츠 &amp;lt;/div&amp;gt;

콘텐츠를 가지지 않는 태그
&amp;lt;br /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성과 값&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ophcI/btsGhntWcGa/NAoOxXilXKDvGSDpoDq3W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ophcI/btsGhntWcGa/NAoOxXilXKDvGSDpoDq3W0/img.png&quot; data-alt=&quot;속성과 값&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ophcI/btsGhntWcGa/NAoOxXilXKDvGSDpoDq3W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FophcI%2FbtsGhntWcGa%2FNAoOxXilXKDvGSDpoDq3W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;242&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;속성과 값&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 기본 문서 형식&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/483ce/btsGgAG5Va5/i2ytNdnrMxlahDmQEse6VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/483ce/btsGgAG5Va5/i2ytNdnrMxlahDmQEse6VK/img.png&quot; data-alt=&quot;HTML 기본 문서 형식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/483ce/btsGgAG5Va5/i2ytNdnrMxlahDmQEse6VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F483ce%2FbtsGgAG5Va5%2Fi2ytNdnrMxlahDmQEse6VK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;279&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HTML 기본 문서 형식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모요소 자식요소&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UcbUP/btsGfh2ibkN/Qjm8eslYkcmXKVpfxdpM81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UcbUP/btsGfh2ibkN/Qjm8eslYkcmXKVpfxdpM81/img.png&quot; data-alt=&quot;들여쓰기 내어쓰기 주의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UcbUP/btsGfh2ibkN/Qjm8eslYkcmXKVpfxdpM81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUcbUP%2FbtsGfh2ibkN%2FQjm8eslYkcmXKVpfxdpM81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;292&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;들여쓰기 내어쓰기 주의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;들여쓰기와 내어쓰기를 제대로 안해도 컴퓨터는 해석 가능&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 태그 : &amp;lt;!--&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 태그 : --&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1712027152254&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 주석은 이렇게 사용 --&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;head&amp;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;lt;style&amp;gt;, &amp;lt;link&amp;gt;, &amp;lt;script&amp;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;lt;body&amp;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;block (블록 레벨 요소)&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;lt;div&amp;gt;, &amp;lt;article&amp;gt;, &amp;lt;section&amp;gt; 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inline (인라인 레벨 요소)&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;lt;span&amp;gt; , &amp;lt;a&amp;gt; , &amp;lt;strong&amp;gt; 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inline-block&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글자처럼 취급되나 block 태그의 성질을 가지는 요소&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;HTML5 부터 태그를 의미있게 사용하기 위해 &quot;Semantic&quot; 태그 사용&lt;/li&gt;
&lt;li&gt;div만 사용했을 때 원하는 내용을 찾기 힘들기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;1356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/osmt0/btsGfKXtohH/LL2cuO04Q3FPQHFNrWkvE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/osmt0/btsGfKXtohH/LL2cuO04Q3FPQHFNrWkvE1/img.png&quot; data-alt=&quot;레이아웃 태그 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/osmt0/btsGfKXtohH/LL2cuO04Q3FPQHFNrWkvE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fosmt0%2FbtsGfKXtohH%2FLL2cuO04Q3FPQHFNrWkvE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;574&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;1356&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;레이아웃 태그 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;div&amp;gt; : 가장 흔히 사용되는 레이아웃 태그, 단순 구역을 나누기 위한 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;header&amp;gt; : 블로그 글 제목, 작성일 등 주요 정보를 담는 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;footer&amp;gt; : 페이지의 바닥줄에 사용되며 저작권 정보, 연락처 등 부차적 정보를 담는 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;main&amp;gt; : 페이지의 가장 큰 부분, 사이트의 주요 콘텐츠를 담는 태그 (한페이지에 한 번만 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;section&amp;gt; : 컨텐츠 구역을 나누는 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;article&amp;gt; : 블로그 포스트, 뉴스 기사와 같은 독립적인 문서를 전달하는 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;aside&amp;gt; : 문서의 주요 내용에 간접적인 정보 전달을 하는 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;멀티미디어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;img&amp;gt; : 문서 내 이미지를 넣을 수 있는 태그&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;src : 이미지 경로 지정&lt;/li&gt;
&lt;li&gt;alt : 이미지 로딩 실패 시 대체 텍스트 표시&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;lt;figure&amp;gt; , &amp;lt;figcaption&amp;gt; : 하나의 독립적인 컨텐츠로 분리하고, 설명을 넣을 수 있는 태그&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;figcaption 태그를 사용해 컨텐츠의 설명 추가&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;lt;vidio&amp;gt; : 문서 내 영상 첨부 태그&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;src : 비디오 경로 지정&lt;/li&gt;
&lt;li&gt;poster : 비디오 로드 전에 포스터 표시&lt;/li&gt;
&lt;li&gt;&amp;lt;source&amp;gt; : 여러 타입의 비디오 제공 가능&lt;/li&gt;
&lt;li&gt;autoplay : 자동 재생 속성&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;lt;audio&amp;gt; : 문서에 소리 첨부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;ul&amp;gt;, &amp;lt;li&amp;gt; : 순서가 없는 리스트 (정렬x), 기본 불릿 형식으로 목록 표시&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;li&amp;gt; : 목록 구성 및 다양한 태그 포함 가능&lt;/li&gt;
&lt;li&gt;&amp;lt;ul&amp;gt; : 자식 요소로는 li 태그만 가능하고, 하위 리스트 생성 시 li 태그 안에 ul 태그 사용&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;lt;ol&amp;gt;, &amp;lt;li&amp;gt; : 순서가 있는 리스트 (정렬o), 기본 숫자(1,2,3) 형식으로 목록 표시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테이블&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;tr&amp;gt;&amp;nbsp; : 행 구분 - row&lt;br /&gt;&amp;lt;td&amp;gt; : 열 구분 - cell&lt;br /&gt;&amp;lt;th&amp;gt; : 열 제목 태그 (자동으로 bold + 가운데 정렬)&lt;br /&gt;&amp;lt;thead&amp;gt; : th 태그를 넣어서 그루핑, 여러번 사용 X&lt;br /&gt;&amp;lt;tbody&amp;gt; : 여러 열의 행을 넣어서 본문 요소를 그룹, 여러번 사용 X&lt;br /&gt;&amp;lt;tfoot&amp;gt; : 여러 열의 행을 넣어서 바닥글 요소 표현&lt;/p&gt;
&lt;pre id=&quot;code_1712027832258&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;table&amp;gt;
	&amp;lt;thead&amp;gt;
		&amp;lt;tr&amp;gt;
			&amp;lt;th&amp;gt;cell 셀의 제목1&amp;lt;/th&amp;gt;
			&amp;lt;th&amp;gt;cell 셀의 제목2&amp;lt;/th&amp;gt;
		&amp;lt;/tr&amp;gt;
	&amp;lt;/thead&amp;gt;
	&amp;lt;tbody&amp;gt;
		&amp;lt;tr&amp;gt;
			&amp;lt;td colspan=&quot;2&quot;&amp;gt;cell 셀1&amp;lt;/td&amp;gt; &amp;lt;!-- 셀 병합 --&amp;gt;
		&amp;lt;/tr&amp;gt;
		&amp;lt;tr&amp;gt;
			&amp;lt;td&amp;gt;cell 셀1&amp;lt;/td&amp;gt;
			&amp;lt;td&amp;gt;cell 셀2&amp;lt;/td&amp;gt;
		&amp;lt;/tr&amp;gt;
		&amp;lt;tr&amp;gt;
			&amp;lt;td&amp;gt;cell 셀1&amp;lt;/td&amp;gt;
			&amp;lt;td&amp;gt;cell 셀2&amp;lt;/td&amp;gt;
		&amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
			&amp;lt;td&amp;gt;cell 셀1&amp;lt;/td&amp;gt;
			&amp;lt;td&amp;gt;cell 셀2&amp;lt;/td&amp;gt;
		&amp;lt;/tr&amp;gt;
	&amp;lt;/tbody&amp;gt;
	&amp;lt;tfoot&amp;gt;
		&amp;lt;tr&amp;gt;
			&amp;lt;td&amp;gt;마지막&amp;lt;/td&amp;gt;
			&amp;lt;td&amp;gt;tfoot&amp;lt;/td&amp;gt;
		&amp;lt;/tr&amp;gt;
	&amp;lt;/tfoot&amp;gt;
&amp;lt;/table&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beQGeL/btsGjj40DCb/mgQjsHsWPRIqJiyvLyYvUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beQGeL/btsGjj40DCb/mgQjsHsWPRIqJiyvLyYvUk/img.png&quot; data-alt=&quot;테이블 코드 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beQGeL/btsGjj40DCb/mgQjsHsWPRIqJiyvLyYvUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeQGeL%2FbtsGjj40DCb%2FmgQjsHsWPRIqJiyvLyYvUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;417&quot; height=&quot;262&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 코드 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;iframe&amp;gt; : 현재 문서 안에 다른 HTML 페이지를 삽입하는 태그&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;Form 양식 태그&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정보를 제출하기 위한 태그&lt;/li&gt;
&lt;li&gt;입력 및 선택할 수 있는 input, selectbox, textarea 등 가질 수 있음&lt;/li&gt;
&lt;li&gt;action 속성으로 정보 제출되었을 때 페이지를 이동 가능&lt;/li&gt;
&lt;li&gt;method 속성으로 정보 제출되었을 때 처리방식 결정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1712030488100&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;body&amp;gt;
	&amp;lt;form action=&quot;chapter05-form-result.html&quot; method=&quot;post&quot;&amp;gt;
		&amp;lt;input name=&quot;id&quot; type=&quot;text&quot;&amp;gt;
		&amp;lt;input name=&quot;password&quot; type=&quot;password&quot;&amp;gt;
		&amp;lt;select name=&quot;opt&quot;&amp;gt;
			&amp;lt;option&amp;gt;옵션 1&amp;lt;/option&amp;gt;
			&amp;lt;option&amp;gt;옵션 2&amp;lt;/option&amp;gt;
			&amp;lt;option&amp;gt;옵션 3&amp;lt;/option&amp;gt;
			&amp;lt;option&amp;gt;옵션 4&amp;lt;/option&amp;gt;
			&amp;lt;option&amp;gt;옵션 5&amp;lt;/option&amp;gt;
		&amp;lt;/select&amp;gt;
		&amp;lt;button type=&quot;submit&quot;&amp;gt;전송!&amp;lt;/button&amp;gt; &amp;lt;!-- 기본 타입: submit--&amp;gt;
	&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;label&amp;gt; : input, selectbox 등 설명을 작성하는 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;input&amp;gt; : 사용자에게 데이터를 입력받을 수 있는 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;select&amp;gt; : 옵션 메뉴를 제공하는 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;textarea&amp;gt; : 여러 줄을 입력할 수 있는 대화형 태그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;button&amp;gt; : 클릭 가능한 버튼을 태그로 form 태그 내에 어디서든 사용 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/175</guid>
      <comments>https://ssdragon.tistory.com/175#entry175comment</comments>
      <pubDate>Tue, 2 Apr 2024 13:02:57 +0900</pubDate>
    </item>
    <item>
      <title>힙, 동적계획법, DFS, BFS, PEP8 스타일, Tim Sort</title>
      <link>https://ssdragon.tistory.com/174</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 엔지니어링 데브코스 Day 5&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- 자료구조 및 알고리즘 풀기 5&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;  힙(Heap) 대표 문제 : 더 맵게&lt;/h2&gt;
&lt;pre id=&quot;code_1711940992548&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq

def solution(scoville, K):
    answer = 0
    heapq.heapify(scoville) # 리스트를 힙으로 변환
    while scoville[0] &amp;lt; K:
        if len(scoville) &amp;lt; 2:
            return -1
        min1 = heapq.heappop(scoville) # 가장 작은 원소
        min2 = heapq.heappop(scoville) # 두번째 작은 원소
        new_scoville = min1 + (min2 * 2)
        heapq.heappush(scoville, new_scoville)
        answer += 1
    return answer&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;h2 data-ke-size=&quot;size26&quot;&gt;  동적계획법 (DP, Dynamic Programming)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w2Ku7/btsGdDR4JGP/RxQtLw0MRJS9HFi7CsFnu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w2Ku7/btsGdDR4JGP/RxQtLw0MRJS9HFi7CsFnu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w2Ku7/btsGdDR4JGP/RxQtLw0MRJS9HFi7CsFnu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw2Ku7%2FbtsGdDR4JGP%2FRxQtLw0MRJS9HFi7CsFnu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;555&quot; height=&quot;280&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 최적화 문제를 재귀적인 방식으로 보다 작은 부분 문제로 나누어 부분 문제를 풀어, 이 해를 조합하여 전체 문제의 해답에 이르는 방식&lt;/li&gt;
&lt;li&gt;알고리즘의 진행에 따라 탐색해야 할 범위를 동적으로 결정함으로써 탐색 범위를 한정할 수 있음&lt;/li&gt;
&lt;li&gt;동적 계획법도 그리디 알고리즘처럼 최적 부분 구조 조건(Optimal Substructure)를 지닌 중복된 하위 문제들(Overlapping Subproblems)을 분할 정복으로 풀이하는 문제해결 패러다임&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;  대표 문제 : N으로 표현&lt;/h3&gt;
&lt;pre id=&quot;code_1711941099097&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(N, number):
    s = [set() for _ in range(8)]
    
    for i, x in enumerate(s, start=1):
        x.add(int(str(N) * i))
        
    for i in range(len(s)):
        for j in range(i):
            for op1 in s[j]:
                for op2 in s[i-j-1]:
                    s[i].add(op1 + op2)
                    s[i].add(op1 - op2)
                    s[i].add(op1 * op2)
                    if op2 != 0:
                        s[i].add(op1 // op2)
        if number in s[i]:
            answer = i + 1
            break
    else:
        answer = -1
            
    return answer&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;h2 data-ke-size=&quot;size26&quot;&gt;  PEP8 스타일 가이드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이썬 개선 제안서, 파이썬 코드를 어떻게 구상할 지 알려주는 스타일 가이드&lt;/li&gt;
&lt;li&gt;다른 사람과 원활하게 협업하려면 공통된 스타일 공유 필요&lt;/li&gt;
&lt;li&gt;일관성 있는 스타일은 나중에 수정하기 쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;whitespace&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 줄의 문자 길이가 79자 이하여야 함&lt;/li&gt;
&lt;li&gt;함수와 클래스는 빈 줄 두개로 구분함&lt;/li&gt;
&lt;li&gt;클래스에서 메서드는 빈 줄 하나로 구분함&lt;/li&gt;
&lt;li&gt;변수 할당 앞 뒤에 스페이스를 하나만 사용&lt;/li&gt;
&lt;li&gt;리스트 인덱스, 함수 호출, 키워드 인수 할당에는 스페이스를 사용 X&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;naming&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수, 변수, 속성 : lowercase_underscore , 소문자로만 이루어지고 단어 사이 구분은 _ 으로 함&lt;/li&gt;
&lt;li&gt;보호(protected) 인스턴스 속성 : _leading_underscore , 언더바로 시작하고 구분은 _ 으로 함&lt;/li&gt;
&lt;li&gt;비공개(private) 인스턴스 속성 : __double_leading_underscore , 더블 언더바로 시작하고 구분은 _ 로 함&lt;/li&gt;
&lt;li&gt;클래스와 예외 : CapitalizeWord , 단어의 첫글자들을 대문자로 표시 (Carmel Case)&lt;/li&gt;
&lt;li&gt;모듈 수준 상수 : ALL_CAPS , 전부 대문자&lt;/li&gt;
&lt;li&gt;클래스의 인스턴스 메서드에서는 첫번째 파라미터 (해당 객체 참조)의 이름을 self로 지정&lt;/li&gt;
&lt;li&gt;클래스 메서드에서는 첫 번째 파라미터 (해당 클래스 참조)의 이름을 cls 로 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;표현식과 문장&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;if no a is b 보다는 if a is not b 를 사용&lt;/li&gt;
&lt;li&gt;if not somelist 처럼 빈 값은 암시적으로 False가 된다고 가정&lt;/li&gt;
&lt;li&gt;if somelist 처럼 값이 있는 리스트는 암시적으로 True가 된다고 가정&lt;/li&gt;
&lt;li&gt;한 줄로 된 if문, for, while loop, except 복합문을 쓰지 않음&lt;/li&gt;
&lt;li&gt;항상 파일의 맨 위에 import 문을 놓음&lt;/li&gt;
&lt;li&gt;모듈 임포트시에는 항상 모듈의 절대 이름을 사용 import foo 대신 from bar import foo&lt;/li&gt;
&lt;li&gt;상대적인 임포트를 해야 한다면 명시적인 구문을 서서 from . import foo 라고 함&lt;/li&gt;
&lt;li&gt;import 순서 : 표준 라이브러리 모듈 &amp;rarr; 서브파티 모듈 &amp;rarr; 자신이 만든 모듈 / 각각의 하위 섹션에서는 알파벳 순서&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Tim Sort&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tim Sort의 갤로핑 모드(Galloping Mode) 덕분에 배상 비용 최소화 문제에서 sort를 써도 통과 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFcJlV/btsGhnl2QNN/1JKodZ4Uv3TwuhcFQ7T8Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFcJlV/btsGhnl2QNN/1JKodZ4Uv3TwuhcFQ7T8Fk/img.png&quot; data-alt=&quot;비교 정렬 알고리즘 시간복잡도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFcJlV/btsGhnl2QNN/1JKodZ4Uv3TwuhcFQ7T8Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFcJlV%2FbtsGhnl2QNN%2F1JKodZ4Uv3TwuhcFQ7T8Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;193&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;비교 정렬 알고리즘 시간복잡도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평균적으로 느린 정렬 알고리즘 : Bubble Sort, Insertion Sort&lt;/li&gt;
&lt;li&gt;평균적으로 빠른 정렬 알고리즘 : Heap Sort, Merge Sort, Quick Sort&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;시간 복잡도가 O(n log n)이라는 말은 실제 동작 시간은 C * n log n + a 라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C라는 상수가 곱해져 있어서 이 값에 큰 영향을 끼치는 요소로 '알고리즘이 참조 지역성(Locality of Reference) 원리를 얼마나 잘 만족하는가?' 가 있음.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;참조 지역성 원리란, CPU가 미래에 원하는 데이터를 예측하여 속도가 빠른 캐시 메모리에 담아 놓는데 이 때의 예측률을 높이기 위해 사용하는 원리&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균적으로 빠른 알고리즘도 상수 C의 값이 너무 커지지 않게 동작하며, 추가 메모리도 많이 사용하지 않고, 최악의 경우 O(n log n)으로 동작하는 정렬 알고리즘이 필요했다.&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;Tim Sort 등장&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2002년 소프트웨어 엔지니어 Tim Peters에 의해 등장&lt;/li&gt;
&lt;li&gt;수많은 종류의 실세계 데이터에 잘 수행하도록 설계된 하이브리드형 안정 정렬 알고리즘&lt;/li&gt;
&lt;li&gt;Insertion Sort + Merge Sort 결합하여 만든 정렬
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Binary Insertion Sort 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실생활 데이터 특성을 고려하여 최선 O(n), 평균 O(n log n), 최악 O(n log n)&lt;/li&gt;
&lt;li&gt;파이썬 2.3 이후 버전, Java SE 7, Android, Google chrome V8 등 많은 언어에서 표준 정렬 알고리즘으로 채택되어 사용&lt;/li&gt;
&lt;li&gt;전체를 작은 덩어리로 잘라 각각의 덩어리를 Insertion Sort로 정렬한 뒤 병합하면 좀 더 빠르지 않을까 하는 것이 기본적인 아이디어&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : &lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/0315536&quot;&gt;https://d2.naver.com/helloworld/0315536&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;  DFS (깊이우선탐색, Depth-First Search)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 정점에서 인접한 모든 (아직 방문하지 않은) 정점을 방문하되, 각 인접 정점을 기준으로 깊이 우선 탐색을 끝낸 후 다음 정점으로 진행&lt;/li&gt;
&lt;li&gt;스택(Stack)을 이용하여 어느 정점에서 DFS를 하고 있는지를 기억하고 되돌아감&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;  BFS (너비우선탐색, Breadth-First Search)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 정점에서 인접한 모든 (아직 방문하지 않은) 정점을 방문하고, 방문한 각 인접 정점을 기준으로 (방문한 순서에 따라) 또 다시 너비 우선 탐색을 행함&lt;/li&gt;
&lt;li&gt;큐(Queue)를 이용하여 어느 정점에서 BFS를 해야 하는지를 기록하고 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;대표 문제 : 여행 경로&lt;/h4&gt;
&lt;pre id=&quot;code_1711941502912&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import defaultdict

def solution(tickets):
    routes = defaultdict(list)
    for start, end in tickets:
        routes[start].append(end)
    for r in routes:
        routes[r].sort(reverse=True)
    stack = [&quot;ICN&quot;]
    path = []
    while stack:
        top = stack[-1]
        if top not in routes or not routes[top]:
            path.append(stack.pop())
        else:
            stack.append(routes[top].pop())
    return path[::-1]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/174</guid>
      <comments>https://ssdragon.tistory.com/174#entry174comment</comments>
      <pubDate>Mon, 1 Apr 2024 12:18:26 +0900</pubDate>
    </item>
    <item>
      <title>해시, 그리디</title>
      <link>https://ssdragon.tistory.com/173</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 엔지니어링 데브코스 Day 4&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- 자료구조 및 알고리즘 풀기 4&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;  해시 (Hash)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH3UpM/btsF9DD8QhU/TrysBwkpBt0T0JuHW7AzKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH3UpM/btsF9DD8QhU/TrysBwkpBt0T0JuHW7AzKK/img.png&quot; data-alt=&quot;해시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH3UpM/btsF9DD8QhU/TrysBwkpBt0T0JuHW7AzKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH3UpM%2FbtsF9DD8QhU%2FTrysBwkpBt0T0JuHW7AzKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;362&quot; height=&quot;283&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해시 함수(hash function)로도 불리며, 임의의 길이를 갖는 임의의 데이터를 고정된 길이의 데이터로 매핑하는 단방향 함수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아무리 큰 숫자를 넣어도 고정된 크기의 숫자가 나옴&lt;/li&gt;
&lt;li&gt;단방향 함수이기에 출력된 값을 통해 입력값을 알 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;키(key)를 해시 함수로 돌려 나온 해시 값에 따라 해시 테이블(hash table)에 저장&lt;/li&gt;
&lt;li&gt;입력된 값이 조금만 변해도 결과가 크게 달라지는 특성 (눈사태 효과, Avalanche Effect)&lt;/li&gt;
&lt;li&gt;서로 다른 입력값에도 동일한 값이 출력되는 경우가 있음 (해시 충돌, Hash Collision)
&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;이 경우, Separate Chaining 또는 Open Addressing 등의 방법을 사용하여 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;a href=&quot;https://ssdragon.tistory.com/132&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ssdragon.tistory.com/132&lt;/a&gt; 에서 조금 더 자세히 알 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✏️ 파이썬의 대표적인 해시 자료구조인 딕셔너리(Dictionary)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;딕셔너리는 해시 테이블을 기반으로 구현된 자료구조&lt;/li&gt;
&lt;li&gt;딕셔너리는 키(key)와 값(value)의 쌍으로 이루어져 있으며, 각 키는 고유함&lt;/li&gt;
&lt;li&gt;딕셔너리는 해시 함수를 사용하여 각 키를 해시 값으로 변환하고, 해시 값으로 딕셔너리 내부의 해시 테이블에서 해당 키와 연관된 위치를 결정하는 데 사용함&lt;/li&gt;
&lt;li&gt;빠른 검색 및 삽입 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✏️ 해시의 대표 문제 : &lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;완주하지 못한 선수&lt;/a&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1711615977657&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(participant, completion):
    d = {}
    for x in participant:
        d[x] = d.get(x, 0) + 1
    for x in completion:
        d[x] -= 1
    dnf = [k for k, v in d.items() if v &amp;gt; 0]
    return dnf[0]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;  그리디 (Greedy)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;탐욕 알고리즘 or 욕심쟁이 알고리즘으로 불림&lt;/li&gt;
&lt;li&gt;말 그대로 욕심쟁이라서 &lt;b&gt;선택의 순간마다 최적의 선택을 하여 전체적으로 최적이길 바라는 방법&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;선택의 순간마다 최적의 선택을 했다하여 그것이 최적의 해답이라는 보장 X&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  탐욕 알고리즘을 적용하기 위한 2가지 조건&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;탐욕스러운 선택 조건 (Greedy Choice Property) : 앞의 선택이 이후의 선택에 영향을 주지 않음&lt;/li&gt;
&lt;li&gt;최적 부분 구조 조건 (Optimal Substructure) : 문제에 대한 최적해가 부분 문제에 대해서도 최적해라는 것&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;a href=&quot;https://ssdragon.tistory.com/114&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ssdragon.tistory.com/114&lt;/a&gt; 에서 조금 더 자세히 알 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✏️ 그리디의 대표적인 문제 : &lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42862&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;체육복&lt;/a&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1711616484429&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(n, lost, reserve):
    s = set(lost) &amp;amp; set(reserve)
    l = set(lost) - s # 체육복 빌려야하는 집합
    r = set(reserve) - s # 체육복 빌려줄 수 있는 집합
    
    for x in sorted(r):
        if x - 1 in l:
            l.remove(x-1)
        elif x + 1 in l:
            l.remove(x+1)
    
    return n - len(l)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/173</guid>
      <comments>https://ssdragon.tistory.com/173#entry173comment</comments>
      <pubDate>Thu, 28 Mar 2024 18:01:28 +0900</pubDate>
    </item>
    <item>
      <title>큐, 트리, 힙</title>
      <link>https://ssdragon.tistory.com/172</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 엔지니어링 데브코스 Day 3&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- 자료구조 및 알고리즘 풀기 3&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;  큐 (Queue)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXFE57/btsF7KXugAx/p723KW9URapoBwvhybYWd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXFE57/btsF7KXugAx/p723KW9URapoBwvhybYWd0/img.png&quot; data-alt=&quot;큐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXFE57/btsF7KXugAx/p723KW9URapoBwvhybYWd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXFE57%2FbtsF7KXugAx%2Fp723KW9URapoBwvhybYWd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;287&quot; height=&quot;156&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;큐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자료를 보관할 수 있는 선형 구조&lt;/li&gt;
&lt;li&gt;자료를 넣을 때에는 한 쪽 끝에서 넣고, 꺼낼 때는 반대쪽에서 꺼냄
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;넣는 것은 인큐(enqueue) 연산&lt;/li&gt;
&lt;li&gt;빼는 것은 디큐(dequeue) 연산&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;선입선출 (FIFO, First-In-First-Out) 특징을 가지는 선형 자료구조&lt;/li&gt;
&lt;li&gt;e.g. 프린트에 인쇄할 문서 보낼 때, 대기열 등&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;  환형 큐 (Circular Queue)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbBPbp/btsF8aaASlK/3L4Hu9KbGwqLzlBtApzB1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbBPbp/btsF8aaASlK/3L4Hu9KbGwqLzlBtApzB1k/img.png&quot; data-alt=&quot;환형 큐, 원형 큐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbBPbp/btsF8aaASlK/3L4Hu9KbGwqLzlBtApzB1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbBPbp%2FbtsF8aaASlK%2F3L4Hu9KbGwqLzlBtApzB1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;273&quot; height=&quot;285&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;환형 큐, 원형 큐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원형 큐라고도 불림&lt;/li&gt;
&lt;li&gt;큐의 한 종류로 원형 구조를 갖추어 FIFO 방식을 사용&lt;/li&gt;
&lt;li&gt;정해진 개수의 저장 공간을 돌려가며 사용&lt;/li&gt;
&lt;li&gt;원형으로 데이터를 저장하며 앞과 뒤가 연결되어 순환&lt;/li&gt;
&lt;li&gt;Front는 큐의 맨 앞 요소를 가리키고, Rear는 큐의 맨 뒤 요소를 가리킴
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 추가하면 Rear 이동&lt;/li&gt;
&lt;li&gt;데이터 제거하면 Front 이동&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  우선순위 큐 (Priority Queue)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsay8O/btsF62jKMO8/QpgyKPK5FhiJ7MQduheyMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsay8O/btsF62jKMO8/QpgyKPK5FhiJ7MQduheyMK/img.png&quot; data-alt=&quot;우선순위 큐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsay8O/btsF62jKMO8/QpgyKPK5FhiJ7MQduheyMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbsay8O%2FbtsF62jKMO8%2FQpgyKPK5FhiJ7MQduheyMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;297&quot; height=&quot;263&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;우선순위 큐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저장되는 데이터는 우선순위를 가지며, 가장 높은 우선순위를 가진 데이터가 항상 먼저 꺼내지게 됨&lt;/li&gt;
&lt;li&gt;일반 큐와는 달리 들어간 순서가 아닌, 데이터의 우선순위에 따라 처리됨&lt;/li&gt;
&lt;li&gt;Enqueue 할 때 우선순위 순서를 유지하는 방법과 Dequeue 할 때 우선순위 높은 것을 선택하는 방법이 있지만 전자가 효율적&lt;/li&gt;
&lt;li&gt;e.g. CPU 작업 스케줄링, 네트워크 라우팅 등&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;  트리 (Tree)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GAPGL/btsF93aetM5/e2BlgTkxS0QKGkG1kNKVb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GAPGL/btsF93aetM5/e2BlgTkxS0QKGkG1kNKVb1/img.png&quot; data-alt=&quot;트리의 종류&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GAPGL/btsF93aetM5/e2BlgTkxS0QKGkG1kNKVb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGAPGL%2FbtsF93aetM5%2Fe2BlgTkxS0QKGkG1kNKVb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;213&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;트리의 종류&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxVjle/btsF8ntYQBE/yVQMroUslw755QKGxwl0GK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxVjle/btsF8ntYQBE/yVQMroUslw755QKGxwl0GK/img.png&quot; data-alt=&quot;트리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxVjle/btsF8ntYQBE/yVQMroUslw755QKGxwl0GK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxVjle%2FbtsF8ntYQBE%2FyVQMroUslw755QKGxwl0GK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;295&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;트리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계층적인 구조를 갖는 비선형 자료구조&lt;/li&gt;
&lt;li&gt;노드들이 간선으로 연결되어 있음&lt;/li&gt;
&lt;li&gt;하나의 루트 노드에서 시작하여 여러 개의 자식 노드를 갖는 부모-자식 관계로 구성&lt;/li&gt;
&lt;li&gt;각 노드는 자식 노드를 가질 수 있고, 각각의 자식 노드는 또 다시 자식 노드들을 가질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  이진 트리 (Binary Tree)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NnpXq/btsF7Z7VQd2/TAzWnLKgcfuq73lbkwEMkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NnpXq/btsF7Z7VQd2/TAzWnLKgcfuq73lbkwEMkk/img.png&quot; data-alt=&quot;이진트리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NnpXq/btsF7Z7VQd2/TAzWnLKgcfuq73lbkwEMkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNnpXq%2FbtsF7Z7VQd2%2FTAzWnLKgcfuq73lbkwEMkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;355&quot; height=&quot;319&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이진트리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트리의 종류 중 하나로 모든 노드의 차수가 2 이하인 트리&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;  포화 이진 트리 (Full Binary Tree)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;308&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ap9lC/btsF92Coo6q/LXkKeOrSFVpOJHcC3dmxAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ap9lC/btsF92Coo6q/LXkKeOrSFVpOJHcC3dmxAK/img.png&quot; data-alt=&quot;포화 이진 트리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ap9lC/btsF92Coo6q/LXkKeOrSFVpOJHcC3dmxAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAp9lC%2FbtsF92Coo6q%2FLXkKeOrSFVpOJHcC3dmxAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;224&quot; height=&quot;253&quot; data-origin-width=&quot;308&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;포화 이진 트리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 레벨에서 노드들이 모두 채워져 있는 이진 트리&lt;/li&gt;
&lt;li&gt;모든 내부 노드가 두 개의 자식을 가진 이진 트리&lt;/li&gt;
&lt;li&gt;내부 노드가 0개 또는 2개의 자식을 가짐&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;  완전 이진 트리 (Complete Binary Tree)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8cvNX/btsF9ceFBBz/lQQViDi6lKfNhLb6LGPvaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8cvNX/btsF9ceFBBz/lQQViDi6lKfNhLb6LGPvaK/img.png&quot; data-alt=&quot;완전 이진 트리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8cvNX/btsF9ceFBBz/lQQViDi6lKfNhLb6LGPvaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8cvNX%2FbtsF9ceFBBz%2FlQQViDi6lKfNhLb6LGPvaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;355&quot; height=&quot;155&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;완전 이진 트리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 레벨에서 노드가 왼쪽에서 오른쪽으로 채워진 이진 트리&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;  넓이 우선 순회 (Breadth First Traversal)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SNkTR/btsF9MzJ1vh/MX9aFFShODz0dXAtSqDsEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SNkTR/btsF9MzJ1vh/MX9aFFShODz0dXAtSqDsEk/img.png&quot; data-alt=&quot;넓이 우선 순회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SNkTR/btsF9MzJ1vh/MX9aFFShODz0dXAtSqDsEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSNkTR%2FbtsF9MzJ1vh%2FMX9aFFShODz0dXAtSqDsEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;254&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;넓이 우선 순회&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수준(level)이 낮은 노드를 우선 방문&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;  이진 탐색 트리 (Binary Search Tree)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cN5FmE/btsF9KPtUgA/yC7OxC3WmpNpuYk8SnOFtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cN5FmE/btsF9KPtUgA/yC7OxC3WmpNpuYk8SnOFtK/img.png&quot; data-alt=&quot;이진 탐색 트리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cN5FmE/btsF9KPtUgA/yC7OxC3WmpNpuYk8SnOFtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcN5FmE%2FbtsF9KPtUgA%2FyC7OxC3WmpNpuYk8SnOFtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;354&quot; height=&quot;284&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이진 탐색 트리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 노드의 왼쪽 하위 트리에는 해당 노드 값보다 작은 값들만, 오른쪽 하위 트리에는 해당 노드 값보다 큰 값들만 위치하는 이진 트리&lt;/li&gt;
&lt;li&gt;데이터를 빠르게 검색할 수 있다는 장점&lt;/li&gt;
&lt;li&gt;트리가 균형을 이루지 않으면 검색 성능이 저하될 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 해결하기 위해 AVL 트리, 레드-블랙 트리 등의 균형 이진 탐색 트리가 이용됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  힙 (Heap)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tJ5LR/btsF7w6dY9V/TzL0Xa8K3jD3DP0NcDd1p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tJ5LR/btsF7w6dY9V/TzL0Xa8K3jD3DP0NcDd1p0/img.png&quot; data-alt=&quot;최소힙, 최대힙&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tJ5LR/btsF7w6dY9V/TzL0Xa8K3jD3DP0NcDd1p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtJ5LR%2FbtsF7w6dY9V%2FTzL0Xa8K3jD3DP0NcDd1p0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;384&quot; height=&quot;170&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최소힙, 최대힙&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이진 트리의 한 종류 (이진 힙 - binary heap)&lt;/li&gt;
&lt;li&gt;부모 노드의 값이 항상 자식 노드의 값보다 크거나(최대힙), 작거나(최소힙) 같은 규칙을 만족하는 이진 트리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;루트 노드는 항상 최댓값 또는 최솟값을 가짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;완전 이진 트리여야 함&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/172</guid>
      <comments>https://ssdragon.tistory.com/172#entry172comment</comments>
      <pubDate>Wed, 27 Mar 2024 17:58:08 +0900</pubDate>
    </item>
    <item>
      <title>연결리스트, 스택</title>
      <link>https://ssdragon.tistory.com/171</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 엔지니어링 데브코스 Day 2&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- 자료구조 및 알고리즘 풀기 2&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;  연결 자료구조 방식 (Linked Data Structure)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순차 자료구조 방식에서 연산 시간 및 저장 공간에 대한 문제를 개선한 자료 표현 방식으로 연결 자료구조와 비순차 자료구조(Nonsequential Data Structure)가 있음&lt;/li&gt;
&lt;li&gt;순차 자료구조 방식처럼 원소의 논리적 순서와 물리적 순서가 일치할 필요 X
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연속한 메모리 물리주소에 의해 원소 순서를 표현 X&lt;/li&gt;
&lt;li&gt;각 원소에 저장되어 있는 다음 원소의 주소에 대한 참조에 의해서 연결되는 방식이기 때문&lt;/li&gt;
&lt;li&gt;물리적 순서를 맞추기 위한 오버헤드 발생 X&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;순차 자료구조는 하나의 고정 크기 메모리 공간이 필요하지만, 연결 자료구조는 여러 개의 작은 공간을 연결하여 전체를 표현하기에 크기 변경이 유연하고 효율적 메모리 사용 가능&lt;/li&gt;
&lt;li&gt;연결 자료구조 방식을 표현한 구조에는 연결 리스트가 있고, 연결하는 방식에 따라 단순 연결 리스트, 원형 연결 리스트, 이중 연결 리스트, 이중 원형 연결 리스트로 나눌 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  연결 리스트 (Linked List)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biqCeo/btsF4Wjp8mo/avn67DGB7K6P0hLqF7QDoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biqCeo/btsF4Wjp8mo/avn67DGB7K6P0hLqF7QDoK/img.png&quot; data-alt=&quot;연결 리스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biqCeo/btsF4Wjp8mo/avn67DGB7K6P0hLqF7QDoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiqCeo%2FbtsF4Wjp8mo%2Favn67DGB7K6P0hLqF7QDoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;447&quot; height=&quot;162&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;연결 리스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연결 자료구조 방식으로 데이터 요소가 노드로 구성되고, 각 노드는 데이터와 다음 노드를 가리키는 Link(링크)를 포함&lt;/li&gt;
&lt;li&gt;데이터 순서가 메모리에 연속적으로 저장 X&lt;/li&gt;
&lt;li&gt;데이터 삽입 및 삭제가 빠르지만 특정 인덱스 접근에는 선형 탐색 필요&lt;/li&gt;
&lt;li&gt;선형 배열은 데이터 접근이 빠르지만 삽입 및 삭제 시에는 데이터 이동이 필요하여 연산이 느림&lt;/li&gt;
&lt;li&gt;연결 리스트는 동적 크기 조정이 되어 메모리 관리 유연하며, 삽입 및 삭제 연산의 비용이 일정&lt;/li&gt;
&lt;li&gt;선형 배열은 크기가 고정되어 메모리 사용이 불필요하게 발생 가능하며, 삽입 및 삭제 연산에 따라 메모리 재할당이 필요할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  양방향 연결 리스트 (Doubly Linked List)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DxFTi/btsF63oki3i/BPKNVu1KYkzmL6pwpaFP9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DxFTi/btsF63oki3i/BPKNVu1KYkzmL6pwpaFP9k/img.png&quot; data-alt=&quot;양방향 연결 리스트 (이중 연결 리스트)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DxFTi/btsF63oki3i/BPKNVu1KYkzmL6pwpaFP9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDxFTi%2FbtsF63oki3i%2FBPKNVu1KYkzmL6pwpaFP9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;149&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;양방향 연결 리스트 (이중 연결 리스트)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연결 리스트는 앞으로만 연결되어 있었지만, 양방향 연결 리스트는 노드들이 앞/뒤로 연결&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;  스택 (Stack)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tXeVi/btsF7JJVGII/vhgFka7zaTSbNwrE0c7FdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tXeVi/btsF7JJVGII/vhgFka7zaTSbNwrE0c7FdK/img.png&quot; data-alt=&quot;스택&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tXeVi/btsF7JJVGII/vhgFka7zaTSbNwrE0c7FdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtXeVi%2FbtsF7JJVGII%2FvhgFka7zaTSbNwrE0c7FdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;387&quot; height=&quot;264&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스택&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스택이란 쌓아올린다는 의미로써, 접시를 쌓듯이 자료를 차곡차곡 쌓아 올린 형태의 구조&lt;/li&gt;
&lt;li&gt;스택은 top 이라고 정한 한 곳으로만 쌓을 수 있고, top으로만 접근하도록 제한하여 만든 자료구조
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;삽입/삭제는 전부 top을 통해서만 가능하기에 top이 가리키고 있는 스택의 마지막 자료만 삭제 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간순서에 따라 자료가 쌓이고, 삭제할 때는 가장 마지막에 삽입된 자료가 가장 먼저 삭제되는 후입선출(LIFO, Last-In-First-Out) 구조를 가짐&lt;/li&gt;
&lt;li&gt;삽입 연산 push, 삭제 연산 pop&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/171</guid>
      <comments>https://ssdragon.tistory.com/171#entry171comment</comments>
      <pubDate>Tue, 26 Mar 2024 17:14:57 +0900</pubDate>
    </item>
    <item>
      <title>선형배열, 정렬, 탐색, 재귀, 시간복잡도</title>
      <link>https://ssdragon.tistory.com/170</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 엔지니어링 데브코스 Day 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자료구조 및 알고리즘 풀기 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  선형 배열 (Linear Array)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열은 원소들을 순서대로 늘어놓은 것 : [1, 3, -9, 10, 4]&lt;/li&gt;
&lt;li&gt;배열의 각각의 원소에는 번호가 있는데 이를 index(인덱스)라 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;index 는 0부터 시작함&lt;/li&gt;
&lt;li&gt;마지막 인덱스부터 첫 번째 인덱스까지 접근하려면 -1 부터 -1씩 증가시키면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;파이썬에서는 서로 다른 종류의 데이터 또한 줄세울 수 있는 list(리스트) 라는 데이터형 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buMDnQ/btsF3EbqQds/RFgcRPR4yCaa2Ifeg3d6t1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buMDnQ/btsF3EbqQds/RFgcRPR4yCaa2Ifeg3d6t1/img.png&quot; data-alt=&quot;파이썬 리스트 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buMDnQ/btsF3EbqQds/RFgcRPR4yCaa2Ifeg3d6t1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuMDnQ%2FbtsF3EbqQds%2FRFgcRPR4yCaa2Ifeg3d6t1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;154&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파이썬 리스트 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  리스트에 활용할 수 있는 연산들&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 리스트 길이와 관계 없이 빠른 실행 결과를 보여주는 연산&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원소 덧붙이기 : `.append(추가할원소)`&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트 끝에 추가함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;끝에서 원소 하나 꺼내기 : `.pop()'&lt;/li&gt;
&lt;li&gt;a번째 원소 하나 꺼내기 : `.pop(a)`
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`pop()` 연산은 해당 원소를 꺼내고 리스트에서 삭제함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 리스트 길이에 비례하여 실행 시간이 걸리는 연산&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원소 삽입하기 : `.insert(a,b)`
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트의 a번째 위치에서 b를 삽입&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;원소 삭제하기 : `.del(L[x])` or `del L[x]`
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트 L의 x번째 요소값을 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;원소 탐색하기 : `.index(a)`
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트에 a 값이 있으면 a의 인덱스 리턴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  정렬(Sort)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복수의 원소로 주어진 데이터를 정해진 기준에 따라 새로 늘어놓는 작업&lt;/li&gt;
&lt;li&gt;파이썬 내장 함수 : `sorted(L)`
&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;원본 리스트 L은 변경 X&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리스트에 쓸 수 있는 메서드 : `.sort()`
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 리스트를 정렬&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정렬의 순서를 반대로 하려면 `reverse=True` 옵션 추가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`L2 = sorted(L, reverse=True)`&lt;/li&gt;
&lt;li&gt;`L.sort(reverse=True)`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;숫자(number)가 아닌 문자열을 정렬할 때는 사전순으로 정렬함.&lt;br /&gt;이 때, 문자열 길이가 더 크다고 더 큰 문자로 취급 X&lt;br /&gt;Python 문자열은 대문자가 소문자에 비해 무조건 우선.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;pre id=&quot;code_1711355025795&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;L = ['abcd', 'xyz', 'spam']
sorted(L, key=lambda x: len(x))
# ['xyz', 'abcd', 'spam']

L = ['spam', 'xyz', 'abcd']
sorted(L, key=lambda x: len(x))
# ['xyz', 'abcd', 'spam']


## 사전(dictionary)를 정렬하는 방법 중 이름순으로 하는 방법
L = [{'name' : 'John', 'score' : 83},
		 {'name' : 'Paul', 'score' : 92}]
# 레코드들을 이름순 정렬
L.sort(key=lambda x: x['name'])
# 레코드들을 점수가 높은순으로 정렬
L.sort(key=lambda x: x['score'], reverse=True)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`key` 매개변수는 정렬의 기준이 됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`lambda x: len(x)` 는 각 원소 x의 길이를 기준으로 정렬하도록 함&lt;/li&gt;
&lt;li&gt;이를 통해 리스트의 각 요소들이 길이에 따라 정렬됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;lambda(람다)는 파이썬에서 간단한 익명 함수를 생성하는 키워드. &lt;br /&gt;즉, 이름이 없는 함수를 생성할 때 사용됨. &lt;br /&gt;람다 함수의 일반적인 구조는 `lambda 매개변수: 표현식`이다.&lt;br /&gt;- lambda :&amp;nbsp;함수를 정의하는 키워드&lt;br /&gt;- 매개변수 : 함수의 입력 값&lt;br /&gt;- 표현식 : 함수의 결과 값&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다 함수 예시&lt;/p&gt;
&lt;pre id=&quot;code_1711355140748&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;addition = lambda x, y: x + y
print(addition(3, 5)) # 8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  탐색(Search)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복수의 원소로 이루어진 데이터에서 특정 원소를 찾아내는 작업&lt;/li&gt;
&lt;li&gt;기본적으로 선형 탐색(Linear Search)와 이진 탐색(Binary Search)가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선형 탐색&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순차 탐색(Sequential Search)라고도 함&lt;/li&gt;
&lt;li&gt;순차적으로 모든 요소 탐색하여 원하는 값 찾음&lt;/li&gt;
&lt;li&gt;배열의 길이에 비례하는 시간 걸림 O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1711355232857&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 선형 탐색 구현
def linear_search(L, x):
	for i in range(len(L)):
		if x == L[i]:
			return i
	return -1&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;이진 탐색&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;탐색하려는 배열이 정렬되어 있는 경우에만 적용 가능&lt;/li&gt;
&lt;li&gt;배열 가운데 원소와 찾으려 하는 값을 비교하고, 가운데 원소가 더 작다면 왼쪽의 값들(절반)은 없애고, 다시 오른쪽 배열들에서 반복함&lt;/li&gt;
&lt;li&gt;탐색할 때마다 절반씩 사라지므로 빠른 탐색 가능 O(log n)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1711355335766&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 이진 탐색 구현
def binary_search(L, x):
	start, end = 0, len(n) - 1
	while start &amp;lt;= end:
		mid = (start + end) // 2
		if L[mid] == x: return mid
		start, end = (start, mid - 1) if L[mid] &amp;gt; x else (mid + 1, end)
	
	return -1

# 예시
# 시작 상태: 전체 배열 [4, 8, 15, 16, 23, 42, 48, 50, 56, 71, 88]
# 찾고자 하는 값: 23

# 1. 중간 값: 42
#    중간 값이 더 크기에 오른쪽은 탐색 X
#    [4, 8, 15, 16, 23]

# 2. 중간 값: 15
#    중간 값이 더 작기에 왼쪽은 탐색 X
#    [16, 23]

# 3. 중간 값: 16
#    중간 값이 더 작기에 왼쪽 탐색 X
#    [23]

# 4. 중간 값: 23
#    찾고자 하는 값과 중간 값이 일치하므로 찾음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  재귀 알고리즘 (Recursive Algorithm)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 알고리즘을 반복적으로 적용함으로써 풀어내는 방법&lt;/li&gt;
&lt;li&gt;재귀함수는 하나의 함수에서 자신을 다시 호출하여 작업 수행하는 것&lt;/li&gt;
&lt;li&gt;종결 조건(trivial case) 명시해야하고, 스택오버플로우가 나지 않도록 주의&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;pre id=&quot;code_1711355467229&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 자연수의 합 구하기
def sum(n):
	if n &amp;lt;= 1:
		return n
	else:
		return n + sum(n - 1)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1711355479761&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 피보나치 구하기
mem = {}

def fibo(n) :
	if n in mem:
		return mem[n]
	if n &amp;lt;= 1:
		return n
	mem[n] = fibo(n-1) + fibo(n-2)
	return mem[n]&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;mem 이라는 빈 딕셔너리(dictionary)를 만들어서 한번이라도 구했던 값 저장
&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;mem[5] = 8 이 있으면 키가 5이고 값이 8&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 코드는 메모이제이션 기법을 구현한 방법으로, 메모이제이션은 이전에 계산한 값을 저장해 두고 필요할 때마다 사용하는 기법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복 계산을 피하고 효율적인 알고리즘을 만들 수 있도록 도와줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;재귀를 통해 문제를 풀다보면 반복적으로 똑같은 함수를 여러번 실행하여 비효율적인 모습을 보일 때가 있다. 이럴 때 쓰이는 것이 메모이제이션 기법이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것에 대해서는 아래 글에 작성했으니 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ssdragon.tistory.com/100&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ssdragon.tistory.com/100&lt;/a&gt;&lt;/p&gt;</description>
      <category>데브코스-데이터엔지니어링</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/170</guid>
      <comments>https://ssdragon.tistory.com/170#entry170comment</comments>
      <pubDate>Mon, 25 Mar 2024 17:33:53 +0900</pubDate>
    </item>
    <item>
      <title>FK(외래키)를 주식별자인 기본키(PK)로 꼭 넣어야할까? 아니면 비식별자로 넣어야할까?</title>
      <link>https://ssdragon.tistory.com/169</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우선 간단한 개념부터 살펴보자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  식별자(Identifier) 개념&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 엔터티(entity)에 구서된 여러 개의 속성 중에 엔터티를 대표할 수 있는 속성&lt;/li&gt;
&lt;li&gt;하나의 엔터티는 반드시 하나의 유일한 식별자가 존재&lt;/li&gt;
&lt;li&gt;논리 데이터 모델링 단계에서는 '식별자', 물리 데이터 모델링 단계에서는 '키(key)' 라고도 불림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  식별자 분류와 특징&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔터티 내에서 대표성을 가졌는가? 주식별자 or 보조식별자&lt;/li&gt;
&lt;li&gt;엔터티 내에서 스스로 생성 되었는가? 내부식별자 or 외부식별자&lt;/li&gt;
&lt;li&gt;단일 속성으로 식별 되는가? 단일식별자 or 복합식별자&lt;/li&gt;
&lt;li&gt;업무적으로 의미가 있던 식별자 속성을 대체하여 새롭게 만들었는가? 본질식별자 or 인조식별자&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(위 질문에 대한 답이 맞으면 왼쪽 아니라면 오른쪽)&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;  주 식별자(Primary Identifier) 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유일성 : 주식별자에 의해 엔터티 내에 모든 인스턴스들이 유일하게 구분됨&lt;/li&gt;
&lt;li&gt;최소성 : 주식별자를 구성하는 속성의 수는 유일성을 만족하는 최소의 수&lt;/li&gt;
&lt;li&gt;불변성 : 주식별자의 값은 변하지 않는 것&lt;/li&gt;
&lt;li&gt;존재성 : 주식별자가 지정되면 반드시 값이 존재 (Not Null)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;  외부 식별자(Foreign Identifier) 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주식별자의 특징과 일치하지 않고, 참조무결성 제약조건(Referential Integrity)에 따른 특징 있음&lt;/li&gt;
&lt;li&gt;다른 엔터티와의 관계를 통해 자식 쪽 엔터티에 생성되는 속성&lt;/li&gt;
&lt;li&gt;DB 생성 시, 외래키(Foreign Key) 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  식별자 관계(Identifying Relationship)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모로부터 받은 식별자를 자식엔터티의 주식별자로 이용하는 경우를 식별자 관계라 함&lt;/li&gt;
&lt;li&gt;주식별자의 특징을 전부 가지고 있어야 하며, 부모의 식별자가 손자까지 계속 이어지는 점이 있음&lt;/li&gt;
&lt;li&gt;반드시 부모엔터티가 생성되어야 자식엔터티가 생성되는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  비식별자 관계(Non-Identifying Relationship)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모엔터티로부터 속성을 받았지만 자식엔터티의 주식별자로 사용하지 않고, 일반적인 속성으로 사용하는 경우&lt;/li&gt;
&lt;li&gt;부모엔터티의 식별자가 필수가 아니므로, 부모 없는 자식이 생성될 수 있음&lt;/li&gt;
&lt;li&gt;부모-자식 간에 관계가 있지만, 부모 먼저 소멸될 수 있음&lt;/li&gt;
&lt;li&gt;부모의 식별자를 자식 식별자로 사용해도 되지만, 별도의 주식별자를 생성하는 것이 유리할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  식별자 관계로만 설정한다면?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발 복잡성과 오류를 유발하는 요인이 될 수 있음&lt;/li&gt;
&lt;li&gt;지속적인 식별자 관계를 연결한 데이터 모델의 PK의 개수는 데이터 모델의 흐름이 길어질수록 증가&lt;/li&gt;
&lt;li&gt;조인할 경우 SQL 구문의 WHERE 절이 매우 길어짐&lt;/li&gt;
&lt;li&gt;조인관계를 누락할 가능성이 높아짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_9B3338451E9C-1.jpeg&quot; data-origin-width=&quot;2754&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwiN7j/btsEmFXX2u2/KW9743msKRhsjHT9KgV67K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwiN7j/btsEmFXX2u2/KW9743msKRhsjHT9KgV67K/img.jpg&quot; data-alt=&quot;식별자 관계로만 이루어졌을 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwiN7j/btsEmFXX2u2/KW9743msKRhsjHT9KgV67K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwiN7j%2FbtsEmFXX2u2%2FKW9743msKRhsjHT9KgV67K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;253&quot; data-filename=&quot;IMG_9B3338451E9C-1.jpeg&quot; data-origin-width=&quot;2754&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;식별자 관계로만 이루어졌을 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 규칙처럼 자식 엔터티가 많아질수록 식별자는 1개씩 추가된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  비식별자 관계로만 설정한다면?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;불필요하게 부모엔터티까지 찾아가면서 자식엔터티에서 조회할 경우가 생김&lt;/li&gt;
&lt;li&gt;즉, 불필요한 조인이 다량으로 유발될 가능성&lt;/li&gt;
&lt;li&gt;SQL 구문이 길어지면서 복잡성이 증가하고, 성능이 저하될 가능성&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_116CFB2F12C2-1.jpeg&quot; data-origin-width=&quot;2755&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2Sit3/btsEnWYWobV/3zB9Pzm3nvJrInrT3vxFlK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2Sit3/btsEnWYWobV/3zB9Pzm3nvJrInrT3vxFlK/img.jpg&quot; data-alt=&quot;비식별자 관계로만 이루어졌을 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2Sit3/btsEnWYWobV/3zB9Pzm3nvJrInrT3vxFlK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2Sit3%2FbtsEnWYWobV%2F3zB9Pzm3nvJrInrT3vxFlK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;251&quot; data-filename=&quot;IMG_116CFB2F12C2-1.jpeg&quot; data-origin-width=&quot;2755&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;비식별자 관계로만 이루어졌을 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 손자 테이블에서 부모번호가 '1'인 데이터를 조회할 경우 SQL문은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1707138900655&quot; class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM 부모 A, 자식 B, 손자 C
WHERE A.부모번호 = B.부모번호
AND B.자식번호 = C.자식번호
AND A.부모번호 = '1';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 식별자 관계였다면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1707138900656&quot; class=&quot;sql&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM 손자
WHERE 부모번호 = '1';&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  비식별자관계 선택 프로세스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 식별자 관계로 모든 관계가 연결되면서 아래 조건에 해당할 경우 비식별자 관계로 조정한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_EE6472D3315C-1.jpeg&quot; data-origin-width=&quot;2697&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqOV53/btsEoqrYMMT/Z1aVQ5svVmNRIfWc6jW350/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqOV53/btsEoqrYMMT/Z1aVQ5svVmNRIfWc6jW350/img.jpg&quot; data-alt=&quot;비식별자 관계 선택 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqOV53/btsEoqrYMMT/Z1aVQ5svVmNRIfWc6jW350/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqOV53%2FbtsEoqrYMMT%2FZ1aVQ5svVmNRIfWc6jW350%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;259&quot; data-filename=&quot;IMG_EE6472D3315C-1.jpeg&quot; data-origin-width=&quot;2697&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;비식별자 관계 선택 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;  식별자관계 고려사항&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반드시 부모엔터티에 종속&lt;/li&gt;
&lt;li&gt;자식 주식별자 구성에 부모 주식별자 포함 필요&lt;/li&gt;
&lt;li&gt;상속받은 주식별자속성을 타 엔터티에 이전 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  비식별자관계 고려사항&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;약한 종속관계&lt;/li&gt;
&lt;li&gt;자식 주식별자 구성을 독립적으로 구성&lt;/li&gt;
&lt;li&gt;자식 주식별자 구성에 부모 주식별자 부분 필요&lt;/li&gt;
&lt;li&gt;상속받은 주식별자 속성을 타 엔터티에 차단 필요&lt;/li&gt;
&lt;li&gt;부모쪽의 관계참여가 선택관계 (부모 없이 자식이 먼저 생성되는 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;  개인적인 생각&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 다룬 내용은 &quot;SQL 전문가 가이드&quot; 도서를 통해 얻은 지식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 도서를 통해 얻은 지식을 바탕으로 식별자에 대한 고민을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;무조건 비식별자로 두고, 모든 테이블에 시퀀스와 같은 일련번호를 부여한 PK(기본키)를 만드는 것이 좋을까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;상황에 따라 다르지만 아니다&quot; 고 말할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대학교 DB 교수님은 &quot;의미있는 속성들로 기본키를 구성하되, 없다면 시퀀스(더미키)를 사용해라&quot;고 말씀하셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 회사마다 다르지만 시퀀스(UUID, TSID, 일시 등)를 사용하여 DB 구조를 유연하게 만드는 곳도 있었고, 이는 다양한 기업의 테크 블로그에서 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇듯 ERD 설계에 대한 접근 방식이 다양하지만 크게 개발자 입장과 DBA 입장이 있었음을 확인할 수 있었다.&lt;/p&gt;
&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;1. DB 구조의 유연성으로 미래의 변화에 대응하기 쉽다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;rarr; 복합키를 가진 두 테이블 사이에 중간 테이블을 만들어야 하는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;rarr; 주민등록번호와 비슷한 속성과 복합키로 이루어졌으나 정책상 변경해야하는 난감한 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;rarr; 비즈니스를 의존하지 않는 시퀀스라면 위 두 상황에서 자유롭게 변경 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 식별관계로 이루어진 테이블이 자식 테이블로 전파하면서 기본키가 증가하는 특징이 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 복합키로 된 테이블은 쿼리 조건문에 따라 인덱스가 제대로 사용되지 않을 가능성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 비즈니스 로직상 제약조건들이나 쿼리 성능개선에 유연한 대처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점으로는 불필요한 JOIN과 성능문제, 더미값, 중복데이터 등이 있다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nidev.gitlab.io/2019/12/11/%EB%A0%88%EA%B1%B0%EC%8B%9C%EC%97%90%EC%84%9C-JPA%EA%B8%B0%EB%B0%98-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A1%9C%EC%9D%98-%EC%9D%B4%EB%8F%99/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;레거시에서 JPA 기반 서비스로의 이동&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2595/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Legacy DB의 JPA Entity Mapping (복합키 매핑 편)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사이트가 많은 생각을 하게 해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;그렇다면 많은 개발자들이 비즈니스와 개발편의성, 유지보수성을 위해 시퀀스를 사용하는 것일까?&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;결국 프로젝트 특성과 개발 상황을 고려하여 DB 모델을 유연하게 만드는 방법에 대한 고민은 충분한 시간이 필요하지만, 이렇지 않을 경우거나 DBA가 없다면, 이커머스처럼 MAU가 백만명 넘는 활발한 사용자가 없다면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시퀀스를 사용하여 테이블 간에 비식별자 관계로 두고 빠른 개발을 하는 것이 낫다고 본다.&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/169</guid>
      <comments>https://ssdragon.tistory.com/169#entry169comment</comments>
      <pubDate>Mon, 5 Feb 2024 15:48:33 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 공유라이브러리 만들고 jitpack으로 배포하기 - 2편</title>
      <link>https://ssdragon.tistory.com/168</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ssdragon.tistory.com/167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스프링부트 공유라이브러리 만들고 jitpack으로 배포하기 - 1편&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링부트&amp;nbsp;공유라이브러리&amp;nbsp;만들고&amp;nbsp;jitpack으로&amp;nbsp;배포하기&amp;nbsp;-&amp;nbsp;2편&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃허브 주소 : &lt;a href=&quot;https://github.com/Sangyong-Jeon/practice_core-service&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Sangyong-Jeon/practice_core-service&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;3. 모든 서비스에 공유할 클래스 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 Filter를 만들겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 필터는 요청이 들어오면 콘솔창에 log를 남깁니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;718&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l7s03/btsCc2He9jh/ZiokSYdCs1xERGl9JmCmC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l7s03/btsCc2He9jh/ZiokSYdCs1xERGl9JmCmC1/img.png&quot; data-alt=&quot;CustomFilter&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l7s03/btsCc2He9jh/ZiokSYdCs1xERGl9JmCmC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl7s03%2FbtsCc2He9jh%2FZiokSYdCs1xERGl9JmCmC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;352&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;718&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CustomFilter&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유할 클래스를 만드시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 라이브러리 jar 파일로 만들기 위한 준비작업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행가능한 jar 파일로 빌드하지 않도록 빌드 시스템을 비활성화시키는 작업.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업에서 SpringBootPlugin 비활성화하여 bootJar도 비활성화하고, 실행가능한 jar로 빌드할 수 없게 설정함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링부트 의존성 관리를 위해 bom을 가져오도록 BOM_COORDINATES를 설정함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bom 파일은 의존성 라이브러리 버전을 명세한 pom 파일.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, SpringBootPlugin을 비활성화 했으니 버전관리가 안되므로 이를 정상적으로 작동시키게 하는 작업임.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u5QHs/btsCg3kOeQM/SHPhyTjHrvzPJDHtRzGBH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u5QHs/btsCg3kOeQM/SHPhyTjHrvzPJDHtRzGBH1/img.png&quot; data-alt=&quot;main 메서드 및 SpringBoot를 실행시키는 클래스 삭제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u5QHs/btsCg3kOeQM/SHPhyTjHrvzPJDHtRzGBH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu5QHs%2FbtsCg3kOeQM%2FSHPhyTjHrvzPJDHtRzGBH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;353&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;main 메서드 및 SpringBoot를 실행시키는 클래스 삭제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main 메서드 및 SpringBoot를 실행시키는 클래스 제거함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유 라이브러리는 다른 서비스에서 사용하기 위해 존재하기에 실행가능한 클래스파일은 필요 X&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;1010&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8tXck/btsCbdvdkcs/Zsr3Qb6Rv09sO2tLI75XRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8tXck/btsCbdvdkcs/Zsr3Qb6Rv09sO2tLI75XRK/img.png&quot; data-alt=&quot;build.gradle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8tXck/btsCbdvdkcs/Zsr3Qb6Rv09sO2tLI75XRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8tXck%2FbtsCbdvdkcs%2FZsr3Qb6Rv09sO2tLI75XRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;495&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;1010&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;build.gradle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpringBootPlugin을 비활성화하고 BOM_COORDINAES 설정함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 gradle을 리로드하여 갱신&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KvRf7/btsCfZDcbMf/Ns1HhT9eEMMSWlkYagWTLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KvRf7/btsCfZDcbMf/Ns1HhT9eEMMSWlkYagWTLk/img.png&quot; data-alt=&quot;build 해서 jar파일 만들기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KvRf7/btsCfZDcbMf/Ns1HhT9eEMMSWlkYagWTLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKvRf7%2FbtsCfZDcbMf%2FNs1HhT9eEMMSWlkYagWTLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;415&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;build 해서 jar파일 만들기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우측 Gradle 버튼을 눌려 build를 실행함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build/libs 폴더에 jar파일이 만들어졌는지 확인함.&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;5. 자동 구성(Auto-Configuration) 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 필터를 스프링 빈(컴포넌트)로 만들었지만, 다른 서비스에서 Component Scan(컴포넌트 스캔) 대상이 되지 않는다. 따라서 컴포넌트 스캔 대상으로 만들어서 스프링 빈으로 등록을 하도록 작업한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;1450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4JWBm/btsB7VB3ZME/xkJ79UqT42OnMzP65zffS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4JWBm/btsB7VB3ZME/xkJ79UqT42OnMzP65zffS1/img.png&quot; data-alt=&quot;https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4JWBm/btsB7VB3ZME/xkJ79UqT42OnMzP65zffS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4JWBm%2FbtsB7VB3ZME%2FxkJ79UqT42OnMzP65zffS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;616&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;1450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서에 나와있는 방식으로 자동구성을 정의할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 공유라이브러리의 스프링 빈(컴포넌트)들은 Component Scan(컴포넌트 스캔) 대상이 아니기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공유라이브러리에서 `@Bean` 또는 `@Component` 등 작성해도 다른 서비스에서는 스프링 빈으로 등록안됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 다른 서비스에서도 컴포넌트 스캔 대상이 되도록 정의해야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 Locating Auto-configuration Candidates 방식으로 자동구성을 만들어서 스캔 대상이 되도록 할 것임.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LmNqF/btsB7V3adik/T0pnJUtG1bpmUUxvkHqQ11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LmNqF/btsB7V3adik/T0pnJUtG1bpmUUxvkHqQ11/img.png&quot; data-alt=&quot;AutoConfig 클래스 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LmNqF/btsB7V3adik/T0pnJUtG1bpmUUxvkHqQ11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLmNqF%2FbtsB7V3adik%2FT0pnJUtG1bpmUUxvkHqQ11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;358&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AutoConfig 클래스 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AutoConfig 클래스를 만들고 위와 같이 작성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5gBJR/btsB7gM4DQr/TDLw8sFnzvHWYDn3lmKICK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5gBJR/btsB7gM4DQr/TDLw8sFnzvHWYDn3lmKICK/img.png&quot; data-alt=&quot;META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5gBJR/btsB7gM4DQr/TDLw8sFnzvHWYDn3lmKICK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5gBJR%2FbtsB7gM4DQr%2FTDLw8sFnzvHWYDn3lmKICK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;427&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777; text-align: center;&quot;&gt;META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports &lt;/span&gt;위치에 파일을 만들고 여기에 AutoConfig 클래스 위치를 적으면 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 build를 해서 jar파일을 다시 만듦.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  주의사항&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck5lEk/btsCgtque3q/JbvhsHu6tBohC5DzH2VxPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck5lEk/btsCgtque3q/JbvhsHu6tBohC5DzH2VxPK/img.png&quot; data-alt=&quot;자체 테스트할 때 version 조심&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck5lEk/btsCgtque3q/JbvhsHu6tBohC5DzH2VxPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck5lEk%2FbtsCgtque3q%2FJbvhsHu6tBohC5DzH2VxPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;300&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자체 테스트할 때 version 조심&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build를 하면 프로젝트 이름 + version의 jar 파일이 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체 테스트할 때, 코드는 변경하지만 버전을 변경하지 않고 다른 서비스에 똑같은 이름의 jar 파일을 다시 테스트하는데 이러면 제대로 작동하지 않는다. 꼭 다른 이름이나 버전으로 지정해야한다.&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;6. 다른 서비스에서 테스트&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;1234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qXqwK/btsCbciNLak/gOsvrgQAZ1vZeJ3ao4HJZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qXqwK/btsCbciNLak/gOsvrgQAZ1vZeJ3ao4HJZK/img.png&quot; data-alt=&quot;테스트할 프로젝트 만듦&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qXqwK/btsCbciNLak/gOsvrgQAZ1vZeJ3ao4HJZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqXqwK%2FbtsCbciNLak%2FgOsvrgQAZ1vZeJ3ao4HJZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;464&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;1234&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테스트할 프로젝트 만듦&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 테스트하는 프로젝트를 공유 라이브러리와 동일하게 만들었음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cI6xBe/btsB7QtOG2U/nyjsK9SMb8CzCPK4zegb6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cI6xBe/btsB7QtOG2U/nyjsK9SMb8CzCPK4zegb6k/img.png&quot; data-alt=&quot;공유라이브러리 jar 파일 가져오기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cI6xBe/btsB7QtOG2U/nyjsK9SMb8CzCPK4zegb6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcI6xBe%2FbtsB7QtOG2U%2FnyjsK9SMb8CzCPK4zegb6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;267&quot; height=&quot;451&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공유라이브러리 jar 파일 가져오기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유라이브러리 jar 파일을 최상단에 가져옴&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JlW6J/btsB7eBEOG7/N3dBn5KXBAWhdn5p6zO9hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JlW6J/btsB7eBEOG7/N3dBn5KXBAWhdn5p6zO9hk/img.png&quot; data-alt=&quot;build.gradle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JlW6J/btsB7eBEOG7/N3dBn5KXBAWhdn5p6zO9hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJlW6J%2FbtsB7eBEOG7%2FN3dBn5KXBAWhdn5p6zO9hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;181&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;build.gradle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle에 `implementation files('core-service-0.0.3.jar')` 을 작성하고 리로드하여 갱신함&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KHu6q/btsCc4rs5kz/vtbGKzkPnfVFzGbzDolYHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KHu6q/btsCc4rs5kz/vtbGKzkPnfVFzGbzDolYHk/img.png&quot; data-alt=&quot;TestController&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KHu6q/btsCc4rs5kz/vtbGKzkPnfVFzGbzDolYHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKHu6q%2FbtsCc4rs5kz%2FvtbGKzkPnfVFzGbzDolYHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;232&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TestController&lt;/figcaption&gt;
&lt;/figure&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buUWEL/btsCg2zrqPx/QjhK7mvHabv3rYWxS18IrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buUWEL/btsCg2zrqPx/QjhK7mvHabv3rYWxS18IrK/img.png&quot; data-alt=&quot;테스트 시도하여 정상적으로 log가 남음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buUWEL/btsCg2zrqPx/QjhK7mvHabv3rYWxS18IrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuUWEL%2FbtsCg2zrqPx%2FQjhK7mvHabv3rYWxS18IrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;396&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테스트 시도하여 정상적으로 log가 남음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 CustomFilter가 작동함을 볼 수 있음&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;7. JitPack으로 배포하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;920&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDYen/btsCbcXn8rB/5ghPeoFsdwyRtO0s4WmkAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDYen/btsCbcXn8rB/5ghPeoFsdwyRtO0s4WmkAk/img.png&quot; data-alt=&quot;커밋 내역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDYen/btsCbcXn8rB/5ghPeoFsdwyRtO0s4WmkAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDYen%2FbtsCbcXn8rB%2F5ghPeoFsdwyRtO0s4WmkAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;233&quot; height=&quot;386&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;920&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;커밋 내역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금껏 커밋 내역인데 편의상 아무렇게나 하셔도 상관없지만 일정한 규칙을 가져야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태그를 위해 git flow 전략을 사용하고 커밋도 `feat: `, `fix: `, `refactor: ` 등으로 작성하셔야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태로 JitPack에 배포하면 자체적으로 build하게 되는데 이 때 기본값으로 JDK 1.8로 시도하기에 에러가 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 JitPack 설정을 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;1826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q6ehD/btsB6wihelk/6eQxksQ6nNiZAuzopkr161/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q6ehD/btsB6wihelk/6eQxksQ6nNiZAuzopkr161/img.png&quot; data-alt=&quot;build.gradle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q6ehD/btsB6wihelk/6eQxksQ6nNiZAuzopkr161/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq6ehD%2FbtsB6wihelk%2F6eQxksQ6nNiZAuzopkr161%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;901&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;1826&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;build.gradle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;종속성에 버전을 명시하지 않으면 JitPack에서 build할 때 에러가 발생합니다. 이 것 때문에 몇 시간 날렸습니다 ^^&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle 파일에서 빨간색 네모칸에 있는 것을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JitPack에서는 Gradle 프로젝트에서 build 하는 경우 maven 또는 mavne-publish 플로그인을 활성화해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고 : &lt;a href=&quot;https://docs.jitpack.io/building/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.jitpack.io/building/&lt;/a&gt; )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업은 gradle 프로젝트에서 maven local repository에 라이브러리를 배포하는 방법입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WPEg0/btsCbcJRv7h/i4H6arVYN7laZkk0l6lqJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WPEg0/btsCbcJRv7h/i4H6arVYN7laZkk0l6lqJ1/img.png&quot; data-alt=&quot;jitpack.yml&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WPEg0/btsCbcJRv7h/i4H6arVYN7laZkk0l6lqJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWPEg0%2FbtsCbcJRv7h%2Fi4H6arVYN7laZkk0l6lqJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;463&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;jitpack.yml&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 최상위 경로에 jitpack.yml 파일을 만들어서 위 내용을 적습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일을 JitPack에서 읽고 그대로 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 설명하면 JDK를 Zulu 17.0.2 버전으로 정의하고 그걸로 build 및 publishToMavenLocal 을 한다는 의미입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;1488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tNQYx/btsCg6aLOyt/EQhPRFoFYTD1vu6kCy3i71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tNQYx/btsCg6aLOyt/EQhPRFoFYTD1vu6kCy3i71/img.png&quot; data-alt=&quot;https://jitpack.io/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tNQYx/btsCg6aLOyt/EQhPRFoFYTD1vu6kCy3i71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtNQYx%2FbtsCg6aLOyt%2FEQhPRFoFYTD1vu6kCy3i71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;637&quot; height=&quot;752&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;1488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://jitpack.io/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jitpack.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jitpack.io/&lt;/a&gt; 에 들어가서 Github랑 연동하면 이렇게 뜹니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력창에 저희가 만들었던 Github Repository URL(주소)를 넣고 [Look up] 버튼을 누르고 아래를 보면 저희가 릴리즈한 버전들이 나옵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;914&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVLwT7/btsCf7utElf/GzzDGlePIbpR5oTlm78TSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVLwT7/btsCf7utElf/GzzDGlePIbpR5oTlm78TSk/img.png&quot; data-alt=&quot;JitPack&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVLwT7/btsCf7utElf/GzzDGlePIbpR5oTlm78TSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVLwT7%2FbtsCf7utElf%2FGzzDGlePIbpR5oTlm78TSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;439&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;914&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JitPack&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 [Get it] 버튼을 누르고 기다리면 Log 에 결과가 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨간색은 실패이고 초록색은 성공입니다. Log를 클릭하면 기록을 보여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공하면 [Get it] 버튼이 초록색인데 누르면 홈페이지 아래에 어떻게 사용하는지 나옵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;1242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CceMr/btsCg3yllGH/1JMQF8d5kAXDddqrhKYSPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CceMr/btsCg3yllGH/1JMQF8d5kAXDddqrhKYSPk/img.png&quot; data-alt=&quot;JitPack&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CceMr/btsCg3yllGH/1JMQF8d5kAXDddqrhKYSPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCceMr%2FbtsCg3yllGH%2F1JMQF8d5kAXDddqrhKYSPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;599&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;1242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JitPack&lt;/figcaption&gt;
&lt;/figure&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;1198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqhpwk/btsB8VPkrIp/p4OTFihQLhISnUu0RiapT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqhpwk/btsB8VPkrIp/p4OTFihQLhISnUu0RiapT0/img.png&quot; data-alt=&quot;test-service 프로젝트의 build.gradle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqhpwk/btsB8VPkrIp/p4OTFihQLhISnUu0RiapT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqhpwk%2FbtsB8VPkrIp%2Fp4OTFihQLhISnUu0RiapT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;584&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;1198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;test-service 프로젝트의 build.gradle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 넣고 gradle을 리로드를 하면 정상적으로 JitPack에서 공유라이브러리를 가져오는 것을 볼 수 있습니다.&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;  또 다른 배포 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JitPack 말고도 Github Packages 를 사용하여 배포가 가능합니다. 그런데 단점으로는 Github Token을 발급받아서 정의해야하는데 이게 노출됩니다. 따라서 private repository로 바꾸어야하는데 이 때 비용이 발생합니다. 물론 권한을 write:packages 와 delete:packages 만을 주어서 깃허브 패키지만 건들 수 있다고 하지만 찝찝합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 배포 방법으로는 사설(사내) 저장소인 Nexus(넥서스)를 사용하는 것입니다. 이러면 사내에서만 사용되기에 외부에서 접근이 불가능하고 빠른 배포가 가능합니다.&lt;/p&gt;</description>
      <category>Spring/Spring</category>
      <category>Github Actions</category>
      <category>jitpack</category>
      <category>SpringBoot</category>
      <category>SprintBoot Library</category>
      <category>깃허브 액션</category>
      <category>스프링부트 라이브러리</category>
      <category>스프링부트 라이브러리 만들기</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/168</guid>
      <comments>https://ssdragon.tistory.com/168#entry168comment</comments>
      <pubDate>Mon, 18 Dec 2023 23:57:12 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 공유라이브러리 만들고 jitpack으로 배포하기 - 1편</title>
      <link>https://ssdragon.tistory.com/167</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링부트&amp;nbsp;공유라이브러리&amp;nbsp;만들고&amp;nbsp;jitpack으로&amp;nbsp;배포하기&amp;nbsp;-&amp;nbsp;1편&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ssdragon.tistory.com/168&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스프링부트 공유라이브러리 만들고 jitpack으로 배포하기 - 2편&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃허브 주소 : &lt;a href=&quot;https://github.com/Sangyong-Jeon/practice_core-service&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Sangyong-Jeon/practice_core-service&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;작성 이유&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SpringBoot(스프링부트)와 Gradle, Java로 라이브러리 만드는 자료가 많이 없었음&lt;/li&gt;
&lt;li&gt;Maven은 꽤 있었으나 Gradle은 많이 안보였음&lt;/li&gt;
&lt;li&gt;JitPack 으로 배포하는 방법도 있기는 했으나 나에겐 잘 안되던 것들이었음&lt;/li&gt;
&lt;li&gt;시행착오를 많이 겪었던 부분이라 기록으로 남겨서 추후에도 사용하기 위함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기술 사양&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt; Java 17.0.2 - Zulu&lt;/li&gt;
&lt;li&gt;SpringBoot 3.2&lt;/li&gt;
&lt;li&gt;IntelliJ&lt;/li&gt;
&lt;li&gt;Github&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;간단한 동작 설명&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;공유 라이브러리(core-service)에서 Github의 main branch(브랜치)에 Pull Request(PR)를 하여 Merge를 함&lt;/li&gt;
&lt;li&gt;미리 정의했던 Github Actions(깃허브 액션)을 통해 Release(릴리즈) 만듬&lt;/li&gt;
&lt;li&gt;JitPack에서 릴리즈의 최신버전을 배포함&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Github를 조금이라도 알고있어야 합니다. JitPack은 깃허브와 연동하고 깃허브 릴리즈를 기반으로 만들어 배포하기 때문입니다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 깃허브 액션으로 Tag/Release 자동화 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 : &lt;a href=&quot;https://devocean.sk.com/blog/techBoardDetail.do?ID=164861&amp;amp;boardType=techBlog&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devocean.sk.com/blog/techBoardDetail.do?ID=164861&amp;amp;boardType=techBlog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;1034&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTSf8U/btsB7dbJhDC/CrKpOYa5BhbOzJtxFGIL6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTSf8U/btsB7dbJhDC/CrKpOYa5BhbOzJtxFGIL6K/img.png&quot; data-alt=&quot;Github Repository 만들기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTSf8U/btsB7dbJhDC/CrKpOYa5BhbOzJtxFGIL6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTSf8U%2FbtsB7dbJhDC%2FCrKpOYa5BhbOzJtxFGIL6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;436&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;1034&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Github Repository 만들기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 공유 라이브러리인 core-service를 만들겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 깃허브 리포지토리를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Actions 탭에 들어갑니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;1036&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR3lPu/btsB7n6GyR0/lxmFCOVphVCvtnzeX29XL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR3lPu/btsB7n6GyR0/lxmFCOVphVCvtnzeX29XL1/img.png&quot; data-alt=&quot;깃허브 액션에 와서 [set up a workflow yourself] 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR3lPu/btsB7n6GyR0/lxmFCOVphVCvtnzeX29XL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR3lPu%2FbtsB7n6GyR0%2FlxmFCOVphVCvtnzeX29XL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;455&quot; height=&quot;510&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;1036&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;깃허브 액션에 와서 [set up a workflow yourself] 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨간 네모칸에 있는 [set up a workflow yourself] 를 클릭해서 워크플로우를 작성해봅니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이미 만들어져있는 워크플로우도 많으니 검색을 해서 그대로 편집하셔도 좋습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;1034&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCtWil/btsCgZJsHYN/V8IgKo0ujGkEqojli1EnrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCtWil/btsCgZJsHYN/V8IgKo0ujGkEqojli1EnrK/img.png&quot; data-alt=&quot;깃허브 액션에서 워크플로우 파일 편집&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCtWil/btsCgZJsHYN/V8IgKo0ujGkEqojli1EnrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCtWil%2FbtsCgZJsHYN%2FV8IgKo0ujGkEqojli1EnrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;518&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;1034&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;깃허브 액션에서 워크플로우 파일 편집&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 어떤 작업을 할 것인지(워크플로우)를 작성하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 넣습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702905521781&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: github action tag release

on:
  push:
    branches:
      - main
jobs:
  release:
    name: Release
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: write
      id-token: write
      
    steps:
      - name: Bump version and push tag
        id: tag_version
        uses: mathieudutour/github-tag-action@v6.1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Create a GitHub release
        uses: ncipollo/release-action@v1
        with:
          tag: ${{ steps.tag_version.outputs.new_tag }}
          name: ${{ steps.tag_version.outputs.new_tag }}
          body: ${{ steps.tag_version.outputs.changelog }}&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;name : 워크플로우 이름&lt;/li&gt;
&lt;li&gt;on : 어떤 조건에서 워크플로우를 실행시킬 것인가를 정의하는 곳&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서는 main 브랜치에 push가 올 때 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;jobs : 워크플로우는 여러개의 job으로 구성되며, job들은 기본값으로 병렬실행이기에 순차실행이 필요하면 따로 id 지정해야함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;release : job의 고유한 id를 의미하며 문자열이어야 함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;name : 해당 job의 이름 설정&lt;/li&gt;
&lt;li&gt;runs-on : 어떤 OS에서 실행할 것인지&lt;/li&gt;
&lt;li&gt;permissions : 권한 설정&lt;/li&gt;
&lt;li&gt;steps : job 안에서 순차적으로 실행되는 프로세스 단위가 step
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;- name : step 이름&lt;/li&gt;
&lt;li&gt;id : step의 고유 id&lt;/li&gt;
&lt;li&gt;uses : 어떤 액션을 사용할지 지정함. 이미 만들어진 액션을 사용할 때 지정.&lt;/li&gt;
&lt;li&gt;with : 실행할 때 어떤 파라미터를 같이 넣을건지 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;워크플로우 동작 설명&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;main 브랜치에 push를 하면 워크플로우가 실행되어 자동 태그하여 릴리즈에 올림&lt;/li&gt;
&lt;li&gt;깃허브의 Commit Log를 기반으로 릴리즈 버전이 업데이트되면서 커밋 로그를 바탕으로 배포로그 생성&lt;/li&gt;
&lt;li&gt;init, modify 는 버전으로 기록되지 않음&lt;/li&gt;
&lt;li&gt;`fix: `, `feat: ` 두 가지의 깃허브 로그가 기록되어 태그되고 배포로그가 생성됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;1508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0Xd6C/btsB6vwSpak/slWKPuj3v6GTcpsQOAkBgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0Xd6C/btsB6vwSpak/slWKPuj3v6GTcpsQOAkBgK/img.png&quot; data-alt=&quot;코드 넣고 커밋&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0Xd6C/btsB6vwSpak/slWKPuj3v6GTcpsQOAkBgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0Xd6C%2FbtsB6vwSpak%2FslWKPuj3v6GTcpsQOAkBgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;482&quot; height=&quot;560&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;1508&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코드 넣고 커밋&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Commit changes] 버튼을 누르고 커밋합니다.&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;2. 스프링부트 공유 라이브러리 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 Git Flow 전략으로 사용하기에 별도의 브랜치를 만들어서 commit과 merge를 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(저처럼 안하셔도 됩니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 develop 브랜치를 만들고 여기서 진행하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;1084&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh8JPf/btsCawofxG7/P94QbnRujQTMMhnK1aL1pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh8JPf/btsCawofxG7/P94QbnRujQTMMhnK1aL1pk/img.png&quot; data-alt=&quot;IntelliJ(인텔리제이)로 스프링부트 Gradle 기반 프로젝트 만들기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh8JPf/btsCawofxG7/P94QbnRujQTMMhnK1aL1pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh8JPf%2FbtsCawofxG7%2FP94QbnRujQTMMhnK1aL1pk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;428&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;1084&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IntelliJ(인텔리제이)로 스프링부트 Gradle 기반 프로젝트 만들기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃허브 리포지토리와 폴더를 연결시키고, 거기에 리포지토리 이름과 동일한 프로젝트 이름을 생성함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 /Documents/Github 경로에 practice_core-service 폴더를 만들었음.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UvbxF/btsB6uLwo1J/7tuYi26B9dVtpyXKyinKNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UvbxF/btsB6uLwo1J/7tuYi26B9dVtpyXKyinKNK/img.png&quot; data-alt=&quot;종속성 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UvbxF/btsB6uLwo1J/7tuYi26B9dVtpyXKyinKNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUvbxF%2FbtsB6uLwo1J%2F7tuYi26B9dVtpyXKyinKNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;432&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;종속성 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 가장 최신버전인 스프링부트 3.2.0 버전 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Web과 Lombok을 넣고 생성함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 편한대로 넣고 진행하면 됨.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;1386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnnVnA/btsCbfNkgtL/4xJMQvVG8vUrWJAZfPSq91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnnVnA/btsCbfNkgtL/4xJMQvVG8vUrWJAZfPSq91/img.png&quot; data-alt=&quot;Github Desktop으로 Git을 다룸&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnnVnA/btsCbfNkgtL/4xJMQvVG8vUrWJAZfPSq91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnnVnA%2FbtsCbfNkgtL%2F4xJMQvVG8vUrWJAZfPSq91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;560&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;1386&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Github Desktop으로 Git을 다룸&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;편의상 Github Desktop을 사용해서 Git을 다룸. 명령어 기반으로 하셔도 됨&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 커밋하고 PR을 하여 main 브랜치에 Merge하고 Actions 탭에 들어간다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;1416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bURGKL/btsB7TYymzh/GyRc5FKpOsznKKN8ukcKSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bURGKL/btsB7TYymzh/GyRc5FKpOsznKKN8ukcKSK/img.png&quot; data-alt=&quot;깃허브 액션에서 실행한 워크플로우를 보여줌&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bURGKL/btsB7TYymzh/GyRc5FKpOsznKKN8ukcKSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbURGKL%2FbtsB7TYymzh%2FGyRc5FKpOsznKKN8ukcKSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;525&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;1416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;깃허브 액션에서 실행한 워크플로우를 보여줌&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저렇게 초록색으로 표시가 되었다면 정상적으로 깃허브 액션 워크플로우가 동작한 것.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;1154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cf7K9Y/btsB6uLwtTY/6gCpjONcXMiHWLwnp2G040/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cf7K9Y/btsB6uLwtTY/6gCpjONcXMiHWLwnp2G040/img.png&quot; data-alt=&quot;깃허브 리포지토리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cf7K9Y/btsB6uLwtTY/6gCpjONcXMiHWLwnp2G040/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf7K9Y%2FbtsB6uLwtTY%2F6gCpjONcXMiHWLwnp2G040%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;434&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;1154&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;깃허브 리포지토리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 리포지토리에 들어가면 우측 하단에 릴리즈가 생긴 것을 볼 수 있음.&lt;/p&gt;
&lt;p data-ke-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>Spring/Spring</category>
      <category>Github Actions</category>
      <category>jitpack</category>
      <category>springboot library</category>
      <category>SpringBoot 공유라이브러리</category>
      <category>SpringBoot 라이브러리 jar 만들기</category>
      <category>깃허브 액션</category>
      <category>스프링부트 라이브러리</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/167</guid>
      <comments>https://ssdragon.tistory.com/167#entry167comment</comments>
      <pubDate>Mon, 18 Dec 2023 22:33:20 +0900</pubDate>
    </item>
    <item>
      <title>AWS Builders Online Series 후기</title>
      <link>https://ssdragon.tistory.com/166</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  잡담하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 아마존의 AWS가 한국에 정말 많은 투자와 관심을 주고 있다는게 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전부터 한국어 자막이나 한국어로 된 강의를 찍어서 행사나 강의, 세미나 등 열어주었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 최근 뉴스  &quot;AWS에서 2027년까지 7조 8500억원 한국 클라우드 시장 투자&quot;였는데 정말 어마무시하다...;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 말고도 클라우드 서비스를 하는 국내 기업은 &lt;a href=&quot;https://www.nhncloud.com/kr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NHN&lt;/a&gt;, &lt;a href=&quot;https://www.ncloud.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Naver&lt;/a&gt;, &lt;a href=&quot;https://kakaocloud.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Kakao&lt;/a&gt;, &lt;a href=&quot;https://cloud.samsungsds.com/serviceportal/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Samsung&lt;/a&gt; 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(해외는 Oracle, Google, Microsoft 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버는 두 번째 데이터센터 세종을 오픈하고, 카카오 클라우드도 여러 세미나 참여하면서 홍보하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NHN 클라우드도 올해 세미나 크게 열면서 엄청났죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 저번에 갔던 &quot;대한민국 디지털 미래혁신대전 2023&quot; 컨퍼런스에서 삼성에서도 클라우드 서비스를 하는것을 처음 보았다.&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;  왜 후기를 적는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 컨퍼런스를 다시 참여하다보니 예전에 받은 참석 증명서가 보여서 까먹기전에 기록하기 위해서 간단하게 끄적여본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kwRVm/btsAr4sQfSy/2HBfGrkzMKBtwwWJ6zEkwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kwRVm/btsAr4sQfSy/2HBfGrkzMKBtwwWJ6zEkwK/img.png&quot; data-alt=&quot;AWS Builders Online Series 참석 증명서(23.07.13)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kwRVm/btsAr4sQfSy/2HBfGrkzMKBtwwWJ6zEkwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkwRVm%2FbtsAr4sQfSy%2F2HBfGrkzMKBtwwWJ6zEkwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;488&quot; height=&quot;364&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS Builders Online Series 참석 증명서(23.07.13)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서는 자주 행사와 강의를 열어주고, 매번 새로운 강의로 볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 매번 같은 컨퍼런스이지만 다른 내용의 강의를 들을 수 있어서 정말 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오전, 오후로 나뉘고 여러 세션이 있는데 다시보기도 지원하기 때문에 편하게 들으면 되지만 그 날 다 들어아야하는 이유는 AWS 크레딧을 받아야하기 때문이다 (ㅎㅎ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨로 난이도를 나타내는데 나는 가장 쉬운 레벨100을 기준으로 들었고, 그 후 궁금한 것들로만 들었다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3254&quot; data-origin-height=&quot;1356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMbgYi/btsAnFOrFpU/6BEsOSckhDrNhChmfxaKdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMbgYi/btsAnFOrFpU/6BEsOSckhDrNhChmfxaKdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMbgYi/btsAnFOrFpU/6BEsOSckhDrNhChmfxaKdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMbgYi%2FbtsAnFOrFpU%2F6BEsOSckhDrNhChmfxaKdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;290&quot; data-origin-width=&quot;3254&quot; data-origin-height=&quot;1356&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;a href=&quot;https://www.youtube.com/watch?v=RkB1OtJR1ag&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;오프닝 연설 : 천만 사용자를 위한 서버리스 기반 클라우드 아키텍처 구성하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Track 1 : AWS 핵심 서비스 잘 이해하기&lt;/li&gt;
&lt;li&gt;Track 2 : AWS를 좀 더 잘 활용해보기&lt;/li&gt;
&lt;li&gt;Track 3 : AWS와 함께 비즈니스 키우기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 이 영상에 대해 &lt;a href=&quot;https://www.youtube.com/@AWSKorea/videos&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Amazon Web Services Korea&lt;/a&gt; 에서 올려줬기 때문에 시간 날 때 편하게 볼 수 있어서 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요새 IT 대기업들은 발표한 내용을 유튜브에 올려줘서 너무 좋다...&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;  오프닝 연설 살펴보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 발표에 했었던 천만 사용자 서비스 만들기 자료를 보여주면서 진행을 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3252&quot; data-origin-height=&quot;1602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuO3Sy/btsArpcXDQC/Qb9QGxLjPcYozs0fPGwCuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuO3Sy/btsArpcXDQC/Qb9QGxLjPcYozs0fPGwCuk/img.png&quot; data-alt=&quot;아마존 웹 서비스와 함께 천만 사용자 서비스 만들기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuO3Sy/btsArpcXDQC/Qb9QGxLjPcYozs0fPGwCuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuO3Sy%2FbtsArpcXDQC%2FQb9QGxLjPcYozs0fPGwCuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;342&quot; data-origin-width=&quot;3252&quot; data-origin-height=&quot;1602&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아마존 웹 서비스와 함께 천만 사용자 서비스 만들기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3220&quot; data-origin-height=&quot;1598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NdF3K/btsAp2byDje/dMGbK09epwMEri5ksuWfPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NdF3K/btsAp2byDje/dMGbK09epwMEri5ksuWfPK/img.png&quot; data-alt=&quot;인스턴스 기반 천만 사용자 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NdF3K/btsAp2byDje/dMGbK09epwMEri5ksuWfPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNdF3K%2FbtsAp2byDje%2FdMGbK09epwMEri5ksuWfPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;340&quot; data-origin-width=&quot;3220&quot; data-origin-height=&quot;1598&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인스턴스 기반 천만 사용자 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용은 &lt;a href=&quot;https://aws.amazon.com/ko/blogs/korea/5-years-scalling-up-to-10-million-users/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS 블로그&lt;/a&gt;에 나와있다. 무려 7년 전에 영상을 찍었는데 대단한 것 같다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3266&quot; data-origin-height=&quot;1508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KcrdB/btsAq3A1diB/jxZ4qfbPz67dn7VeW48TD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KcrdB/btsAq3A1diB/jxZ4qfbPz67dn7VeW48TD0/img.png&quot; data-alt=&quot;초기 서버리스 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KcrdB/btsAq3A1diB/jxZ4qfbPz67dn7VeW48TD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKcrdB%2FbtsAq3A1diB%2FjxZ4qfbPz67dn7VeW48TD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;322&quot; data-origin-width=&quot;3266&quot; data-origin-height=&quot;1508&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;초기 서버리스 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3244&quot; data-origin-height=&quot;1832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diEKKO/btsAqvq0VAO/dAou9ofGGIdze97kWPzkck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diEKKO/btsAqvq0VAO/dAou9ofGGIdze97kWPzkck/img.png&quot; data-alt=&quot;완성된 서버리스 기반 천만 사용자 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diEKKO/btsAqvq0VAO/dAou9ofGGIdze97kWPzkck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiEKKO%2FbtsAqvq0VAO%2FdAou9ofGGIdze97kWPzkck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;394&quot; data-origin-width=&quot;3244&quot; data-origin-height=&quot;1832&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;완성된 서버리스 기반 천만 사용자 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 구성은 AWS가 알아서 해주기 때문에 앱 구현에만 집중할 수 있게 된다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3164&quot; data-origin-height=&quot;1822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGIbae/btsAnDC9hjq/zOIER2FTK3XfG0MP4ZhzCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGIbae/btsAnDC9hjq/zOIER2FTK3XfG0MP4ZhzCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGIbae/btsAnDC9hjq/zOIER2FTK3XfG0MP4ZhzCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGIbae%2FbtsAnDC9hjq%2FzOIER2FTK3XfG0MP4ZhzCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;389&quot; data-origin-width=&quot;3164&quot; data-origin-height=&quot;1822&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  느낀점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라우드 서비스에서 불필요한 것들은 없애고 자동화로 만들어서 편의성을 향상시킴&lt;/li&gt;
&lt;li&gt;AWS의 새로운 서비스가 계속 생김&lt;/li&gt;
&lt;li&gt;AWS 컨퍼런스마다 새로운 영상을 찍어서 매년 보게 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 장점들 때문에 계속 AWS를 보게되는 선순환(?)이 생기고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강제로 AWS 공부시키고 관심을 가지게 만든다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 다양한 기업에서 AWS 도입 사례도 올라오기에 재밌게 보게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&quot;어? 이 기업도 AWS를 쓰네?&quot; 라면서 역시 클라우드는 AWS인가..?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨퍼런스를 참석하면 크레딧도 주기 때문에 쏠쏠하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나조차도 이러는걸 생각해보면 AWS는 역시 막강하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 Oracle Cloud 써보았는데 Oracle에서도 클라우드 서비스 하는지 모르는 사람도 꽤 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼성도 그렇고... 많이 쓰는 것만 보이니 안 쓰는 것은 알 방법이 없는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국내 클라우드 서비스 힘내요 ㅜㅜ!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 네이버가 두 번째 데이터센터 세종을 오픈하면서 그 쪽으로 그나마 점유율이 올라가지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번 클라우드 서비스 사용하면 쭉 사용하게 되니까 첫 도입하는 기업들을 잘 노려야 할 듯 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요새 OpenAI의 GPT가 유행하고 최근 발표로는 GPTs 도 나오면서 개인용 AI 비서 얘기가 잘 나오는데, 오픈AI에 투자한 마이크로소프트가 다시 1위 클라우드 기업이 될 지도 모르겠다. 오픈AI 입장에선 투자사인 마소랑 협약해서 하는게 맞을테니 거대한 인공지능을 개인들에게 보급할 때 마소의 Azure가 빛을 발휘하지 않을까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 기업들에게 클라우스 서비스 보급하지만, 삼성의 온디바이스AI와 GPT 연동된 기기 및 AI기기들이 나오면서 개인이 가지는 클라우드 인공지능도 생기지 않을까? 자연어로 GPTs를 사용하여 챗봇을 커스터마이징을 하니까 말이다. 그렇기에 현재는 기업에 많이 알리는 것도 중요하지만 개인들의 관심도 넓혀야하지 싶다. 자연스럽게 &quot;클라우드 서비스면 OOO 아니야?&quot; 라면서 생각하게끔 말이다.&lt;/p&gt;</description>
      <category>개발 etc</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/166</guid>
      <comments>https://ssdragon.tistory.com/166#entry166comment</comments>
      <pubDate>Thu, 16 Nov 2023 15:30:47 +0900</pubDate>
    </item>
    <item>
      <title>Spring Cloud 마이크로서비스들은 어떻게 서로 통신 할 수 있을까? - Discovery Client</title>
      <link>https://ssdragon.tistory.com/165</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 마이크로서비스(MS)들은 서로 위치를 알고 호출하는 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eureka(유레카)를 이용하여 서로 물리적 위치를 검색하여 찾기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 디스커버리를 위해 MS들이 Spring Cloud Load Balancer(로드밸런서)와 상호 작용할 수 있는 3가지 방법을 찾아보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;스프링 Discovery Client&lt;/li&gt;
&lt;li&gt;REST 템플릿을 사용한 스프링 Discovery Client&lt;/li&gt;
&lt;li&gt;넷플릭스 Feign Client&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  스프링 Discovery Client&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Discovery Client와 표준 스프링 RestTemplate 클래스를 사용하여 호출&lt;/li&gt;
&lt;li&gt;로드 밸런서와 그 안에 등록된 서비스에 대해 가장 낮은 수준 접근 가능&lt;/li&gt;
&lt;li&gt;즉, Discovery Client를 사용하면 로드밸런서에 등록된 모든 서비스와 URL을 쿼리할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1696831796282&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@RefreshScope
@EnableDiscoveryClient // Eureka Discovery Client를 활성화
public class MicroServiceApplication {
	public static void main(String[] args) {
    	SpringApplication.run(MicroServiceApplication.class, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`@EnableDiscoveryClient`로 스프링 클라우드에서 애플리케이션이 Discovery Client와 스프링 클라우드 로드 밸런서 라이브러리를 사용할 수 있게 됨.&lt;/p&gt;
&lt;pre id=&quot;code_1696832371887&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@RequiredArgsConstructor
public class UserDiscoveryClient {

	// Discovery Client를 클래스에 주입
	private final DiscoveryClient discoveryClient;
    
	public User getUser(String userId) {
		RestTemplate restTemplate = new RestTemplate();
		// 유저 서비스의 모든 인스턴스 리스트를 얻음
		List&amp;lt;ServiceInstance&amp;gt; instances = discoveryClient.getInstances(&quot;user-service&quot;);
		if (instances.isEmpty()) return null;
        
		// 서비스 엔드포인트를 검색해서 가져옴
		String serviceUri = String.format(&quot;%s/v1/user/%s&quot;, instances.get(0).getUri().toString(), userId);
		// 서비스 호출을 위해 표준 스프링 RestTemplate 클래스 사용
		ResponseEntity&amp;lt;User&amp;gt; restExchange = restTemplate.exchange(serviceUri, HttpMethod.GET, null, User.class, userId);
		return restExchange.getBody();
	}
}&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;  단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Discovery Client를 직접 호출해서 모든 서비스 리스트를 얻고, 그 중 호출할 서비스 인스턴스를 개발자가 책임지고 선정&lt;/li&gt;
&lt;li&gt;개발자가 서비스 호출하는 데 사용될 URL 생성&lt;/li&gt;
&lt;li&gt;RestTemplate을 직접 생성하여 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동 구성이 필요하지 않고 직접적인 제어가 필요할 때 직접 생성함&lt;/li&gt;
&lt;li&gt;로드밸런서 인터셉터의 영향을 피하고 특정한 설정 적용가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  로드 밸런서를 지원하는 스프링 REST 템플릿&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;향상된 스프링 RestTemplate으로 로드밸런서를 사용하는 서비스 호출&lt;/li&gt;
&lt;li&gt;스프링을 통해 로드 밸런서와 상호 작용할 수 있는 일반적인 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1696834521565&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@RefreshScope
public class MicroServiceApplication {
	public static void main(String[] args) {
		SpringApplication.run(MicroServiceApplication.class, args);
	}

	// RestTemplate 또는 WebClient가 스프링 클라우드 서비스 디스커버리(유레카)와 통합
	// 서비스 이름을 기반으로 특정 서비스에 대한 인스턴스를 선택하고 요청 가능
	@LoadBalanced
	@Bean
	public RestTemplate getRestTemplate() {
		return new RestTempalte();
	}
}&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;서비스의 물리적 위치 대신 호출하려는 서비스의 유레카 서비스 ID를 사용하여 대상 URL 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1696834865068&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@RequiredArgsConstructor
public class UserRestTemplateClient {

	private final RestTemplate restTemplate;
    
	public User getUser(String userId) {
		ResponseEntity&amp;lt;User&amp;gt; restExchange = 
			restTemplate.exchange(&quot;http://user-service/v1/user/{userId}&quot;, HttpMethod.GET, null, User.class, userId);
        
		return restExchange.getBody();
	}
}&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;첫 번째 방법과 다르게 Discovery Client가 없어짐&lt;/li&gt;
&lt;li&gt;URL에서 서버 이름은 유레카에 등록한 서비스 키의 애플리케이션 ID를 사용&lt;/li&gt;
&lt;li&gt;즉, 로드 밸런서를 지원하는 RestTemplate 클래스는 전달된 URL을 파싱하고 서버 이름으로 전달된 것을 키로 사용하여 서비스의 인스턴스를 로드 밸런서에 쿼리함&lt;/li&gt;
&lt;li&gt;실제 서비스 위치와 포트는 개발자에게 완전히 추상화&lt;/li&gt;
&lt;li&gt;RestTemplate 클래스를 사용하면 스프링 클라우드 로드 밸런서는 서비스 인스턴스에 대한 모든 요청을 라운드 로빈 방식으로 부여&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;  Feign Client&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;넷플릭스 Feign Client 라이브러리를 사용하여 로드 밸런서를 경유하여 서비스 호출&lt;/li&gt;
&lt;li&gt;자바 인터페이스를 정의하고, 스프링 클라우드 애노테이션을 추가하여 쉽게 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1696835134936&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@RefreshScope
@EnableFeignClients // Feign Client 사용하도록 설정
public class MicroServiceApplication {
	public static void main(String[] args) {
		SpringApplication.run(MicroServiceApplication.class, args);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저 서비스의 엔드포인트를 호출할 Feign Client의 Interface 정의를 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1696835497603&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FeignClient(&quot;user-service&quot;) // Feign에 서비스를 알려줌
public interface UserFeignClient {
	
    @GetMapping(&quot;/v1/user/{userId}&quot;) // 엔드포인트 경로와 액션(verb) 정의
    User getUser(@PathVariable String userId); // 엔드포인트에 전달되는 매개변수 정의
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;표준 스프링 RestTemplate은 서비스 호출에 대한 HTTP 상태 코드(status code)는 ResponseEntity의 getStatus()에 반환된다. 하지만 Feign Client는 호출된 서비스에서 반환한 모든 HTTP 4xx ~ 5xx 상태코드가 FeignException에 매핑된다. 이 예외에 특정 에러 메시지에 대해 파싱할 수 있는 JSON 내용이 포함되어 있다.&lt;br /&gt;&lt;br /&gt;Feign은 사용자가 정의한 Exception 클래스에 에러를 재매핑하는 에러 디코더(ErrorDecoder) 클래스를 작성할 수 있는 기능을 제공한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 역시 Feign Client가 가장 쉽게 작성가능하며, 추상화 되어 있고, Load Balanced 지원하기에 이것을 사용하는것이 가장 좋은 방법으로 보인다. 하지만 예외처리는 평범한 RestTemplate과는 다르니 FeignException 또는 FeignErrorDecoder, CircuitBreaker (Resilience4j) 등 클라이언트 측 회복성을 위한 다양한 방법을 숙지해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;스프링 마이크로서비스 코딩공작소 개정2판&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Spring Cloud</category>
      <category>Discovery Client</category>
      <category>Eureka 서비스 호출</category>
      <category>FeignClient</category>
      <category>restTemplate</category>
      <category>Spring Cloud Feign Client</category>
      <category>Spring Cloud MSA 통신</category>
      <category>Spring Cloud RestTemplate</category>
      <category>Spring MSA 서비스끼리 통신방법</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/165</guid>
      <comments>https://ssdragon.tistory.com/165#entry165comment</comments>
      <pubDate>Mon, 9 Oct 2023 16:24:12 +0900</pubDate>
    </item>
    <item>
      <title>Spring Cloud 마이크로서비스들이 직접 Vault에서 값을 읽도록 만들어야 할까? - 보안과 유연성의 고민</title>
      <link>https://ssdragon.tistory.com/164</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  현재 상황&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2742&quot; data-origin-height=&quot;1492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8T1HX/btsxtnuYDyi/9W2fnokfcqVkHGSjSUElgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8T1HX/btsxtnuYDyi/9W2fnokfcqVkHGSjSUElgk/img.png&quot; data-alt=&quot;Vault를 적용한 Spring Cloud 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8T1HX/btsxtnuYDyi/9W2fnokfcqVkHGSjSUElgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8T1HX%2FbtsxtnuYDyi%2F9W2fnokfcqVkHGSjSUElgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;375&quot; data-origin-width=&quot;2742&quot; data-origin-height=&quot;1492&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vault를 적용한 Spring Cloud 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Cloud Config Server (구성서버)는 가장 중요한 정보를 Vault에서 가져옴 (id, pw ...)&lt;/li&gt;
&lt;li&gt;덜 중요한 정보는 Github를 통해 가져옴 (application.yml ...)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;application.yml에는 `${vault.gitId}`로 vault값을 사용하고 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;application.yml 또는 application.properties에 굳이 ${vault.gitId}를 사용하지 않고, Vault에서 &quot;spring.cloud.config.server.git.username&quot; 키에 값을 넣으면 자동으로 적용되게 된다. 따라서 소스코드에서 Vault를 통해 값을 주입받는다는 기록을 남기지 않을 수 있다. 나는 왜 Vault에서 값을 가져온다고 명시했냐면 추후 설정정보를 변경하게 될 때 빠르게 찾기 위해서이다. 이것 또한 trade-off이다. 소스코드에 남기느냐 남기지 않느냐이기 때문이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  문제점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 마이크로서비스들은 Vault에서 값을 가져오지 못하는 문제점 발생
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`${vault.gitId}`를 문자 그대로 인식하고 있는 문제점&lt;/li&gt;
&lt;li&gt;즉, Vault에서 값을 못가져왔음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 나는 2가지 해결방법을 생각했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Vault에서 값을 가져오도록 각 MS(마이크로서비스)에 Vault와 Bootstrap 라이브러리 추가하기 (유연성 하락)&lt;/li&gt;
&lt;li&gt;Github의 application.yml에 id,pw 명시하기 (보안 하락)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안을 선택하면 각 마이크로서비스들은 Vault에 의존성이 생기며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유연성을 선택하면 구성 서버만 잘 관리하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  해결방법&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2602&quot; data-origin-height=&quot;1442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ueELT/btsxrlEoMGH/eByEotFIadaASqMiu4VYBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ueELT/btsxrlEoMGH/eByEotFIadaASqMiu4VYBK/img.png&quot; data-alt=&quot;보안을 선택한 Spring Cloud 아키텍처 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ueELT/btsxrlEoMGH/eByEotFIadaASqMiu4VYBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FueELT%2FbtsxrlEoMGH%2FeByEotFIadaASqMiu4VYBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;380&quot; data-origin-width=&quot;2602&quot; data-origin-height=&quot;1442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;보안을 선택한 Spring Cloud 아키텍처 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안을 선택해서 각 MS들도 Vault와 Bootstrap 라이브러리를 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github에 중요 정보를 적어놓으면 불안하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 Vault와 Config Server(구성서버)의 고가용성이 보장되어야하는 아키텍처 구조가 만들어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✅ 그런데 순간 머리에서 생각이 팍! 하고 나왔다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Config Server의 백엔드 저장소를 vault, github, native를 사용하고 있는데 그러면 여기 저장소들에서 설정값을 MS에 배포하니 MS에서는 Vault를 의존하지 않아도 되는것이 아닌가????&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;1540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9md96/btsxtnV5sUu/ZOS08968BPx2mD3zO6YKA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9md96/btsxtnV5sUu/ZOS08968BPx2mD3zO6YKA0/img.png&quot; data-alt=&quot;Config Server에서 배포하는 UserService의 설정파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9md96/btsxtnV5sUu/ZOS08968BPx2mD3zO6YKA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9md96%2FbtsxtnV5sUu%2FZOS08968BPx2mD3zO6YKA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;529&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;1540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Config Server에서 배포하는 UserService의 설정파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Config Server에서 Vault에 user-service/dev 값을 넣으니 위 그림처럼 제공되고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, MS에서는 Vault를 의존하지 않고 Config Server에서 모든 값들을 제공받으므로 의존성을 추가하지 않아도 됐다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Native에 있던 것은 테스트하려고 한 것이라 Github에 이동시킬 예정&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2700&quot; data-origin-height=&quot;1448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0LwE/btsxtnPqYHb/JetFiK4BKeDs8NdwhaXROk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0LwE/btsxtnPqYHb/JetFiK4BKeDs8NdwhaXROk/img.png&quot; data-alt=&quot;Spring Cloud Config Server에서 제공하는 설정정보&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0LwE/btsxtnPqYHb/JetFiK4BKeDs8NdwhaXROk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq0LwE%2FbtsxtnPqYHb%2FJetFiK4BKeDs8NdwhaXROk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;372&quot; data-origin-width=&quot;2700&quot; data-origin-height=&quot;1448&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Cloud Config Server에서 제공하는 설정정보&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Config Serveer가 Vault와 Github에서 설정정보를 가져와 취합하여 한번에 설정정보를 제공&lt;/li&gt;
&lt;li&gt;MS들은 Config Server만 의존하면 됨&lt;/li&gt;
&lt;li&gt;MS들은 Vault 라이브러리를 추가하지않아도 되었음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 봤었던 아키텍처 구조와 동일하게 보안적으로 향상된 구조가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 쪽을 선택해서 다른 한 쪽을 포기하게 되는 trade-off를 생각하다보니 큰 고민을 하게 되었다.&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;처음에는 단순하게 MS에다가 Vault 라이브러리를 추가하여 값을 읽도록 하지만 의존성이 생기는 차선책을 선택했지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Cloud Config Server의 백엔드 저장소를 제대로 생각하지 못했다는 점을 깨달았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault의 Secret Engine은 각 MS별로 만들어야하는 줄 알았기 때문이다. (&lt;s&gt;나 약간 바보...?&lt;/s&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Config Server가 읽고있는 Secret Engine에다가 &quot;user-service/dev&quot; 를 만들어 값을 넣으니 제대로 작동되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2244&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccPhES/btsxzYn1Fai/cvIsBGaxeFCzyIaFpgXGJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccPhES/btsxzYn1Fai/cvIsBGaxeFCzyIaFpgXGJ0/img.png&quot; data-alt=&quot;Vault 저장소&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccPhES/btsxzYn1Fai/cvIsBGaxeFCzyIaFpgXGJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccPhES%2FbtsxzYn1Fai%2FcvIsBGaxeFCzyIaFpgXGJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;213&quot; data-origin-width=&quot;2244&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vault 저장소&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진처럼 &quot;config-service-secret&quot; 이라는 Secret Engine이 있는데, 여기에 &quot;user-service&quot; 값을 넣었어야 했는데 또 다른 Secret Engine으로 만들었었다. 그래서 각 MS별로 Secret Engine을 읽어야하는 줄 알았는데 Config Server가 읽어서 거기서 나온 값을 MS로 전달하면 되었던 것이다. 계속 코드를 살펴보고 찾다보니 이렇게 놓치고 있었다는 점을 깨닫게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글로 설명하면 이해가 안갈 수 있으니 그림으로 내가 착각했던 것을 남겨본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2800&quot; data-origin-height=&quot;1288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpnD6I/btsxg20Hanc/QktfLJIplTwRAF7SB9NQcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpnD6I/btsxg20Hanc/QktfLJIplTwRAF7SB9NQcK/img.png&quot; data-alt=&quot;착각했던 Vault 사용법과 올바른 Vault 사용법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpnD6I/btsxg20Hanc/QktfLJIplTwRAF7SB9NQcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpnD6I%2Fbtsxg20Hanc%2FQktfLJIplTwRAF7SB9NQcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;320&quot; data-origin-width=&quot;2800&quot; data-origin-height=&quot;1288&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;착각했던 Vault 사용법과 올바른 Vault 사용법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파란색이 Secret Engine을 의미한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Cloud Config Server와 Vault에 대한 자료 및 강의가 많이 없기에 외국 행님들의 영상도 찾아보며 고생했던 기억이 무척 많이 남았다. 그래서 글을 써보게 되는데, 나중에는 무료강의도 한번 찍어볼 수 있지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Spring Cloud</category>
      <category>github</category>
      <category>Spring Cloud Config Server</category>
      <category>Spring Cloud Config Server Vault</category>
      <category>Spring Cloud Config Vault</category>
      <category>Spring Cloud Vault</category>
      <category>vault</category>
      <category>스프링 클라우드 Vault</category>
      <category>스프링 클라우드 구성 서버</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/164</guid>
      <comments>https://ssdragon.tistory.com/164#entry164comment</comments>
      <pubDate>Sat, 7 Oct 2023 16:43:24 +0900</pubDate>
    </item>
    <item>
      <title>Spring Cloud Config Server - Vault 적용</title>
      <link>https://ssdragon.tistory.com/163</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Spring Cloud Config 란?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6y9mo/btsvaGSnsLK/2MbXgq7BRHciJ59GVL9KI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6y9mo/btsvaGSnsLK/2MbXgq7BRHciJ59GVL9KI0/img.png&quot; data-alt=&quot;Spring Cloud Config Server&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6y9mo/btsvaGSnsLK/2MbXgq7BRHciJ59GVL9KI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6y9mo%2FbtsvaGSnsLK%2F2MbXgq7BRHciJ59GVL9KI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;371&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Cloud Config Server&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분산 시스템에서  애플리케이션 구성 데이터를 하나의 중앙화 된 저장소에서 관리 가능&lt;/li&gt;
&lt;li&gt;각 서비스를 다시 build(빌드)하지 않고 최신 구성 정보 적용 가능&lt;/li&gt;
&lt;li&gt;스프링 및 스프링이 아닌 서비스와 긴밀한 통합&lt;/li&gt;
&lt;li&gt;애플리케이션 환경에 맞는 구성 정보 사용 가능(dev, local. prod ...)&lt;/li&gt;
&lt;li&gt;속성 값 암호화 및 해독(대칭 or 비대칭)&lt;/li&gt;
&lt;li&gt;MSA 인스턴스를 많이 실행하더라도 항상 동일한 구성 보장 가능&lt;/li&gt;
&lt;li&gt;자체 프로퍼티(property) 관리 저장소가 있지만, 오픈 소스 프로젝트와도 통합 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Git(깃)&lt;/li&gt;
&lt;li&gt;Eureka(유레카)&lt;/li&gt;
&lt;li&gt;Vault(볼트)&lt;/li&gt;
&lt;li&gt;....&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림처럼 Config Server는 외부 저장소에서 최신 구성 정보를 가져오고, 그것을 애플리케이션에게 줄 수 있습니다.&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; &amp;nbsp; 외부 저장소는 어떤 것을 사용해야 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Config Server는 로컬 저장소(파일 저장소)를 사용해도 된다.&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;따라서 Git과 Vault를 사용하는 것이다.&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;  Github 저장소&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;깃허브에 구성 정보를 저장하고 손쉽게 변경 및 제어를 할 수 있음&lt;/li&gt;
&lt;li&gt;원격 저장소이기 때문에 개발자는 어디서든지 구성 정보 제어 가능&lt;/li&gt;
&lt;li&gt;private repository로 관리를 해야하는 것이 중요!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Github 저장소에 의존적이어서, SPOF(단일장애지점)이 될 가능성 있음&lt;/li&gt;
&lt;li&gt;Github에 접속하기 위한 ID, PW 또는 토큰값을 서버에 미리 설정해야 함 (보안)&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;이렇게 결국 Github ID, PW, 토큰값이나 각종 중요한 ID,PW를 미리 설정하거나 Github의 private repository에 저장해야한다는 문제점이 있다. 암호화를 하여 읽을때 해독하는 방법도 제공하지만 나는 이런 중요한 정보들(ID,PW)를 또 다른 외부 저장소인 Vault에 저장하기로 했다.&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;a href=&quot;https://www.vaultproject.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HashiCorp Vault&lt;/a&gt;(하시코프 볼트)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;secrets(시크릿)에 &lt;u&gt;민감한 정보를 안전하게 접근 및 관리 할 수 있는 오픈소스&lt;/u&gt; 도구&lt;/li&gt;
&lt;li&gt;PW, 인증서, API 키 등 접근을 제한하거나 제한하려는 어떤 정보로도 시크릿 정의 가능&lt;/li&gt;
&lt;li&gt;Spring Cloud Config Server의 설정 저장소로 Git과 Vault 지원&lt;/li&gt;
&lt;li&gt;관리자, 다른 Vault 사용자도 내 토큰이 없으면 정보를 읽을 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bppyDu/btsvkcivzHJ/LXoI8mVE08Kr83mJO5npk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bppyDu/btsvkcivzHJ/LXoI8mVE08Kr83mJO5npk1/img.png&quot; data-alt=&quot;Spring Cloud Config Server와 Vault, Github 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bppyDu/btsvkcivzHJ/LXoI8mVE08Kr83mJO5npk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbppyDu%2FbtsvkcivzHJ%2FLXoI8mVE08Kr83mJO5npk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;299&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Cloud Config Server와 Vault, Github 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 볼트에 가장 중요한 정보를 저장하고, 그 후에 Git에 저장하기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 나는 가장 중요한 정보에 Github의 ID,PW도 포함시키고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 내가 생각하는 구조는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QP8r4/btsvbEfTfO0/eKJHDweqF41TdY5HAM2WnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QP8r4/btsvbEfTfO0/eKJHDweqF41TdY5HAM2WnK/img.png&quot; data-alt=&quot;Spring Cloud Config Server 외부 저장소 구조 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QP8r4/btsvbEfTfO0/eKJHDweqF41TdY5HAM2WnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQP8r4%2FbtsvbEfTfO0%2FeKJHDweqF41TdY5HAM2WnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;322&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Cloud Config Server 외부 저장소 구조 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github 뿐만 아니라 DB나 중요한 정보는 전부 Vault에 저장하고 꺼내 쓰는 것으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Config Server는 Vault에 먼저 접속해서 설정정보를 가져와야하므로 Bootstrap 라이브러리를 활용해야 한다.&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;  Bootstrap(부트스트랩)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;properties와 yml을 동일하게 여기시면 됩니다.&lt;br /&gt;e.g. application.properties == application.yml&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 실행 시, application.yml보다 bootstrap.yml이 먼저 로드되게 함&lt;/li&gt;
&lt;li&gt;Bootstrap Context는 Application Context의 상위 컨텍스트&lt;/li&gt;
&lt;li&gt;2021년도 기준으로 Spring Cloud에서는 bootstrap을 자동으로 읽어오는 기능이 없음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 spring-cloud-starter-bootstrap 의존성 추가해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;bootstrap.yml은 Config Server(구성서버)를 준비하기 위한 것&lt;/li&gt;
&lt;li&gt;application.yml은 애플리케이션과 관련된 속성을 위한 것&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Spring Cloud Config Server 외부 저장소 설계&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2384&quot; data-origin-height=&quot;1542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCZ5k5/btsvfyNgJkC/XQ4ajNiA7sk78qLjrxuZIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCZ5k5/btsvfyNgJkC/XQ4ajNiA7sk78qLjrxuZIk/img.png&quot; data-alt=&quot;Spring Cloud Config Serve 보안 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCZ5k5/btsvfyNgJkC/XQ4ajNiA7sk78qLjrxuZIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCZ5k5%2FbtsvfyNgJkC%2FXQ4ajNiA7sk78qLjrxuZIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;451&quot; data-origin-width=&quot;2384&quot; data-origin-height=&quot;1542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Cloud Config Serve 보안 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Vault와 Github 연동하고 사용해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 사용하려면 우선 아래 라이브러리 추가가 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1695374124342&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.springframework.cloud:spring-cloud-config-server' //구성 서버
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap' //부트스트랩
implementation 'org.springframework.cloud:spring-cloud-starter-vault-config' //볼트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 볼트 라이브러리를 추가한 것에 의아할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 Config Server에서 기본적으로 설정 저장소로 지원하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서부터는 나의 여러 실험 환경에 의한 개인적인 의견이 포함된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 Config Server와 Bootstrap 의존성만 추가하고 테스트 해보았지만 Vault에서 가져온 값으로 Github에 접속할 수 없었다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695374297827&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  profiles:
    active: git, vault
  cloud:
    config:
      server:
        git:
          uri: file:///path/to/git/repo
          order: 2
        vault:
          host: 127.0.0.1
          port: 8200
          order: 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 우선순위를 정해보기도 하고, bootstrap.yml에 따로 적어보기도 하는 등 구글과 스택오버플로우 등 여러 방법을 적용해봤지만 절대로 불가능했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Vault에서 가져온 값으로 Github 접속하는 것이 아니라, Vault와 Github를 접속할 수 있는 값을 미리 적는 경우라면 상관이 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하지만 나는 Vault에서 가져온 값으로 Github에 접속하고 싶었기에 Vault 라이브러리를 추가해보았다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sO6qL/btsvjfmli0B/kwchRMIqUKFlkhvy1txocK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sO6qL/btsvjfmli0B/kwchRMIqUKFlkhvy1txocK/img.png&quot; data-alt=&quot;bootstrap.yml&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sO6qL/btsvjfmli0B/kwchRMIqUKFlkhvy1txocK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsO6qL%2Fbtsvjfmli0B%2FkwchRMIqUKFlkhvy1txocK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;399&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bootstrap.yml&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 bootstrap.yml에 vault에 접속하기 위한 설정정보를 적는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값을 통해서 vault에 접속하고 값을 미리 가져온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqc760/btsu9R7S3HI/R3zx3lRhMxgHYU9rkkFmlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqc760/btsu9R7S3HI/R3zx3lRhMxgHYU9rkkFmlK/img.png&quot; data-alt=&quot;application.yml&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqc760/btsu9R7S3HI/R3zx3lRhMxgHYU9rkkFmlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqc760%2Fbtsu9R7S3HI%2FR3zx3lRhMxgHYU9rkkFmlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;528&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;application.yml&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 vault에서 github ID, PW를 가져와서 application.yml에 넣어 접속을 시도하고 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config-server 설정정보에서 vault를 적지않아도 되지만 나는 config-server에서도 vault 구성정보를 확인하고 싶었기에 추가했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;1486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9g7df/btsvkQ7QlyP/AykTCgkOR09mhOtrnoocXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9g7df/btsvkQ7QlyP/AykTCgkOR09mhOtrnoocXK/img.png&quot; data-alt=&quot;spring cloud config server 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9g7df/btsvkQ7QlyP/AykTCgkOR09mhOtrnoocXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9g7df%2FbtsvkQ7QlyP%2FAykTCgkOR09mhOtrnoocXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;520&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;1486&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;spring cloud config server 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &quot;localhost:8888/config-service/dev&quot; 를 통해 vault에 있는 값을 최우선적으로 가져오는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 github에서 application.yml을 가져왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml에서 config-server의 백엔드 구성정보에 vault를 작성하지 않으면 github의 application.yml만 확인할 수 있고, 가져올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Vault를 도커로 실행하기&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;pre id=&quot;code_1695459511479&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d -p 8200:8200 --name vault -e 'VAULT_DEV_ROOT_TOKEN_ID=myroot' -e 'VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200' vault:1.13.3&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;VAULT_DEV_ROOT_TOKEN_ID
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;루트 토큰(root token) ID 설정하는 것&lt;/li&gt;
&lt;li&gt;루트 토큰은 볼트 구성을 시작하는 초기 액세스 토큰&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;VAULT_DEV_LISTEN_ADDRESS
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발 서버의 IP주소와 포트 설정&lt;/li&gt;
&lt;li&gt;기본값은 0.0.0.0:8200&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 도커 명령어로 vault 1.13.3 버전 이미지를 가져오고 컨테이너로 실행시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 편의성을 위해 볼트 UI를 이용하여 설정하기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLI(명령어)를 선호한다면 그건 다른 쪽에서 알아보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;application.yml과 bootstrap.yml에서 vault의 토큰값을 넣는 부분이 있는데 여기서 설정한 루트 토큰값을 넣으면 된다. 위 명령어대로라면 &quot;myroot&quot;를 넣으면 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;2258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDiB0E/btsvfBcKtDi/3FNfOnAHWnUXSxqgsCSpk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDiB0E/btsvfBcKtDi/3FNfOnAHWnUXSxqgsCSpk1/img.png&quot; data-alt=&quot;Vault UI (볼트 UI) 접속 페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDiB0E/btsvfBcKtDi/3FNfOnAHWnUXSxqgsCSpk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDiB0E%2FbtsvfBcKtDi%2F3FNfOnAHWnUXSxqgsCSpk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;677&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;2258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vault UI (볼트 UI) 접속 페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;http://0.0.0.0:8200/ui/vault/auth&quot; 주소를 통해 볼트를 접속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰값은 도커를 실행했을 때 설정했던 루트 토큰값인 &quot;myroot&quot;를 통해 접속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;2258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rS0eN/btsvl7abpkU/mk8zenLjkSXDju3bksqN5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rS0eN/btsvl7abpkU/mk8zenLjkSXDju3bksqN5K/img.png&quot; data-alt=&quot;Vault UI 접속 후, 시크릿(secret) 목록들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rS0eN/btsvl7abpkU/mk8zenLjkSXDju3bksqN5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrS0eN%2Fbtsvl7abpkU%2Fmk8zenLjkSXDju3bksqN5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;757&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;2258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vault UI 접속 후, 시크릿(secret) 목록들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속하면 초기 secret들이 있는데 새로 만들기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Enable new engine] 버튼을 누른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/60eeG/btsvqAXcc9V/O5RgocKgKCROTsCTZp4KPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/60eeG/btsvqAXcc9V/O5RgocKgKCROTsCTZp4KPK/img.png&quot; data-alt=&quot;Enable new engine 버튼 클릭 후 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/60eeG/btsvqAXcc9V/O5RgocKgKCROTsCTZp4KPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F60eeG%2FbtsvqAXcc9V%2FO5RgocKgKCROTsCTZp4KPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;452&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Enable new engine 버튼 클릭 후 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 엔진 중에서 범용 KV 를 선택하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pu1mj/btsvkM5rNM1/B6oeSqKcgZav2omOMzKg01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pu1mj/btsvkM5rNM1/B6oeSqKcgZav2omOMzKg01/img.png&quot; data-alt=&quot;enable new engine 시크릿 엔진 데이터 입력&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pu1mj/btsvkM5rNM1/B6oeSqKcgZav2omOMzKg01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpu1mj%2FbtsvkM5rNM1%2FB6oeSqKcgZav2omOMzKg01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;354&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;enable new engine 시크릿 엔진 데이터 입력&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KV 시크릿 엔진 데이터를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 시크릿 이름을 &quot;config-service-secret&quot;으로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/weMZh/btsvkTQ2KNl/D5Abo4zOCjBx6LYwKbjsl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/weMZh/btsvkTQ2KNl/D5Abo4zOCjBx6LYwKbjsl0/img.png&quot; data-alt=&quot;시크릿&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/weMZh/btsvkTQ2KNl/D5Abo4zOCjBx6LYwKbjsl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FweMZh%2FbtsvkTQ2KNl%2FD5Abo4zOCjBx6LYwKbjsl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;243&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시크릿&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 시크릿 엔진이 만들어졌다면 이제 시크릿을 생성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Create secret] 버튼을 누른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;1126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lftcG/btsvmysawx0/t4LJMlMyG3jkAsUhZTCmY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lftcG/btsvmysawx0/t4LJMlMyG3jkAsUhZTCmY0/img.png&quot; data-alt=&quot;Vault Secret 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lftcG/btsvmysawx0/t4LJMlMyG3jkAsUhZTCmY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlftcG%2Fbtsvmysawx0%2Ft4LJMlMyG3jkAsUhZTCmY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;557&quot; height=&quot;378&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;1126&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vault Secret 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 시크릿을 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시크릿 이름을 적을 때 &quot;/&quot;를 기준으로 세분화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;config-service/dev&quot;를 적고, 여기에 저장할 데이터를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 gitId와 gitPw를 적어놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 [Save] 버튼을 누르면 끝난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kp4UK/btsvkYZb9bY/6ldNAHb0TYoxpAFZUXWSY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kp4UK/btsvkYZb9bY/6ldNAHb0TYoxpAFZUXWSY0/img.png&quot; data-alt=&quot;새로운 secret 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kp4UK/btsvkYZb9bY/6ldNAHb0TYoxpAFZUXWSY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKp4UK%2FbtsvkYZb9bY%2F6ldNAHb0TYoxpAFZUXWSY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;301&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;새로운 secret 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 정상적으로 Vault 데이터를 가져오는지 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBs7Yg/btsvoYRri1K/Adp7DQZyKm1rNbZQdq6jH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBs7Yg/btsvoYRri1K/Adp7DQZyKm1rNbZQdq6jH1/img.png&quot; data-alt=&quot;Spring Cloud Config Server 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBs7Yg/btsvoYRri1K/Adp7DQZyKm1rNbZQdq6jH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBs7Yg%2FbtsvoYRri1K%2FAdp7DQZyKm1rNbZQdq6jH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;365&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Cloud Config Server 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;localhost:8888/config-service/dev&quot; 주소를 입력해서 가져오는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 저장했던 gitId와 gitPw가 가져와지는 것을 알 수 있고, 이 데이터로 Github에 접속해서 application.yml 구성정보를 가져오는 것도 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;참고로 Github에서 public repository라면 ID와 PW를 적을 필요 없이 주소(uri)만 적으면 가져올 수 있다. 여기서는 Github의 private repository에 접근하기 위해 ID와 PW를 입력해야하는 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Spring Cloud</category>
      <category>Spring Cloud Config Server</category>
      <category>Spring Cloud Config Server Vault</category>
      <category>Spring Cloud Config Vault 연동</category>
      <category>Spring Vault</category>
      <category>vault</category>
      <category>스프링 클라우드 구성 서버</category>
      <category>스프링 클라우드 구성 서버 Vault</category>
      <category>스프링 클라우드 구성 서버 볼트 연동</category>
      <category>스프링 클라우드 컨피그 서버</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/163</guid>
      <comments>https://ssdragon.tistory.com/163#entry163comment</comments>
      <pubDate>Sat, 23 Sep 2023 18:19:18 +0900</pubDate>
    </item>
    <item>
      <title>분산환경에서 DB 기본키(PK)는 어떤 ID 생성 전략으로 만들어야할까? (UUID,ULID,TSID...)</title>
      <link>https://ssdragon.tistory.com/162</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;보통 개발 편의성을 위해 Oracle의 Sequence, MySQL의 auto_increment 로 숫자를 1씩 증가시키는 것으로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 어떤 문제점이 있을까?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;외부에서 해당 시스템 PK를 예측하기 쉬워져서 &lt;b&gt;SQL Injection 문제&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Sequence, auto_increment는 중앙 집중식으로 값을 생성하는 방식이므로 &lt;b&gt;DB에 의존적이게 되어 확장성이 제한되는 문제&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;서비스 폭풍성장 시, &lt;b&gt;ID 고갈되는 문제 &lt;/b&gt;(BIGINT 최댓값은 4,294,967,295 이고, unsigned BIGINT라면 18,446,744,073,709,551,615)&lt;/li&gt;
&lt;li&gt;데이터베이스 &lt;b&gt;변경의 어려움&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;DB가 2대 이상일 때 &lt;b&gt;중복 문제&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  MySQL 5.7 이하 버전에서는 AUTO_INCREMENT counter 값을 메모리에 저장하였기에 재기동한다면 이 값이 소멸된다. 따라서 0부터 시작하게되는 문제가 발생한다.&lt;br /&gt;&lt;br /&gt;  MySQL 8.0 이후부터는 AUTO_INCREMENT counter 값을 디스크에 저장하는 방식으로 변경되었다.(redo log에 저장) 따라서 재기동되더라도 counter값이 남아있기에 중복문제가 발생하지 않는다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;분산환경에서 유일성이 보장되는 ID를 만드는 방법은 어떠한 것이 있을까?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  다중 마스터 복제(multi-master replication)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg43Vv/btssLCD25ZN/4WTbGwsX9uDqCfG4CFPaYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg43Vv/btssLCD25ZN/4WTbGwsX9uDqCfG4CFPaYK/img.png&quot; data-alt=&quot;다중 마스터 복제 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg43Vv/btssLCD25ZN/4WTbGwsX9uDqCfG4CFPaYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg43Vv%2FbtssLCD25ZN%2F4WTbGwsX9uDqCfG4CFPaYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;415&quot; height=&quot;249&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다중 마스터 복제 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB의 auto_increment 기능을 활용하고, 1씩 증가하는 것이 아닌 서버의 수 k만큼 증가시키는 방법&lt;/li&gt;
&lt;li&gt;각 DB서버가 다음에 만들 ID값은 자신이 생성한 이전 ID값에 전체 서버의 수 k를 더한 값&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 그림에서는 DB서버가 2개이므로 k = 2 가 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;규모 확장성을 어느정도 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 데이터 센터에 걸쳐 확장 어려움&lt;/li&gt;
&lt;li&gt;시간 흐름에 맞춰 ID값을 커지도록 보장할 수 없음&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;  UUID&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;UUID값 예시 : bb6266a8-09aa-4c90-b597-05f1cc958acd&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터 시스템에 저장되는 정보를 유일하게 식별하기 위한 128비트짜리 수&lt;/li&gt;
&lt;li&gt;UUID 값은 충돌 가능성이 매우 낮음 (중복 확률 0.00000000006% 인데 운석 맞을 확률임)&lt;/li&gt;
&lt;li&gt;서버 간 조율 없이 독립적으로 생성 가능&lt;/li&gt;
&lt;li&gt;알파벳은 소문자로 표현되며, 입력시 대소문자를 구분하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  장점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UUID 생성은 간단&lt;/li&gt;
&lt;li&gt;동기화 이슈 X&lt;/li&gt;
&lt;li&gt;규모 확장 쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ID값이 128비트로 길며, 용량도 큼
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대규모 테이블에서 저장공간 많이 차지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간순 정렬 X&lt;/li&gt;
&lt;li&gt;숫자가 아닌 값 포함&lt;/li&gt;
&lt;li&gt;UUID 값은 문자 단위로 비교되기 때문에, 정수와 비교하면 성능이 느림 (2.5배 ~ 28배 성능 저하)&lt;/li&gt;
&lt;li&gt;MySQL(innoDB) 인덱스는 B+Tree 구조로써 순차성에 강한데, UUID는 랜덤이므로 성능 저하가 크게 발생 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;InnoDB는 기본키(PK)의 B+Tree에 테이블 행을 저장한다.이를 클러스터형 인덱스(clustered index)라고 부른다. 클러스터형 인덱스는 기본키를 기준으로 자동으로 행의 순서를 지정한다. 그런데 무작위 UUID를 가진 행을 삽입하면 여러 문제가 발생할 수 있어 성능 저하를 초래한다. 이와 관련된 내용은 아래 참고자료를 살펴보자.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  참고자료&lt;/h4&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://www.percona.com/blog/uuids-are-popular-but-bad-for-performance-lets-discuss/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UUID는 인기가 있지만 성능에는 좋지 않습니다&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vladmihalcea.com/uuid-database-primary-key/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;데이터베이스 기본키에 가장 적합한 UUID 유형&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  ULID&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://github.com/ulid/spec&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ULID Github 링크&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNidAA/btssVG6KY9e/Hsq80ZXfK25NOvzKoF1AXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNidAA/btssVG6KY9e/Hsq80ZXfK25NOvzKoF1AXK/img.png&quot; data-alt=&quot;https://blog.daveallie.com/ulid-primary-keys&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNidAA/btssVG6KY9e/Hsq80ZXfK25NOvzKoF1AXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNidAA%2FbtssVG6KY9e%2FHsq80ZXfK25NOvzKoF1AXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;528&quot; height=&quot;173&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://blog.daveallie.com/ulid-primary-keys&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UUID와 많이 유사하며, 128bit 용량을 가짐&lt;/li&gt;
&lt;li&gt;앞의 Timestamp(타임스탬프)가 48bit 사용하므로 시간순 정렬 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기원시간인 Epoch(밀리초)로 시간을 인코딩&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;뒤의 Randomness(무작위성)은 80bit를 가짐&lt;/li&gt;
&lt;li&gt;밀리초의 정밀도를 가지며, 단순하게 증가&lt;/li&gt;
&lt;li&gt;밀리초 내에 동시 생성되면 무작위성에 따라 순서가 달라짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  장점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UUID의 단점을 해결하기 위해 만들어짐&lt;/li&gt;
&lt;li&gt;UUID의 36문자와 달리 ULID는 26문자로 인코딩됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UUID는 Base16, ULID는 Base32 문자 집합을 사용하기 때문에 ULID가 더 짧은 문자열을 가짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;특수 문자 없음 (URL 안전)&lt;/li&gt;
&lt;li&gt;대소문자 구분하지 않음&lt;/li&gt;
&lt;li&gt;시간순 정렬 가능&lt;/li&gt;
&lt;li&gt;효율성과 가독성을 위해 Crockford의 Base 32 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;I&quot;, &quot;L&quot;, &quot;O&quot;, &quot;U&quot; 제외됨&lt;/li&gt;
&lt;li&gt;문자당 5bit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;128bit로 상대적으로 용량이 큼&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Base32는 Base16보다 더 많은 정보를 한 문자에 담을 수 있기 때문에 ULID가 문자열이 26자로 표현된다. Base16은 16진수로 표현된 데이터를 나타내며, 각 문자는 4비트를 나타낸다. 128bit(16byte)의 데이터를 Base16으로 표현하면 32자의 문자열이 필요하다. 반면에 Base32는 각 문자가 5비트의 정보를 표현하고, 128bit 데이터를 26자의 문자열만으로 표현할 수 있다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  UUID는 Base16이므로 32문자로 표현할 수 있지만 특수문자인 하이픈(`-`)이 4개 추가되기 때문에 총 36문자이고, ULID는 특수문자가 없기 때문에 온전히 26문자로 표현이 가능하다. 이것이 UUID와 ULID를 구별짓는 중요한 차이 중 하나이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  티켓 서버(ticket server)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7M33i/btssBpMQxtw/mvVbNgjwjifqIK9fjQmZ21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7M33i/btssBpMQxtw/mvVbNgjwjifqIK9fjQmZ21/img.png&quot; data-alt=&quot;티켓 서버 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7M33i/btssBpMQxtw/mvVbNgjwjifqIK9fjQmZ21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7M33i%2FbtssBpMQxtw%2FmvVbNgjwjifqIK9fjQmZ21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;244&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;티켓 서버 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;auto_increment 기능을 갖춘 DB를 중앙 집중형으로 하나만 사용하는 것&lt;/li&gt;
&lt;li&gt;즉, 분산 환경에서 고유한 식별자를 생성하고 관리하기 위해 사용되는 중앙 집중식 서버를 의미&lt;/li&gt;
&lt;li&gt;티켓 서버는 다양한 클라이언트나 서비스가 동시에 접근하여 고유한 티켓(식별자)를 얻을 수 있는 장소로 사용됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  장점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유일성이 보장되는 숫자로만 구성된 ID를 쉽게 생성&lt;/li&gt;
&lt;li&gt;구현하기 쉽고, 중소 규모 애플리케이션에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;티켓 서버가 SPOF(Single-Point-of-Failure, 단일장애지점)이 됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;티켓 서버 장애 발생 시, 해당 서버를 이용하는 모든 시스템이 영향을 받음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;티켓 서버 여러 대 준비해야하는데 이러면 데이터 동기화 같은 새로운 문제 발생&lt;/li&gt;
&lt;li&gt;확장하기 어려움&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;  트위터 스노플레이크(snowflake) 접근법&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;트위터는 고가용성 방식으로 초당 수만 개의 ID를 생성할 수 있는 것이 필요했고, 이러한 ID는 대략적으로 정렬 가능해야 하며, 64bit의 용량을 가져야 한다. MySQL 기반 티켓 서버는 일종의 재 동기화 루틴을 구축해야 했고, 다양한 UUID는 128bit가 필요했다. 따라서 대략적으로 정렬된 64bit 용량을 가진 ID를 생성하기 위해 타임 스탬프(timestamp), 작업자 번호(worker number) 및 시퀀스 번호(sequence number)의 구성을 결정했다. 시퀀스 번호는 스레드별로 지정되며, 작업자 번호는 시작 시 Zookeeper를 통해 선택된다. [&lt;a href=&quot;https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;원문링크&lt;/a&gt;] [&lt;a href=&quot;https://github.com/twitter-archive/snowflake&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Github 링크&lt;/a&gt;]&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트위터(twitter)가 OSS(Open Source System, 오픈소스)로 공개한 ID 생성기(generator)&lt;/li&gt;
&lt;li&gt;Time-base 한 ID (시간대별 정렬 가능하며, 의미를 가지는 ID)&lt;/li&gt;
&lt;li&gt;확장 가능하며, 병렬로 유일성을 가진 ID 생성 가능&lt;/li&gt;
&lt;li&gt;생성해야 하는 ID 구조를 여러 영역(section)으로 분할하여 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsazK0/btssBkZfIIE/RZdKqI6lhVaH4FONLnOoMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsazK0/btssBkZfIIE/RZdKqI6lhVaH4FONLnOoMK/img.png&quot; data-alt=&quot;twitter snowflake(트위터 스노우플레이크) 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsazK0/btssBkZfIIE/RZdKqI6lhVaH4FONLnOoMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsazK0%2FbtssBkZfIIE%2FRZdKqI6lhVaH4FONLnOoMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;459&quot; height=&quot;94&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;twitter snowflake(트위터 스노우플레이크) 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  스노우플레이크 구조 설명&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사인(sign) 비트
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1bit 할당&lt;/li&gt;
&lt;li&gt;음수와 양수를 구별하는 데 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;타임스탬프(timestamp)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;41bit 할당&lt;/li&gt;
&lt;li&gt;기원 시각(epoch) 이후로 몇 밀리초(millisecond)가 경과했는지 나타내는 값&lt;/li&gt;
&lt;li&gt;41bit로 표현할 수 있는 타임스탬프 최댓값은 2^41 - 1 = 2199023255551ms&lt;/li&gt;
&lt;li&gt;최댓값은 69년이므로 이 생성기는 69년동안 정상 작동&lt;/li&gt;
&lt;li&gt;시간의 흐름에 따라 큰 값을 가지게 되므로 결국 ID는 시간순 정렬 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터센터(datacentor) ID
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;5bit 할당&lt;/li&gt;
&lt;li&gt;2^5 = 32개 데이터센터 지원 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;서버(server) ID
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;5bit 할당&lt;/li&gt;
&lt;li&gt;데이터센터당 32개 서버를 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;일련번호(sequence number)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;12bit 할당&lt;/li&gt;
&lt;li&gt;12bit이므로, 2^12 = 4096개의 값을 가질 수 있음&lt;/li&gt;
&lt;li&gt;각 서버에서는 ID 생성할 때마다 이 일련번호를 1만큼 증가&lt;/li&gt;
&lt;li&gt;일련번호는 1밀리초가 경과할 때마다 0으로 초기화(reset)&lt;/li&gt;
&lt;li&gt;즉, 1밀리초 내에 두 개 이상의 ID를 생성하지 않는 한 이 영억은 일반적으로 0 이다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;데이터센터ID와 서버ID는 시스템 시작 시 결정되며, 일반적으로 시스템 운영 중에는 바뀌지 않음.&lt;br /&gt;타임스탬프나 일련번호는 ID 생성기가 돌고 있는 중에 만들어지는 값.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  장점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스당 초당 최소 10,000개 ID 생성&lt;/li&gt;
&lt;li&gt;응답 속도 2ms (네트워크 대기 시간 추가)&lt;/li&gt;
&lt;li&gt;시계열(시간순, 날짜순) 정렬 가능&lt;/li&gt;
&lt;li&gt;UUID 128bit에 비해 64bit로 상대적으로 적으면서 많은 id 생성 가능&lt;/li&gt;
&lt;li&gt;독립적으로 ID 생성 가능하므로 분산환경에서 확장성 높음&lt;/li&gt;
&lt;li&gt;병렬로 유일한 ID 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;69년동안 사용가능함&lt;/li&gt;
&lt;li&gt;중~소규모에서 운영할 때는 trade-off를 생각해야 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sequence 또는 auto_increment에 비해 용량이 크기때문에 비용이 상대적으로 높음&lt;/li&gt;
&lt;li&gt;서비스가 폭풍성장한다면 DB구조변경, 데이터 마이그레이션, 동시성 문제같은 여러 비용을 생각한다면 미리 만드는것이 좋을수 있음&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  TSID(Time-Sorted Unique Identifier)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://github.com/f4b6a3/tsid-creator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TSID Github 링크&lt;/a&gt;)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/twitter-archive/snowflake/tree/snowflake-2010&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;twitter의 snowflake&lt;/a&gt;와 &lt;a href=&quot;https://github.com/ulid/spec&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ULID Spec&lt;/a&gt;의 아이디어를 결합한 것&lt;/li&gt;
&lt;li&gt;64bit 정수 생성 가능&lt;/li&gt;
&lt;li&gt;문자열 형식은 Crockford의 base32로 저장&lt;/li&gt;
&lt;li&gt;문자열은 URL 형식을 구분하지 않음&lt;/li&gt;
&lt;li&gt;UUID, ULID, KSUID보다 짧음&lt;/li&gt;
&lt;li&gt;13자의 문자(13 chars)로 저장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSkL6r/btssN0Tt0SV/2cUK6TmpiCYneivYSOxabK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSkL6r/btssN0Tt0SV/2cUK6TmpiCYneivYSOxabK/img.png&quot; data-alt=&quot;TSID 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSkL6r/btssN0Tt0SV/2cUK6TmpiCYneivYSOxabK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSkL6r%2FbtssN0Tt0SV%2F2cUK6TmpiCYneivYSOxabK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;280&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TSID 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  TSID 구조&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간(Time component) 42bit 와 랜덤구성요소(Random component) 22bit 로 이루어짐&lt;/li&gt;
&lt;li&gt;시간구성요소는 2020-01-01 00:00:00 UTC 이후의 밀리초 수&lt;/li&gt;
&lt;li&gt;랜덤구성요소는 노드ID(node ID)의 0~20bit와 카운터(counter)의 2~22bit로 이루어짐
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. 노드 비트가 10이면 카운터 비트는 12로 제한되는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간구성요소는 부호 있는 64bit 정수 필드에 저장되면 약 69년 동안 사용 가능&lt;/li&gt;
&lt;li&gt;시간구성요소는 부호 없는 64bit 정수 필드에 저장되면 약 139년 동안 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  장점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;64bit(8 byte)로 UUID에 비해 상대적으로 적은 용량&lt;/li&gt;
&lt;li&gt;DB에 bitint로 저장하고, Java에서 long 사용 가능&lt;/li&gt;
&lt;li&gt;UUID와 기존 정수 기반 ID 모두 활용 가능&lt;/li&gt;
&lt;li&gt;기본키를 바이트 배열 대신 읽을 수 있는 정수(64bit)로 가져옴&lt;/li&gt;
&lt;li&gt;시계열 정렬이 가능해서 DB 성능 저하가 발생하지 않음&lt;/li&gt;
&lt;li&gt;JPA와 Hibernate에서 구현되었기에 매우 간단하게 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;69년 또는 139년동안만 사용가능함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  참고자료&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/primary-keys-db-what-use-id-vs-uuid-something-else-lucas-persson/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DB의 기본키는 무엇을 사용해야 할까? ID와 UUID 외에 다른 것이 있을까?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vladmihalcea.com/tsid-identifier-jpa-hibernate/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JPA 및 Hibernate를 사용하여 TSID 엔티티 식별자를 생성하는 가장 좋은 방법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vladmihalcea.com/uuid-database-primary-key/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;데이터베이스 기본키에 가장 적합한 UUID 유형&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; &amp;zwj;  JPA &amp;amp; Hibernate 엔티티에서 사용방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 3 버전을 사용할 경우 Hibernate 6 버전을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 아래 코드를 build.gradle의 dependencies(의존성)에 추가하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(아래 코드의 맨 마지막 버전은 자기 프로젝트 호환성에 맞게 고쳐도 됨)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;implementation 'io.hypersistence:hypersistence-utils-hibernate-60:3.5.1'&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 라이브러리를 추가했다면 이제 `@Tsid` 어노테이션만 사용하면 아주 간단하게 구현이 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1693657611272&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import io.hypersistence.utils.hibernate.id.Tsid;
import jakarta.persistence.Id;

public class TestEntity {

    @Id @Tsid
    private Long id;

    private String title;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/vladmihalcea/hypersistence-utils/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Hypersistence Utils 프로젝트&lt;/a&gt;는 Spring과 Hibernate 모두를 위한 범용 유틸리티를 제공하는데 여기에 TSID가 구현되어있기 때문이다. Hypersistence Utils 3.2 버전부터 시간정렬식별자(tsid)를 제공하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 Hibernate 5에서 사용하거나 TSID 값 생성기를 커스터마이징 하고 싶으면 &lt;a href=&quot;https://vladmihalcea.com/tsid-identifier-jpa-hibernate/&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;TSID 값만 생성하는 것이 필요하다면 &lt;a href=&quot;https://github.com/f4b6a3/tsid-creator/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TSID Github&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;  결론&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부에 왠만하면 추측가능한 ID를 노출하지 않는게 좋음 (엔티티를 외부에 노출하지 않듯이)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. &quot;/users/109&quot; 보다는 &quot;/users/38352658568129975&quot; 또는 &quot;/users/01294A2BDA2&quot;&lt;/li&gt;
&lt;li&gt;외부 노출용 ID를 따로 생성해서 내부 pk와 키-값으로 연결하여 주고받거나, 내부 pk를 암호화하는 방법도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&quot;이게 제일 좋다!&quot; 라는 것은 없고 상황마다 적절한 것을 trade-off 하여 사용하여야함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. 내부적으로 사용하는 것이라면 굳이 PK를 UUID, TSID로 사용할 필요 없이 auto_increment 사용&lt;/li&gt;
&lt;li&gt;e.g. 숫자는 문자열에 비해 작은 크기를 가지므로 저장 및 검색 속도에서 좋고, 문자열은 복잡하게 구성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;분산환경에서는 UUID의 단점을 개선한 TSID, snowflake 를 사용하는 것이 좋음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 서버가 독립적으로 유일성이 보장된 ID를 생성할 수 있기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DB가 2대 이상 확장될 수 있는 가능성이 있다면 auto_increment보단 TSID, snowflake이 좋음&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 etc</category>
      <category>id generator</category>
      <category>id 생성 전략</category>
      <category>id 생성기</category>
      <category>ticket server</category>
      <category>tsid</category>
      <category>twitter snowflake</category>
      <category>ulid</category>
      <category>UUID</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/162</guid>
      <comments>https://ssdragon.tistory.com/162#entry162comment</comments>
      <pubDate>Sun, 3 Sep 2023 19:08:00 +0900</pubDate>
    </item>
    <item>
      <title>맥 m1 mysql 데이터베이스 경로(위치) 찾기</title>
      <link>https://ssdragon.tistory.com/161</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;homebrew로 mysql을 설치하고, m1 맥북이어서 예전의 경로와 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 mysql에 접속한 후 아래 2가지 쿼리 방법으로 database(데이터베이스)가 저장된 폴더 경로(위치)를 알아낼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1690859935133&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1번째
show variables like 'datadir';

# 2번째
select @@datadir;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C639k/btspFMJlHbl/0AKwRoS1ScimR5uyI2i6xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C639k/btspFMJlHbl/0AKwRoS1ScimR5uyI2i6xk/img.png&quot; data-alt=&quot;쿼리 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C639k/btspFMJlHbl/0AKwRoS1ScimR5uyI2i6xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC639k%2FbtspFMJlHbl%2F0AKwRoS1ScimR5uyI2i6xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;367&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쿼리 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;m1 맥에다가 homebrew로 설치한 나는 &quot;/opt/homebrew/var/mysql/&quot; 위치에 데이터베이스가 저장되고 있었다.&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/161</guid>
      <comments>https://ssdragon.tistory.com/161#entry161comment</comments>
      <pubDate>Tue, 1 Aug 2023 12:24:40 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 아키텍처 기초 6 - DB 규모 확장</title>
      <link>https://ssdragon.tistory.com/160</link>
      <description>&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://ssdragon.tistory.com/152&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/154&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 2 - 응답시간 개선&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 3 - 웹 계층 수평적 확장&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/157&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 4 - 데이터 센터&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/159&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 5 - 메시지 큐, 로그, 메트릭, 자동화&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;서비스 규모가 커지면 DB 부하도 같이 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 DB 확장에 대한 다음 2가지 방법을 생각해야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;수직적 규모 확장 (scale up)&lt;/li&gt;
&lt;li&gt;수평적 규모 확장 (scale out)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/weazc/btsnZ0cOLiK/FhEKrGiLri9LhoCz4xLJvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/weazc/btsnZ0cOLiK/FhEKrGiLri9LhoCz4xLJvK/img.png&quot; data-alt=&quot;수직적 규모 확장과 수평적 규모 확장&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/weazc/btsnZ0cOLiK/FhEKrGiLri9LhoCz4xLJvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fweazc%2FbtsnZ0cOLiK%2FFhEKrGiLri9LhoCz4xLJvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;349&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수직적 규모 확장과 수평적 규모 확장&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  수직적 확장&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 서버의 자원(CPU, RAM 등)을 고사양 or 더 많이 증설하는 방법&lt;/li&gt;
&lt;li&gt;하드웨어의 한계로 무한 증설 X&lt;/li&gt;
&lt;li&gt;서버 1대로 모든 트래픽 감당 X&lt;/li&gt;
&lt;li&gt;SPOF(Single Point of Failure) 위험성 증가&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;  수평적 확장&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB의 수평적 확장은 샤딩(sharding)이라고 부름&lt;/li&gt;
&lt;li&gt;더 많은 서버 추가로 성능 향상시키는 방법&lt;/li&gt;
&lt;li&gt;&lt;b&gt;샤딩&lt;/b&gt;은 &lt;u&gt;대규모 DB를 샤드(shard)라고 부르는 작은 단위로 분할하는 기술&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;모든 샤드는 같은 스키마를 사용하지만 샤드에 보관되는 데이터에는 중복 X&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B9dQv/btsn0fVnUef/3HPqCQKw5ILIv8miMRgrB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B9dQv/btsn0fVnUef/3HPqCQKw5ILIv8miMRgrB0/img.png&quot; data-alt=&quot;샤드로 분할된 DB 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B9dQv/btsn0fVnUef/3HPqCQKw5ILIv8miMRgrB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB9dQv%2Fbtsn0fVnUef%2F3HPqCQKw5ILIv8miMRgrB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;298&quot; height=&quot;245&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;샤드로 분할된 DB 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 예시는 id % 4 조건을 해시 함수로 사용하여 데이터가 보관될 샤드를 정함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;904&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfYyRQ/btsn26QVbnA/tUEH5ANmTC69cxnHTnWWR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfYyRQ/btsn26QVbnA/tUEH5ANmTC69cxnHTnWWR0/img.png&quot; data-alt=&quot;DB에 들어가는 데이터 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfYyRQ/btsn26QVbnA/tUEH5ANmTC69cxnHTnWWR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfYyRQ%2Fbtsn26QVbnA%2FtUEH5ANmTC69cxnHTnWWR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;248&quot; height=&quot;290&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;904&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DB에 들어가는 데이터 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;샤딩 전략 구현 시 샤딩 키(sharding key) 정의가 중요함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;샤딩 키는 파티션 키(partition key)라고 부름&lt;/li&gt;
&lt;li&gt;데이터가 어떻게 분산될지 정하는 하나 이상의 컬럼으로 구성됨&lt;/li&gt;
&lt;li&gt;위 그림 예시에서는 샤딩 키는 id&lt;/li&gt;
&lt;li&gt;샤딩 키로 데이터 조회나 변경을 처리하므로 효율 높일 수 있음&lt;/li&gt;
&lt;li&gt;샤딩 키는 데이터를 고르게 분할할 수 있도록 하는게 중요함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  샤딩 도입 시 생길 수 있는 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;  데이터의 재 샤딩(resharding)&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;데이터가 너무 많아져서 하나의 샤드로 감당 못할 때&lt;/li&gt;
&lt;li&gt;샤드 간 데이터 분포가 균등하지 못하여 특정 샤드만 공간 소모가 심할 때&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 2가지 상황을 샤드 소진(shard exhaustion)이라고 부르며, 이 때 재 샤딩 발생&lt;/li&gt;
&lt;li&gt;샤드 소진 시, 샤드 키를 계산하는 함수를 변경하고 데이터를 재배치 해야함&lt;/li&gt;
&lt;li&gt;안정 해시(consistent hashing) 기법 활용하여 문제 해결 가능&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;size18&quot;&gt;&lt;b&gt;&lt;b&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;유명인사(celebrity) 문제&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;핫스팟 키(hotspot key) 문제라고도 부름&lt;/li&gt;
&lt;li&gt;특정 샤드에 질의가 집중되어 서버에 과부하 생기는 문제&lt;/li&gt;
&lt;li&gt;해결하려면 특정 샤드 데이터를 분산시키거나 더 잘게 쪼개야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&amp;nbsp; 조인과 비정규화(join and de-normalization)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 DB를 여러 샤드 서버로 쪼개면 데이터 조인이 힘들어짐&lt;/li&gt;
&lt;li&gt;해결하려면 DB 비정규화하여 하나의 테이블에서 질의가 수행되도록 하는 것&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;  DB 샤딩을 적용한 아키텍처 구조&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;1156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H4kZc/btsnZX1w7K4/gAfTq4WwTXjEqx7NzPdaj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H4kZc/btsnZX1w7K4/gAfTq4WwTXjEqx7NzPdaj1/img.png&quot; data-alt=&quot;데이터베이스 샤딩을 적용한 아키텍처 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H4kZc/btsnZX1w7K4/gAfTq4WwTXjEqx7NzPdaj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH4kZc%2FbtsnZX1w7K4%2FgAfTq4WwTXjEqx7NzPdaj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;719&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;1156&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터베이스 샤딩을 적용한 아키텍처 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  지금까지 아키텍처 구조 정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 계층은 무상태 계층으로&lt;/li&gt;
&lt;li&gt;모든 계층에 다중화 도입&lt;/li&gt;
&lt;li&gt;접근이 많은 데이터는 가능한 많이 캐시할 것&lt;/li&gt;
&lt;li&gt;여러 데이터 센터를 지원하여 지리적 라우팅 할 것&lt;/li&gt;
&lt;li&gt;정적 콘텐츠는 CDN 서비스할 것&lt;/li&gt;
&lt;li&gt;데이터 계층은 샤딩을 통해 규모 확장&lt;/li&gt;
&lt;li&gt;각 계층은 독립적 서비스로 분할할 것&lt;/li&gt;
&lt;li&gt;시스템을 지속적으로 모니터링하고, 자동화 도구 활용할 것&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 etc</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/160</guid>
      <comments>https://ssdragon.tistory.com/160#entry160comment</comments>
      <pubDate>Tue, 18 Jul 2023 16:44:02 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 아키텍처 기초 5 - 메시지 큐, 로그, 메트릭, 자동화</title>
      <link>https://ssdragon.tistory.com/159</link>
      <description>&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://ssdragon.tistory.com/152&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/154&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 2 - 응답시간 개선&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 3 - 웹 계층 수평적 확장&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/157&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 4 - 데이터 센터&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/160&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 6 - 데이터베이스 규모 확장&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;기초 4에서는 전 세계에서 사용할 때 나라별로 데이터센터를 두어 지리적 라우팅으로 요청 및 응답을 처리하였다. 하지만 데이터 센터마다 데이터 동기화 문제가 있을 수 있다. 이러한 문제를 해결하기 위해 메시지 큐를 사용해보자.&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;  메시지 큐(message queue)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 큐는 메시지의 무손실(durability)을 보장하는 비동기(asynchronous) 통신을 지원하는 컴포넌트&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 메시지 큐에 보관된 메시지는 꺼낼 때까지 안전히 보관된다는 특성을 지님&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TtCtK/btsnYZKDvza/D4zlpOT3Ao0Uz7EsrGpHwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TtCtK/btsnYZKDvza/D4zlpOT3Ao0Uz7EsrGpHwk/img.png&quot; data-alt=&quot;메시지 큐 기본 아키텍처 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TtCtK/btsnYZKDvza/D4zlpOT3Ao0Uz7EsrGpHwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTtCtK%2FbtsnYZKDvza%2FD4zlpOT3Ao0Uz7EsrGpHwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;80&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메시지 큐 기본 아키텍처 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;생산자(producer) or 발행자(publisher)라고 불리는 입력 서비스가 메시지를 메시지 큐에 발행(publish)&lt;/li&gt;
&lt;li&gt;큐에는 소비자(consumer) or 구독자(subscriber)라고 불리는 서비스 혹은 서버가 연결되어 있음&lt;/li&gt;
&lt;li&gt;소비자가 메시지를 받아 그에 맞는 동작을 수행&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  메시지 큐 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 큐를 이용하면 서비스 or 서버 간 결합이 느슨해짐
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 규모 확장성이 보장되어야 하는 안정적 애플리케이션을 구성하기 좋음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;생산자는 소비자 프로세스가 다운되어도 메시지 발행 가능&lt;/li&gt;
&lt;li&gt;소비자는 생산자 서비스가 가용한 상태 아니라도 메시지 수신 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 오래 걸리는 작업을 메시지 큐에 넣고, 작업 프로세스들은 메시지 큐에서 비동기적으로 꺼내 완료시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 생산자와 소비자 서비스 규모는 각기 독립적으로 확장가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  메시지 큐로 데이터 동기화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 센터간 독립적인 DB로 데이터 불일치 문제가 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제는 데이터베이스 다중화(&lt;a href=&quot;https://netflixtechblog.com/active-active-for-multi-regional-resiliency-c47719f6685b&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;넷플릭스 다중화 참고&lt;/a&gt;)나 여러 해결방법이 있지만 메시지 큐를 이용한 방법도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMTi8T/btsnZIhp1Mv/4B1WJUqlqXpCphrepaZz5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMTi8T/btsnZIhp1Mv/4B1WJUqlqXpCphrepaZz5k/img.png&quot; data-alt=&quot;데이터 변경 시 다른 DB에 알려줘서 데이터 반영하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMTi8T/btsnZIhp1Mv/4B1WJUqlqXpCphrepaZz5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMTi8T%2FbtsnZIhp1Mv%2F4B1WJUqlqXpCphrepaZz5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;510&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터 변경 시 다른 DB에 알려줘서 데이터 반영하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  로그, 메트릭, 자동화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;로그(log)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에러 로그로 시스템 오류와 문제를 보다 쉽게 찾아낼 수 있기에 중요함&lt;/li&gt;
&lt;li&gt;에러 로그는 서버 단위 모니터링 가능&lt;/li&gt;
&lt;li&gt;로그를 단일 서비스로 모아주는 도구 활용하면 더 편리하게 검색 및 조회 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메트릭(metric)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 수치들을 시각화해서 보여주거나 그대로 알려주는 것&lt;/li&gt;
&lt;li&gt;메트릭을 잘 수집하면 사업 현황에 관한 유용한 정보를 얻거나, 현 시스템 상태를 손쉽게 파악 가능&lt;/li&gt;
&lt;li&gt;호스트 단위 메트릭 : cpu, 메모리, 디스크 I/O 등&lt;/li&gt;
&lt;li&gt;종합(aggregated) 메트릭 : DB 계층 성능, 캐시 계층 성능 등&lt;/li&gt;
&lt;li&gt;핵심 비즈니스 메트릭 : 일별 능동 사용자(daily active user), 수익(revenue), 재방문(retention) 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;자동화(automation)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 크고 복잡해지면 생산성을 높이기 위해 자동화 도구를 사용해야 함&lt;/li&gt;
&lt;li&gt;지속적 통합(CI, Continuous Integration)을 도와주는 도구를 활용하면 쉽게 문제 감지 가능&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;  메시지 큐, 로그, 메트릭, 자동화를 반영한 아키텍처 구조&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d8GgXK/btsn0EFIkLg/WkQrjONOvkqaZz2sOf8VC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d8GgXK/btsn0EFIkLg/WkQrjONOvkqaZz2sOf8VC0/img.png&quot; data-alt=&quot;메시지 큐, 로그, 메트릭, 자동화 반영된 아키텍처 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d8GgXK/btsn0EFIkLg/WkQrjONOvkqaZz2sOf8VC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd8GgXK%2Fbtsn0EFIkLg%2FWkQrjONOvkqaZz2sOf8VC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;448&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1168&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메시지 큐, 로그, 메트릭, 자동화 반영된 아키텍처 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 큐는 각 컴포넌트가 느슨히 결합될 수 있도록 하고, 결함에 대한 내성 높임
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간이 오래 걸리는 작업은 메시지 큐에 넣고, 작업 서버가 비동기적으로 꺼내서 처리함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;로그, 모니터링, 메트릭, 자동화 등을 지원하기 위한 장치 추가&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 etc</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/159</guid>
      <comments>https://ssdragon.tistory.com/159#entry159comment</comments>
      <pubDate>Mon, 17 Jul 2023 15:38:26 +0900</pubDate>
    </item>
    <item>
      <title>kafka 서버에 요청 시 TimeoutException 발생</title>
      <link>https://ssdragon.tistory.com/158</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;kafka 3.5버전&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  문제 상황&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;zookeeper 서버 실행 -&amp;gt; kafka 서버 실행 -&amp;gt; kafka 서버에 토픽이 있는지 확인 요청 -&amp;gt; TimeoutException&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2520&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjeKA9/btsmRMZVO3l/UnYPrMkMQceWAv1AIGgaR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjeKA9/btsmRMZVO3l/UnYPrMkMQceWAv1AIGgaR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjeKA9/btsmRMZVO3l/UnYPrMkMQceWAv1AIGgaR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjeKA9%2FbtsmRMZVO3l%2FUnYPrMkMQceWAv1AIGgaR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;45&quot; data-origin-width=&quot;2520&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zookeeper와 kafka 서버 둘 다 서버 기동 확인하고 포트번호 확인하였고, 그 후 카프카 서버에 토픽 확인 요청이나 생성 요청을 보낼 시에 찾지못하고 TimeoutException이 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용을 보면 &quot;Timed out waiting for a node assignment&quot;로 노드 할당 대기 시간 초과라고 되어있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카 서버에 무언가 제대로 요청이 안되었음을 직감하고 stackoverflow에 검색해보았다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;1370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bV9475/btsmO4tNiVV/XMkU35kbDNkly9Er0OU3XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bV9475/btsmO4tNiVV/XMkU35kbDNkly9Er0OU3XK/img.png&quot; data-alt=&quot;카프카 설정폴더(config)에서 server.properties 수정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bV9475/btsmO4tNiVV/XMkU35kbDNkly9Er0OU3XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbV9475%2FbtsmO4tNiVV%2FXMkU35kbDNkly9Er0OU3XK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;500&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;1370&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;카프카 설정폴더(config)에서 server.properties 수정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카 설정폴더 안에 server.properties를 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진처럼 주석이 되어있거나 없다면 &lt;b&gt;listeners=PLAINTEXT://localhost:9092&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;h2 data-ke-size=&quot;size26&quot;&gt;  server.properties의 listeners 옵션은?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 파일에 주석으로 설명이 되어있는데 이를 읽어보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnJNCV/btsmSuxZscv/PEYve99ro9V48XlBEhxyX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnJNCV/btsmSuxZscv/PEYve99ro9V48XlBEhxyX0/img.png&quot; data-alt=&quot;server.properties 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnJNCV/btsmSuxZscv/PEYve99ro9V48XlBEhxyX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnJNCV%2FbtsmSuxZscv%2FPEYve99ro9V48XlBEhxyX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;187&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;server.properties 파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;소켓 서버가 수신 대기하는(listen) 주소이다. 만약 구성되지 않은 경우, 호스트 이름(host_name)은 java.net.InetAddress.getCanonicalHostName()과 같고, PLAINTEXT 수신기 이름(listener_name)과 포트(port) 9092이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 영어글 밑에는 FORMAT과 EXAMPLE이 있는데 포맷에 주석에 나와있는 내용을 넣어보면 EXAMPLE이 된다.&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;listener_name -&amp;gt; PLAINTEXT&lt;/li&gt;
&lt;li&gt;host_name -&amp;gt; your.host.name&lt;/li&gt;
&lt;li&gt;port -&amp;gt; 9092&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 소켓 서버가 수신대기하는 주소를 저렇게 localhost:9092 로 명시함으로써 실행이 된 것이다.&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;  advertised.listeners 옵션은?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진 바로 아래에 advertised.listeners 옵션 설정도 있기에 놓치면 안될 것 같아서 위 사진에 나온 내용을 해석하여 아래에 적어본다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;브로커가 클라이언트에 알릴 Listener name, hostname, port 를 설정하지 않으면 &quot;listeners&quot; 값을 사용한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 설정안하면 위에서 설정했던 &quot;listeners&quot;를 그대로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &quot;listenres&quot; 는 내부적으로 바인딩하는 주소이고, &quot;advertised.listeners&quot;는 클라이언트(컨슈머,프로듀서)같은 외부에 노출할 주소인 것이다. 내부와 외부에 오픈할 특정 IP 주소를 별도로 두기 위해 존재하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://stackoverflow.com/questions/42998859/kafka-server-configuration-listeners-vs-advertised-listeners&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/42998859/kafka-server-configuration-listeners-vs-advertised-listeners&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://shinwusub.tistory.com/133&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://shinwusub.tistory.com/133&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/Apache Kafka</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/158</guid>
      <comments>https://ssdragon.tistory.com/158#entry158comment</comments>
      <pubDate>Sat, 8 Jul 2023 18:27:00 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 아키텍처 기초 4 - 데이터 센터</title>
      <link>https://ssdragon.tistory.com/157</link>
      <description>&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://ssdragon.tistory.com/152&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/154&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 2 - 응답시간 개선&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 3 - 웹 계층 수평적 확장&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/159&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 5 - 메시지 큐, 로그, 메트릭, 자동화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/160&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 6 - 데이터베이스 규모 확장&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;기초 3에서는 무상태 웹 계층을 가지면서 유연하고 확장성 있는 아키텍처 구조를 살펴보았다.&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;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;전 세계에서 사용한다면?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  데이터 센터&lt;/h2&gt;
&lt;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;이를 지리적 라우팅(geoDNS-routing)이라고 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;1048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd6KxE/btsmQPPQURg/yQeZeYqFKSHZiEsTIKIhX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd6KxE/btsmQPPQURg/yQeZeYqFKSHZiEsTIKIhX1/img.png&quot; data-alt=&quot;데이터 센터가 있을 때 아키텍처 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd6KxE/btsmQPPQURg/yQeZeYqFKSHZiEsTIKIhX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd6KxE%2FbtsmQPPQURg%2FyQeZeYqFKSHZiEsTIKIhX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;609&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;1048&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터 센터가 있을 때 아키텍처 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한국 사용자는 Korea DC1, 미국 사용자는 USA DC1 으로 안내됨&lt;/li&gt;
&lt;li&gt;만약 데이터 센터 하나가 장애 발생 시, 다른 데이터 센터로 모든 트래픽이 전송됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⚠️ 다중 데이터 센터 구성 시 주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;올바른 데이터 센터로 트래픽을 보내는 효과적인 방법 찾기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GeoDNS는 사용자에게서 가장 가까운 데이터센터로 트래픽을 보내야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 동기화(synchronization)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 센터마다 별도의 데이터베이스를 사용할 때, 장애 발생 시 다른 데이터 센터에 데이터가 쌓이고 원래 데이터센터에는 데이터가 없을 것임&lt;/li&gt;
&lt;li&gt;이런 상황을 막으려면 여러 데이터 센터에 걸쳐 다중화를 해야 함 (넷플릭스 다중화 참고)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;테스트와 배포
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 데이터 센터를 사용하도록 시스템이 구성된 상황일 때, 웹 사이트 or 애플리케이션을 여러 위치에서 테스트 해봐야 함&lt;/li&gt;
&lt;li&gt;자동화된 배포 도구는 모든 데이터 센터에 동일한 서비스가 설치되도록 하는 데 중요한 역할을 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 주의사항(분산 시스템의 여러 문제들)을 해결하기 위해 &amp;nbsp;&lt;b&gt;메시지 큐(message queue)&lt;/b&gt;를 사용하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 메시지 큐를 살펴보자.&lt;/p&gt;</description>
      <category>개발 etc</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/157</guid>
      <comments>https://ssdragon.tistory.com/157#entry157comment</comments>
      <pubDate>Fri, 7 Jul 2023 22:05:19 +0900</pubDate>
    </item>
    <item>
      <title>Spring Cloud Config Client에서 actuator refresh 장애</title>
      <link>https://ssdragon.tistory.com/156</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Cloud Config Client 에서는 Config Server의 변경된 설정정보를 받기위해서는 actuator refresh 방법으로 설정정보를 갱신할 수 있다. 그런데 아무리 refresh를 해도 변경된 설정정보가 갱신이 안되는 장애를 겪었다. 아무리 살펴봐도 application.yml에 잘못적은 것이 아닌데 무엇이 문제인가 생각하다가 bootstrap 라이브러리때문인 것을 알게되었다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Cloud Config Server에서 변경된 설정정보를 받으려고 Actuator refresh API 호출함&lt;/li&gt;
&lt;li&gt;refresh API 호출하였지만 설정정보가 갱신되지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  문제해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-cloud-config/docs/current/reference/html/#_spring_cloud_config_server&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Cloud Config 공식홈페이지&lt;/a&gt;에서 Spring Cloud Config Client 챕터를 살펴보다가 레거시가 되버린 bootstrap 라이브러리 때문이지 않을까 싶어서 dependencies에서 제거하니 갱신이 되었다.&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;&amp;nbsp;  정보&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 2.4 버전 이전에는 Spring Cloud Bootstrap을 통해 Spring Cloud Config Server 설정을 관리할 수 있었다. 하지만 Spring Boot 2.4 버전 이후에는 application.yml 설정파일로도 Config Server 데이터를 가져올 수 있게 되면서 더 이상 쓰지않게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 2.4 버전 이후에는 application.yml에 아래와 같이 설정하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;spring:&lt;br /&gt;&amp;nbsp; profiles:&lt;br /&gt;&amp;nbsp; &amp;nbsp; active: dev #참조하고자 하는 yml파일명 뒷부분&lt;br /&gt;&amp;nbsp; cloud:&lt;br /&gt;&amp;nbsp; &amp;nbsp; config: &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; name: ecommerce #참조하고자 하는 yml파일명 앞부분&lt;br /&gt;&amp;nbsp; config: &lt;br /&gt;&amp;nbsp; &amp;nbsp; import: optional:configserver:http://localhost:8888 # Config Server의 URI&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 ecommerce-dev.yml 파일과 ecommerce-prod.yml 파일이 있다고 할 때, 위와 같은 설정이면 ecommerce-dev.yml 파일을 Config Server로부터 가져와서 활성화하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 레거시한 방법인 bootstrap을 사용하려면 공식 홈페이지에 나와있는 대로 설정해야한다. 공식 홈페이지에서는 `spring-cloud-starter-bootstrap` 스타터를 통해 활성화 해야 한다고 적혀있다. 속성은 아래와 같이 설정한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;spring.cloud.bootstrap.enabled=true&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 bootstrap이 활성화되면 bootstrap.yml 에 적어놨던 spring.cloud.config.uri 속성을 통해 Config Server에 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 bootstrap.yml에는 아래와 같이 설정되어 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;spring:&lt;br /&gt;&amp;nbsp; cloud:&lt;br /&gt;&amp;nbsp; &amp;nbsp; config:&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; uri: http://localhost:8888&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; name: 파일명&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Spring</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/156</guid>
      <comments>https://ssdragon.tistory.com/156#entry156comment</comments>
      <pubDate>Sat, 1 Jul 2023 15:58:19 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 아키텍처 기초 3 - 웹 계층 수평적 확장</title>
      <link>https://ssdragon.tistory.com/155</link>
      <description>&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://ssdragon.tistory.com/152&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/154&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 2 - 응답시간 개선&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/157&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 4 - 데이터 센터&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/159&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 5 - 메시지 큐, 로그, 메트릭, 자동화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/160&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 6 - 데이터베이스 규모 확장&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;백엔드 아키텍처 기초 1에서는 규모에 따른 웹 계층을 수평적 확장하는 방법을 알아봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버를 Scale Out 하더라도 로드 밸런서를 통해 트래픽 분산을 시키는 방법이다.&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;웹 서버끼리 상태 정보는 어떻게 하나요?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  무상태(stateless) 웹 계층&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태 정보를 웹 계층에서 제거하고, DB 같은 지속성 저장소에 보관하고 필요할 때 가져오는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  상태 정보 의존적인 아키텍처&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버는 클라이언트(사용자)의 상태 정보를 유지하여 요청들 사이에 공유되도록 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6BEoW/btslA5zCRZO/SjwYKncfwAGFMFKegkOL4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6BEoW/btslA5zCRZO/SjwYKncfwAGFMFKegkOL4k/img.png&quot; data-alt=&quot;상태 정보 의존적인 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6BEoW/btslA5zCRZO/SjwYKncfwAGFMFKegkOL4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6BEoW%2FbtslA5zCRZO%2FSjwYKncfwAGFMFKegkOL4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;394&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상태 정보 의존적인 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 클라이언트(사용자)로부터의 요청은 항상 같은 서버로 전송되어야 하는 단점 존재
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 사용자 A가 서버 2에 요청하면 새로 로그인을 해야 함&lt;/li&gt;
&lt;li&gt;이를 위해 로드밸런서가 고정 세션(sticky session) 기능 제공&lt;/li&gt;
&lt;li&gt;하지만 고정 세션도 하나의 서버에만 트래픽이 몰릴 수 있는 문제점 존재&lt;/li&gt;
&lt;li&gt;또한, 고정 세션을 사용하면 서버 추가 및 제거도 까다로울 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  무상태 아키텍처&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 요청은 어떤 웹 서버로도 전달될 수 있음&lt;/li&gt;
&lt;li&gt;웹 서버는 상태 정보가 필요할 경우 공유 저장소에서 데이터 가져옴&lt;/li&gt;
&lt;li&gt;즉, 상태 정보는 웹 서버로부터 물리적으로 분리되어 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;1008&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYzJaO/btsluWXTuPq/kbz6cC8WCxMGgUivZRkTF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYzJaO/btsluWXTuPq/kbz6cC8WCxMGgUivZRkTF0/img.png&quot; data-alt=&quot;무상태 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYzJaO/btsluWXTuPq/kbz6cC8WCxMGgUivZRkTF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYzJaO%2FbtsluWXTuPq%2Fkbz6cC8WCxMGgUivZRkTF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;348&quot; height=&quot;481&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;1008&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;무상태 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 구조는 단순하고 안정적이며 규모 확장 쉬움&lt;/li&gt;
&lt;li&gt;공유 저장소는 RDBMS, Memcached/Redis, NoSQL 일 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동 규모 확장(autoscaling)이 지원되는 저장소 사용을 추천&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  무상태 &amp;nbsp;웹 계층이 추가된 아키텍처 설계&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;1336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nk3uk/btslvzPbMZc/MsiUSCXAc1eiAJkPdUebH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nk3uk/btslvzPbMZc/MsiUSCXAc1eiAJkPdUebH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nk3uk/btslvzPbMZc/MsiUSCXAc1eiAJkPdUebH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnk3uk%2FbtslvzPbMZc%2FMsiUSCXAc1eiAJkPdUebH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;554&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;1336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태 정보가 웹 서버들로부터 제거&lt;/li&gt;
&lt;li&gt;트래픽 양에 따라 웹 서버 추가 및 제거만 해도 자동으로 규모 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 etc</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/155</guid>
      <comments>https://ssdragon.tistory.com/155#entry155comment</comments>
      <pubDate>Tue, 27 Jun 2023 14:35:47 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 아키텍처 기초 2 - 응답시간 개선</title>
      <link>https://ssdragon.tistory.com/154</link>
      <description>&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://ssdragon.tistory.com/152&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 3 - 웹 계층 수평적 확장&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/157&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 4 - 데이터 센터&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/159&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 5 - 메시지 큐, 로그, 메트릭, 자동화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/160&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 6 - 데이터베이스 규모 확장&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;백엔드 아키텍처 기초1에서는 사용자 규모에 따른 시스템 설계를 알아봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 응답시간(Latency) 개선에 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 시간은 &lt;b&gt;캐시(Cache)&lt;/b&gt;와 &lt;b&gt;콘텐츠 전송 네트워크(CDN, Content Delivery Network)&lt;/b&gt;로 개선할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;사용자는 어떻게 웹사이트의 응답을 빨리 받아볼 수 있을까?&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  캐시&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비싼 연산 결과 or 자주 참조되는 데이터를 메모리에 올린 후, 메모리에서 응답하는 임시 저장소&lt;/li&gt;
&lt;li&gt;DB에서 조회하는 것보다 메모리에서 조회하는 것이 매우 빠름&lt;/li&gt;
&lt;li&gt;단점으로는 용량이 디스크에 비해 매우 적고, 비용이 비쌈&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  캐시 계층&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 잠시 보관되는 곳으로 DB보다 매우 빠름&lt;/li&gt;
&lt;li&gt;DB의 부하를 줄이고, 캐시 계층의 규모를 독립적으로 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 계층에 데이터가 있다면 아래 구조로 빠른 데이터를 반환받을 수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFfLl0/btslb5zKKw0/JBxt3CHANtgTPWkkOmsSMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFfLl0/btslb5zKKw0/JBxt3CHANtgTPWkkOmsSMk/img.png&quot; data-alt=&quot;캐시에 데이터가 존재할 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFfLl0/btslb5zKKw0/JBxt3CHANtgTPWkkOmsSMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFfLl0%2Fbtslb5zKKw0%2FJBxt3CHANtgTPWkkOmsSMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;494&quot; height=&quot;131&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;캐시에 데이터가 존재할 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 계층에 데이터가 없다면 DB에서 데이터를 반환받아야 함&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ODMD1/btslbprNWcR/UCKsC8SxjneSCPhmWG9Tu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ODMD1/btslbprNWcR/UCKsC8SxjneSCPhmWG9Tu0/img.png&quot; data-alt=&quot;캐시에 데이터가 없을 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ODMD1/btslbprNWcR/UCKsC8SxjneSCPhmWG9Tu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FODMD1%2FbtslbprNWcR%2FUCKsC8SxjneSCPhmWG9Tu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;125&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;캐시에 데이터가 없을 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 캐시 계층 구조처럼 캐시에 데이터가 있으면 반환하고 없으면 DB에서 반환받는 캐시 전략을 &lt;b&gt;주도형 캐시 전략(Read-Through Caching Strategy)&lt;/b&gt;라고 한다. 이 외에도 다양한 캐시 전략이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  캐시 사용 시 주의사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q. 캐시는 어떤 상황에서 사용해야하는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 데이터 갱신은 자주 발생 X, 참조는 자주 발생 O&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q. 어떤 데이터를 캐시에 두는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 영속적 보관 데이터는 X, 휘발성 데이터는 O, 따라서 중요 데이터는 지속적 저장소에 보관&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q. 캐시 데이터의 만료시간은?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 만료된 데이터는 삭제해야하며, 너무 짧으면 DB조회가 많이 발생하며, 너무 길면 원본 데이터와 차이날 가능성 존재하므로 적절하게 설정해야함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q. 일관성(consistency)은 어떻게 유지하는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 원본 데이터 갱신 연산과 캐시 갱신 연산이 단일 트랜잭션으로 처리되지 않으면 일관성이 깨질 수 있음. 따라서 Cache Invalidation, Cache Refresh, Write-Through, Read-Through, Write-Behind, Cache Validation 같은 방법들로 일관성을 유지할 수 있다.&lt;/p&gt;
&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;✅ 캐시 무효화 (Cache Invalidation)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버가 데이터 요청 시, 캐시에서 찾아보고 없으면 DB나 다른 백엔드 서비스에서 데이터를 가져와 캐시에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기(write) 요청일 경우, 웹 서버는 DB에 SQL문을 요청하고 memcache에 삭제 요청을 전송하여 오래된 데이터를 무효화시킴.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시된 데이터를 갱신하는 것보다 삭제하는 이유는 삭제는 중복적으로 수행해도 안전하기 때문이다. (멱등성)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 같은 방법을 페이스북 논문인 &lt;a href=&quot;https://users.cs.utah.edu/~stutsman/cs6963/public/papers/memcached.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Scaling Memcache at Facebook&lt;/a&gt; 에서 소개하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세계에서 가장 큰 소셜 네트워크 서비스를 운영하는 페이스북의 Memcached 활용방법을 살펴보면 많은 도움이 될 것 같다.&lt;/p&gt;
&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;Q. 장애 대응은 어떻게 할 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 캐시 서버 한 대만 두면 단일 장애 지점(Single Point of Failure) 될 가능성 존재함. 따라서 여러 지역에 걸쳐 캐시 서버를 분산해야 함.&lt;/p&gt;
&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;Q. 캐시 메모리의 크기는?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 너무 작으면 용량 초과로 장애가 나거나, 데이터가 너무 자주 밀려나서(eviction) 캐시 성능 떨어짐. 결국 과할당(overprovision)해야 여러 문제를 방지할 수 있음.&lt;/p&gt;
&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;Q. 데이터 방출(eviction) 정책은?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 캐시가 꽉 차면 기존 데이터를 방출할 정책을 정해야 한다. 보통 LRU 많이 쓰고, LFU, FIFO 등 상황에 맞게 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr;&amp;nbsp;LRU : 가장 마지막으로 사용된 데이터 방출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; LFU : 사용 빈도가 가장 낮은 데이터 방출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; FIFO : 가장 먼저 들어온 데이터 방출&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;  콘텐츠 전송 네트워크 (CDN)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 콘텐츠를 전송하는 데 쓰이는, 지리적으로 분산된 서버의 네트워크&lt;/li&gt;
&lt;li&gt;이미지, 비디오, CSS, JavaScript 파일 등 캐시 가능&lt;/li&gt;
&lt;li&gt;사용자가 웹 사이트 방문 시, 지리적으로 가장 가까운 CDN 서버가 정적 콘텐츠를 제공함&lt;/li&gt;
&lt;li&gt;물리적 거리가 가까울수록 속도가 빠름&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2068&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CN6PG/btslgi7sr2y/7Fc88hoKy2Eay7mmtWBFF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CN6PG/btslgi7sr2y/7Fc88hoKy2Eay7mmtWBFF0/img.png&quot; data-alt=&quot;지리적 거리에 따른 속도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CN6PG/btslgi7sr2y/7Fc88hoKy2Eay7mmtWBFF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCN6PG%2Fbtslgi7sr2y%2F7Fc88hoKy2Eay7mmtWBFF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;200&quot; data-origin-width=&quot;2068&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;지리적 거리에 따른 속도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;미국 서버를 한국에서 접근하면 느리기에 한국 CDN서버로 빠른 응답 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u40mf/btslhce67tp/cAZTo3Gw7hEZzFXMkhyUXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u40mf/btslhce67tp/cAZTo3Gw7hEZzFXMkhyUXk/img.png&quot; data-alt=&quot;CDN 동작 방식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u40mf/btslhce67tp/cAZTo3Gw7hEZzFXMkhyUXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu40mf%2Fbtslhce67tp%2FcAZTo3Gw7hEZzFXMkhyUXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;221&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CDN 동작 방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자A가 이미지 파일 접근&lt;/li&gt;
&lt;li&gt;CDN 서버의 캐시에 이미지가 없으면 원본 서버에서 가져옴&lt;/li&gt;
&lt;li&gt;원본 서버가 파일을 CDN 서버에 반환&lt;/li&gt;
&lt;li&gt;CDN 서버는 파일을 캐시하고 사용자A에게 반환&lt;/li&gt;
&lt;li&gt;사용자B가 이미지 파일 접근&lt;/li&gt;
&lt;li&gt;만료되지 않은 이미지 요청은 CDN 캐시를 통해 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  CDN 사용 시 주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;비용&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CDN은 제3 사업자에 의해 운영되며, 데이터 전송량에 따라 비용 발생&lt;/li&gt;
&lt;li&gt;따라서 자주 사용되는 콘텐츠만 캐싱해서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적절한 만료 시한 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시처럼 짧으면 서버와 자주 접속하고, 길면 일관성 문제 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CDN 장애 대응&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CDN 서버 장애 시 원본 서버에서 정적 컨텐츠 제공하도록 시스템 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컨텐츠 무효화 방법 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CDN 서비스 사업자가 제공하는 API로 컨텐츠 무효화&lt;/li&gt;
&lt;li&gt;컨텐츠의 다른 버전을 서비스하도록 오브젝트 버저닝(object versioning) 이용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  캐시와 CDN이 추가된 아키텍처 설계&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;1314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mE5xt/btsljTGvxw3/9YuWf3wTfSFBXFNv33tCD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mE5xt/btsljTGvxw3/9YuWf3wTfSFBXFNv33tCD1/img.png&quot; data-alt=&quot;캐시와 CDN 서버가 추가된 아키텍처 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mE5xt/btsljTGvxw3/9YuWf3wTfSFBXFNv33tCD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmE5xt%2FbtsljTGvxw3%2F9YuWf3wTfSFBXFNv33tCD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;571&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;1314&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;캐시와 CDN 서버가 추가된 아키텍처 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 컨텐츠(JS, CSS, 이미지 등)은 웹 서버가 아닌 CDN에서 제공하여 응답속도 개선&lt;/li&gt;
&lt;li&gt;캐시를 통해 DB의 부하를 줄이고 응답속도 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;가상 면접 사례로 배우는 대규모 시스템 설계 기초 | 알렉스 쉬&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발 etc</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/154</guid>
      <comments>https://ssdragon.tistory.com/154#entry154comment</comments>
      <pubDate>Mon, 26 Jun 2023 13:09:36 +0900</pubDate>
    </item>
    <item>
      <title>AWSome Day 온라인 컨퍼런스 후기</title>
      <link>https://ssdragon.tistory.com/153</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;편하게 듣고 정리할 생각은 없었는데 메일을 보니 참석 증명서를 보내주길래 적게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;받아보면 성취감과 AWS의 좋은 감정이 생겨서 글로써 남기고 싶어졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;1010&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oz8h9/btskRoM1xzn/am7i7qdN7q88SvmKL7LoO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oz8h9/btskRoM1xzn/am7i7qdN7q88SvmKL7LoO0/img.png&quot; data-alt=&quot;AWSome Day 온라인 컨퍼런스 참석 증명서(23.06.08)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oz8h9/btskRoM1xzn/am7i7qdN7q88SvmKL7LoO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foz8h9%2FbtskRoM1xzn%2Fam7i7qdN7q88SvmKL7LoO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;496&quot; height=&quot;409&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;1010&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWSome Day 온라인 컨퍼런스 참석 증명서(23.06.08)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 서비스를 제공하는 여러 회사들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;Azure&lt;/li&gt;
&lt;li&gt;Google Cloud&lt;/li&gt;
&lt;li&gt;Oracle Cloud&lt;/li&gt;
&lt;li&gt;네이버 클라우드&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 가장 많은 점유율을 가지고 있는 AWS가 궁금했었는데 마침 AWS 온라인 컨퍼런스 신청을 받고 있길래 냅다 신청하고 들었다. 온라인 강의처럼 3시간동안 듣는 형식이라 큰 부담이 없었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 클라우드 개념&lt;/li&gt;
&lt;li&gt;AWS 핵심 서비스 설명&lt;/li&gt;
&lt;li&gt;AWS 사용 사례&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 내용으로 이루어져 있어서 입문자가 듣기 아주 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  강연 목록&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2390&quot; data-origin-height=&quot;1512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRKLeN/btskNexTVg1/8snV4FP7Ay8KlXxwp0avw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRKLeN/btskNexTVg1/8snV4FP7Ay8KlXxwp0avw0/img.png&quot; data-alt=&quot;AWSome Day 온라인 컨퍼런스 강연 목록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRKLeN/btskNexTVg1/8snV4FP7Ay8KlXxwp0avw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRKLeN%2FbtskNexTVg1%2F8snV4FP7Ay8KlXxwp0avw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;441&quot; data-origin-width=&quot;2390&quot; data-origin-height=&quot;1512&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWSome Day 온라인 컨퍼런스 강연 목록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  AWS 클라우드 소개&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3188&quot; data-origin-height=&quot;1494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KdqHE/btskQjZ6sYQ/DKFbWw1zvAKcGw9Q0BcgKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KdqHE/btskQjZ6sYQ/DKFbWw1zvAKcGw9Q0BcgKK/img.png&quot; data-alt=&quot;온프레미스와 클라우드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KdqHE/btskQjZ6sYQ/DKFbWw1zvAKcGw9Q0BcgKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKdqHE%2FbtskQjZ6sYQ%2FDKFbWw1zvAKcGw9Q0BcgKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;286&quot; data-origin-width=&quot;3188&quot; data-origin-height=&quot;1494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;온프레미스와 클라우드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;온프레미스는 데이터 센터를 직접 구축, 소유, 운영하는 것&lt;/li&gt;
&lt;li&gt;클라우드는 사용한 만큼만 비용 지불하는 방식으로, 직접 데이터 센터를 구축 및 운영 필요가 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  클라우드 컴퓨팅의 장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 컴퓨팅에서는 인프라를 더 이상 하드웨어라고 생각하지 않고 소프트웨어라고 생각하고 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선행 비용을 가변 비용으로 대체&lt;/li&gt;
&lt;li&gt;속도 및 민첩성 향상&lt;/li&gt;
&lt;li&gt;규모의 경제로 얻게 되는 이점&lt;/li&gt;
&lt;li&gt;데이터 센터 운영 및 유지 관리에 비용 투자 불필요&lt;/li&gt;
&lt;li&gt;용량 추정 불필요&lt;/li&gt;
&lt;li&gt;몇 분 만에 전 세계에 배포&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✏️ AWS 핵심 인프라 및 서비스&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3280&quot; data-origin-height=&quot;1564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uLIFE/btskK0Ux9z2/mdsYweKjeopXeNChmaaiJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uLIFE/btskK0Ux9z2/mdsYweKjeopXeNChmaaiJK/img.png&quot; data-alt=&quot;AWS 핵심 인프라 및 서비스(우측)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uLIFE/btskK0Ux9z2/mdsYweKjeopXeNChmaaiJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuLIFE%2FbtskK0Ux9z2%2FmdsYweKjeopXeNChmaaiJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;327&quot; data-origin-width=&quot;3280&quot; data-origin-height=&quot;1564&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS 핵심 인프라 및 서비스(우측)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좌측은 온프레미스 환경에서 서비스를 운영하기 위한 구조&lt;/li&gt;
&lt;li&gt;우측은 클라우드 환경에서 서비스를 운영하기 위한 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✏️ AWS 주요 서비스 영역&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3286&quot; data-origin-height=&quot;1632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cV1Z97/btskK2riyVz/FfrOOjVZck4v1aQzKRQt2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cV1Z97/btskK2riyVz/FfrOOjVZck4v1aQzKRQt2K/img.png&quot; data-alt=&quot;AWS 주요 서비스 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cV1Z97/btskK2riyVz/FfrOOjVZck4v1aQzKRQt2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcV1Z97%2FbtskK2riyVz%2FFfrOOjVZck4v1aQzKRQt2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;330&quot; data-origin-width=&quot;3286&quot; data-origin-height=&quot;1632&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS 주요 서비스 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.kico.co.kr/2022/03/08/aws-vpc/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;VPC&lt;/a&gt; : 논리적으로 할당된 가상 네트워크 공간
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2 : 서버 가상머신&lt;/li&gt;
&lt;li&gt;EBS : 서버에 대한 영구적인 블록 스토리지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;S3 : 거의 모든 종류의 데이터를 저장 및 검색할 수 있는 객체 스토리지&lt;/li&gt;
&lt;li&gt;DynamoDB : 빠르고 유연한 NoSQL 데이터베이스 서비스&lt;/li&gt;
&lt;li&gt;Lambda : 서버를 구축할 필요 없이 코드 실행할 수 있는 이벤트 중심의 서버리스 컴퓨팅 서비스&lt;/li&gt;
&lt;li&gt;SageMaker : 기계 학습 모델을 구축, 훈련, 배포하는 완전관리형 서비스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  느낀점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에는 정말 다양한 서비스를 제공하고 있어서 다른 클라우드 서비스가 필요가 없다. 하지만 개인이 사용하기에는 아주 치명적인 단점이 있다. 그것은 &lt;b&gt;&quot;비용&quot;&lt;/b&gt;이다. &lt;u&gt;Oracle Cloud 같은 경우는 프리티어로 사용해도 한계점을 넘으면 서비스가 중단되고 과금되지 않지만&lt;/u&gt;, &lt;b&gt;&lt;u&gt;AWS는 프리티어 용량을 넘기면 과금되는 구조로써 가장 치명적인 단점&lt;/u&gt;&lt;/b&gt;이다. 이 때문에 AWS를 쉽게 접근하지 못하는 개발자들이 꽤나 있는걸로 알고 있고, 또는 프리티어를 사용하다가 무료 제공되지 않는 서비스나 용량을 넘겨 과금되는 경우가 있어 치를 떠는 개발자도 있었다. 그렇기에 이런 입문자용 컨퍼런스나 강의가 가장 필요하며 AWS가 널리 알려줘야한다고 생각한다. (과금 요소 없애는게 가장 좋지만...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 핵심 서비스를 이해하는데 도움됨&lt;/li&gt;
&lt;li&gt;VPC나 보안 등 알기 힘들었던 부분에 대해서도 너무 좋았음&lt;/li&gt;
&lt;li&gt;입문자용 컨퍼런스가 있다면 다음에도 들을 생각이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 AWS에 대해 하나씩 듣다보면 자격증에도 관심이 자연스레 생길 듯 싶다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://aws.amazon.com/ko/events/awsome-day/awsome-day-online/?trkCampaign=awsome-day-online&amp;amp;trk=a58c5b54-3057-4869-b956-1ec715f07fe1&amp;amp;sc_channel=em&amp;amp;mkt_tok=MTEyLVRaTS03NjYAAAGMen1DvsDodY2bbsoInjl_MIvH-LTqAshrT2fyVzilhy_4_ooYvxMCVsxz97yHU8jh90o1BJ4Cc0H5RN-i0y0Ng2XKhDqwjm2wdqvEsrRlNohjcdfcVUxutg&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWSOME DAY 온라인 컨퍼런스 공식 홈페이지&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://velog.io/@j3ss83/AWSome-Day-온라인-컨퍼런스-강연-내용-정리-참여-후기&quot;&gt;https://velog.io/@j3ss83/AWSome-Day-온라인-컨퍼런스-강연-내용-정리-참여-후기&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://blog.kico.co.kr/2022/03/08/aws-vpc/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.kico.co.kr/2022/03/08/aws-vpc/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발 etc</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/153</guid>
      <comments>https://ssdragon.tistory.com/153#entry153comment</comments>
      <pubDate>Wed, 21 Jun 2023 15:12:59 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 아키텍처 설계 기초</title>
      <link>https://ssdragon.tistory.com/152</link>
      <description>&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://ssdragon.tistory.com/154&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 2 - 응답시간 개선&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 3 - 웹 계층 수평적 확장&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/157&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 4 - 데이터 센터&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/159&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 5 - 메시지 큐, 로그, 메트릭, 자동화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ssdragon.tistory.com/160&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 아키텍처 기초 6 - 데이터베이스 규모 확장&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;[클라이언트]-[서버]-[데이터베이스] 구조로만 생각했었다. 지금은 MSA와 여러 아키텍처에 대한 학습을 하다보니 어떤 식으로 설계를 해야하는가에 대한 고민이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 큐(message queue)는 어느 환경에서 사용해야할까?&lt;/li&gt;
&lt;li&gt;트래픽이 증가하면 단일 서버가 장애나지 않을까?&lt;/li&gt;
&lt;li&gt;현재 프로젝트를 멀티 서버로 했을 때의 문제점은?&lt;/li&gt;
&lt;li&gt;DB가 하나일 때 장애가 난다면 서비스를 빠르게 복구하기 힘들지 않은가?&lt;/li&gt;
&lt;li&gt;DB보다 빠른 캐시 서버의 도입은?&lt;/li&gt;
&lt;li&gt;MSA에서 각 서비스마다의 통신은 어떻게?&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;&lt;u&gt;사용자 수, 트래픽, 비용 등 여러 변수들로 각각의 상황마다 전부 다른 아키텍처 설계를 적절히 해야하기 때문이다.&lt;/u&gt;&lt;/b&gt; 현재 상황보다 이상적인 아키텍처 설계를 한다면 오버 엔지니어링 문제로 비용이 상당히 커질 수도 있다. 그렇다면 어떻게 유연하고 가용성있는 아키텍처 구조를 설계할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;가장 단순한 단일 서버에서부터 생각해보자&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  단일 서버&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹, DB, 캐시 등 모든 것이 서버 한 대에서 실행되는 간단한 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPBQGK/btsknuNT4DY/UPTuKOUQ2n6xxn29b6QSr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPBQGK/btsknuNT4DY/UPTuKOUQ2n6xxn29b6QSr0/img.png&quot; data-alt=&quot;단일 서버 아키텍처 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPBQGK/btsknuNT4DY/UPTuKOUQ2n6xxn29b6QSr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPBQGK%2FbtsknuNT4DY%2FUPTuKOUQ2n6xxn29b6QSr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;482&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;단일 서버 아키텍처 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자는 웹 브라우저에 URL 입력&lt;/li&gt;
&lt;li&gt;URL은 DNS(Domain Name Service)에서 IP 주소로 변환되어 반환&lt;/li&gt;
&lt;li&gt;반환된 IP 주소로 HTTP 요청&lt;/li&gt;
&lt;li&gt;요청 받은 웹 서버는 HTML 또는 JSON 형태의 응답을 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;사용자가 늘어난다면 서버를 여러대 두어야 하나요?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;  데이터베이스 분리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹/모바일 트래픽 처리 서버(웹 계층)와 데이터베이스 서버(데이터 계층)를 분리시킨 구조&lt;/li&gt;
&lt;li&gt;용도에 따른 분리로 각각 독립적으로 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZJwbl/btskuPDWUqP/fPnTmNDdMhiBYIWkm2ORY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZJwbl/btskuPDWUqP/fPnTmNDdMhiBYIWkm2ORY1/img.png&quot; data-alt=&quot;웹 계층과 데이터 계층 분리 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZJwbl/btskuPDWUqP/fPnTmNDdMhiBYIWkm2ORY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZJwbl%2FbtskuPDWUqP%2FfPnTmNDdMhiBYIWkm2ORY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;438&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;웹 계층과 데이터 계층 분리 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 브라우저는 www.naver.com을 요청하여 웹 서버에서 HTML을 반환받음&lt;/li&gt;
&lt;li&gt;모바일 앱은 api.naver.com을 요청하여 웹 서버를 거쳐서 데이터베이스에서 데이터를 반환 받음&lt;/li&gt;
&lt;li&gt;단순 사용자 증가라면 정적 콘텐츠 반환 트래픽이 증가하니 웹 서버를 증가&lt;/li&gt;
&lt;li&gt;사용자가 데이터를 많이 사용한다면 데이터베이스 서버를 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;서버를 증가시키는 방법만 있을까? NO&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 수직적 규모 확장(scale up)과 수평적 규모 확장(scale out)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수직적 규모 확장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버의 사양(CPU, RAM 등)을 고사양으로 추가하는 행위&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수평적 규모 확장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버를 추가해서 트래픽을 분산시켜 성능 개선하는 행위&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방식으로 웹 서버나 데이터베이스 서버의 성능을 개선할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 수직적 규모 확장의 경우 아래와 같은 단점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확장의 한계로, 고사양 자원을 무한대로 증설할 방법 X&lt;/li&gt;
&lt;li&gt;장애 대응 X, 자동복구나 다중화 방안이 없어서 장애 발생 시 완전 중단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;위와 같은 단점으로 대규모 트래픽 서비스같은 경우 수평적 규모 확장법을 사용한다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;Scale Out을 한다면 어떻게 요청을 분산시키나요?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  로드 밸런서&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로드밸런서는 부하 분산 집합(Load Balancing Set)에 속한 웹 서버들에게 트래픽 부하를 고르게 분산시킴&lt;/li&gt;
&lt;li&gt;수평적 규모 확장으로 웹 서버 증설 후, 로드밸런서로 트래픽 부하를 분산시키는 구조
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;하나의 웹 서버에 많은 사용자가 몰릴 때의 문제점을 해결&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p4hZC/btskllRejE8/zF1U1EhHaDLqF9JHoY4JoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p4hZC/btskllRejE8/zF1U1EhHaDLqF9JHoY4JoK/img.png&quot; data-alt=&quot;로드 밸런서 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p4hZC/btskllRejE8/zF1U1EhHaDLqF9JHoY4JoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp4hZC%2FbtskllRejE8%2FzF1U1EhHaDLqF9JHoY4JoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;512&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로드 밸런서 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 공개 IP 주소(Public IP Address)로 접속하고, 로드밸런서는 적절히 사설 IP 주소(Private IP Address)를 이용하여 요청 처리함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 사설 IP 주소를 모르기에 보안적인 측면에서도 좋음&lt;/li&gt;
&lt;li&gt;또한 사설 IP 주소는 인터넷을 통해 접속 X&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;서버 1이 장애 시, 모든 트래픽은 서버 2로 처리&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;웹 계층은 트래픽 처리가 되지만 데이터베이스는 어떡하나요?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  데이터베이스 다중화&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 계층에서 DB 장애 및 부하 문제를 &lt;b&gt;데이터베이스 다중화&lt;/b&gt;를 통해 해결할 수 있음&lt;/li&gt;
&lt;li&gt;데이터베이스 다중화는 &lt;u&gt;서버 사이에 주(Master)-부(Slave) 관계를 설정하여 데이터 원본은 주 서버, 사본은 부 서버에 저장하는 방식&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;주 서버(Master)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쓰기 연산(Write Operation)&lt;/li&gt;
&lt;li&gt;데이터 원본&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;부 서버(Slave)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;읽기 연산(Read Operation)&lt;/li&gt;
&lt;li&gt;데이터 사본&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;대부분 애플리케이션의 경우에 읽기 연산 비중이 훨씬 높기에 부 서버가 많음&lt;/li&gt;
&lt;li&gt;부 서버가 전부 장애 발생 시, 주 서버가 읽기 연산을 처리함&lt;/li&gt;
&lt;li&gt;주 서버가 장애 발생 시, 부 서버 중 하나가 새로운 주 서버가 됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부 서버의 데이터가 최신 상태가 아닐 가능성이 있음&lt;/li&gt;
&lt;li&gt;최신 상태가 아니거나 없는 데이터가 있을 시, 복구 스크립트로 추가해야 함&lt;/li&gt;
&lt;li&gt;이런 문제를 해결하려면 다중 마스터(Multi-Masters), 원형 다중화(Circular Replication) 방식을 생각할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/evaCeo/btskgt3QWjW/kExmpRrp4aOLMHGjXO8FtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/evaCeo/btskgt3QWjW/kExmpRrp4aOLMHGjXO8FtK/img.png&quot; data-alt=&quot;데이터베이스 다중화 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/evaCeo/btskgt3QWjW/kExmpRrp4aOLMHGjXO8FtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FevaCeo%2Fbtskgt3QWjW%2FkExmpRrp4aOLMHGjXO8FtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;551&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터베이스 다중화 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 변경 연산은 Master 에만 전달하고, 읽기 연산은 Slave 들로 분산하여 병렬로 처리될 수 있는 질의(Query)의 수가 늘어나므로 성능이 좋아짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;안정성(Reliability)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 중 하나가 파괴 및 장애가 나도 데이터 보존 가능&lt;/li&gt;
&lt;li&gt;데이터를 지역적으로 떨어진 여러 장소에 다중화 가능하기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp;가용성(Availability)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 여러 지역에 복제함으로써, 하나의 DB에 장애 발생하더라도 다른 서버의 데이터를 가져와서 계속 서비스 할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;로드밸런서와 데이터베이스 다중화를 고려한 구조는 어떻게 생겼나요?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  로드밸런서와 데이터베이스 다중화를 고려한 설계&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;1328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wsVGU/btsklmjOXyI/bcU6YaQ5H59EMR59Hnoz90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wsVGU/btsklmjOXyI/bcU6YaQ5H59EMR59Hnoz90/img.png&quot; data-alt=&quot;로드밸런서와 데이터베이스 다중화 아키텍처 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wsVGU/btsklmjOXyI/bcU6YaQ5H59EMR59Hnoz90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwsVGU%2FbtsklmjOXyI%2FbcU6YaQ5H59EMR59Hnoz90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;594&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;1328&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로드밸런서와 데이터베이스 다중화 아키텍처 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자는 DNS로부터 로드밸런서의 공개 IP 주소 받음&lt;/li&gt;
&lt;li&gt;사용자는 해당 IP 주소를 사용하여 로드밸런서 접속&lt;/li&gt;
&lt;li&gt;HTTP 요청은 서버 1 or 서버 2로 전달&lt;/li&gt;
&lt;li&gt;웹 서버는 사용자의 데이터를 Slave DB에서 읽고, 데이터 변경 연산은 Master DB로 전달&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 가장 기본적으로 서버를 유연하고 확장시킬 수 있는 아키텍처 구조를 살펴보았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 계층과 데이터 계층 분리&lt;/li&gt;
&lt;li&gt;각 계층마다 수평적 규모 확장(Scale Out)&lt;/li&gt;
&lt;li&gt;Scale Out으로 추가된 웹 서버들은 로드밸런서로 트래픽 분산&lt;/li&gt;
&lt;li&gt;데이터베이스 다중화로 부하 분산&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 구조를 만들려면 클라우드 서비스를 이용하는 것이 가장 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 AWS가 가장 인기있고 점유율이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;현재 아키텍처 구조가 가장 이상적인 설계안일까요? NO&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;응답시간을 개선할 수 있어요!&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답시간을 개선하기 위한 방법은 다음 게시글을 통해 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;가상 면접 사례로 배우는 대규모 시스템 설계 기초 | 알렉스 쉬&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발 etc</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/152</guid>
      <comments>https://ssdragon.tistory.com/152#entry152comment</comments>
      <pubDate>Mon, 19 Jun 2023 16:54:17 +0900</pubDate>
    </item>
    <item>
      <title>로컬에서 동일한 SpringBoot 서버를 여러개 실행하는 방법</title>
      <link>https://ssdragon.tistory.com/151</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링부트 1개의 서버를 로컬에서 여러개 띄워야하는 경우에는 여러 방법을 통해 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  IDE(통합개발환경) 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntelliJ(인텔리제이)에서는 서버 옵션을 편하게 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 인텔리제이 2022.2.3(Ultimate edition) 버전을 사용하고 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 수동으로 포트번호 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S22ju/btsisRjZSKz/8kWXmlVmc1OTKqkokadRI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S22ju/btsisRjZSKz/8kWXmlVmc1OTKqkokadRI0/img.png&quot; data-alt=&quot;위 사진처럼 구성 편집(Edit Configurations...) 을 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S22ju/btsisRjZSKz/8kWXmlVmc1OTKqkokadRI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS22ju%2FbtsisRjZSKz%2F8kWXmlVmc1OTKqkokadRI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;437&quot; height=&quot;205&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;482&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 사진처럼 구성 편집(Edit Configurations...) 을 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;1430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPqISf/btsisRRUoJf/9gpJTqMxKzLun2drlTdrQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPqISf/btsisRRUoJf/9gpJTqMxKzLun2drlTdrQk/img.png&quot; data-alt=&quot;서버를 복사 후 VM 옵션 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPqISf/btsisRRUoJf/9gpJTqMxKzLun2drlTdrQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPqISf%2FbtsisRRUoJf%2F9gpJTqMxKzLun2drlTdrQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;416&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;1430&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버를 복사 후 VM 옵션 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이 버전마다 다르지만 내가 사용하는 버전에서는 [옵션 수정] 버튼을 눌려서 VM 옵션을 추가해야 보인다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2304&quot; data-origin-height=&quot;1646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNjwn5/btsiqfNphfb/2hoRiKo0gZ3jq2OHpqd2N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNjwn5/btsiqfNphfb/2hoRiKo0gZ3jq2OHpqd2N0/img.png&quot; data-alt=&quot;VM 옵션에 위 사진처럼 적기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNjwn5/btsiqfNphfb/2hoRiKo0gZ3jq2OHpqd2N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNjwn5%2FbtsiqfNphfb%2F2hoRiKo0gZ3jq2OHpqd2N0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;630&quot; height=&quot;450&quot; data-origin-width=&quot;2304&quot; data-origin-height=&quot;1646&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VM 옵션에 위 사진처럼 적기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;-Dserver.port=포트번호&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VM 옵션에서 [포트번호적기]에 원하는 포트번호를 적으면 해당 포트번호로 서버가 실행되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &quot;-Dserver.port=1234&quot; 를 적었으면 1234 포트번호로 서버를 실행시킨다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 복제한 것에 이름은 바꿔주고 [적용] - [확인] 을 눌려준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgJEKo/btsiqgS7xIo/jTg1W8jwLh1Bq2qSkM3l91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgJEKo/btsiqgS7xIo/jTg1W8jwLh1Bq2qSkM3l91/img.png&quot; data-alt=&quot;서버 실행하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgJEKo/btsiqgS7xIo/jTg1W8jwLh1Bq2qSkM3l91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgJEKo%2FbtsiqgS7xIo%2FjTg1W8jwLh1Bq2qSkM3l91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;334&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 실행하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 실행은 평소와 똑같이하고 중지시키는 것도 똑같은 위치로 들어가서 중단시키면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 자동으로 포트번호 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ssdragon.tistory.com/150&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ssdragon.tistory.com/150&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1685690989866&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;SpringBoot 랜덤포트로 실행하기&quot; data-og-description=&quot;  상황 1개의 SpringBoot 서버를 여러개로 실행해야 함 따라서 포트번호를 서버마다 다르게 지정해야 함 수동으로 지정하는 것은 매번 관리하는 것이 어려움   해결 방법 server: port: 0 application.ym&quot; data-og-host=&quot;ssdragon.tistory.com&quot; data-og-source-url=&quot;https://ssdragon.tistory.com/150&quot; data-og-url=&quot;https://ssdragon.tistory.com/150&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zNGTD/hySQyo9LKl/v6erstor8TOJzXhucYXsI0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/6Ywyd/hySQLWlPL1/uGg0w2mEKH9eiGyxLqkqD1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://ssdragon.tistory.com/150&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ssdragon.tistory.com/150&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zNGTD/hySQyo9LKl/v6erstor8TOJzXhucYXsI0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/6Ywyd/hySQLWlPL1/uGg0w2mEKH9eiGyxLqkqD1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SpringBoot 랜덤포트로 실행하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  상황 1개의 SpringBoot 서버를 여러개로 실행해야 함 따라서 포트번호를 서버마다 다르게 지정해야 함 수동으로 지정하는 것은 매번 관리하는 것이 어려움   해결 방법 server: port: 0 application.ym&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ssdragon.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용에서 나온 것처럼 스프링부트 서버를 랜덤포트로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2304&quot; data-origin-height=&quot;1646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0wyLC/btsismrspyp/psBiRxMEycK0g6E7bokQQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0wyLC/btsismrspyp/psBiRxMEycK0g6E7bokQQK/img.png&quot; data-alt=&quot;VM 옵션 삭제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0wyLC/btsismrspyp/psBiRxMEycK0g6E7bokQQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0wyLC%2Fbtsismrspyp%2FpsBiRxMEycK0g6E7bokQQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;489&quot; data-origin-width=&quot;2304&quot; data-origin-height=&quot;1646&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VM 옵션 삭제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수동으로 설정했던 VM 옵션부분을 삭제한 후 동일하게 실행시키면 개발자는 편하게 실행시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Gradle 사용하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9RAWc/btsisxlVAs1/QHU3LXmIOQ3POt2htOdA6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9RAWc/btsisxlVAs1/QHU3LXmIOQ3POt2htOdA6K/img.png&quot; data-alt=&quot;스프링부트 프로젝트 폴더&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9RAWc/btsisxlVAs1/QHU3LXmIOQ3POt2htOdA6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9RAWc%2FbtsisxlVAs1%2FQHU3LXmIOQ3POt2htOdA6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;242&quot; height=&quot;202&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스프링부트 프로젝트 폴더&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링부트 프로젝트 폴더에 있는 gradlew 를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이는 터미널을 바로 사용할 수 있으므로 해당 IDE에서 gradlew를 통해 실행시켜본다.&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;1. 수동으로 포트번호 설정하기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5344&quot; data-origin-height=&quot;2888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yviWz/btsiop3iTTg/WlIU7OR01lL7akRiAAT6Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yviWz/btsiop3iTTg/WlIU7OR01lL7akRiAAT6Lk/img.png&quot; data-alt=&quot;터미널에서 gradlew 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yviWz/btsiop3iTTg/WlIU7OR01lL7akRiAAT6Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyviWz%2Fbtsiop3iTTg%2FWlIU7OR01lL7akRiAAT6Lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;374&quot; data-origin-width=&quot;5344&quot; data-origin-height=&quot;2888&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;터미널에서 gradlew 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;./gradlew bootRun --args='--serverport=포트번호'&lt;/blockquote&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;2. 자동으로 포트번호 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트번호를 랜덤포트로 설정했다면 아래 명령어만 입력하여 실행시키면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;./gradlew bootRun&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  maven 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradle이 아니라 maven 프로젝트라면 아래 명령어를 터미널에서 사용하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;mvn spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver-port=포트번호'&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 실패한다면 jar 파일이 없거나 설정 문제이므로 아래 명령어로 다시 패키징하여 실행해보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;mvn clean compile package&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것 또한 랜덤포트로 설정해놨다면 아래 명령어로 서버를 실행하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;mvn spring-boot:run&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  java 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링부트 프로젝트를 jar로 패키징하고, jar을 java로 실행시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히는 JRE가 있어야하는데 개발자의 경우 JDK로 보통 설치해놨으므로 바로 실행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(JDK에 JRE 포함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQsxBP/btsimDAQj9j/5t7z0WvvBTmWa6Q3tB9iN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQsxBP/btsimDAQj9j/5t7z0WvvBTmWa6Q3tB9iN0/img.png&quot; data-alt=&quot;gradle 프로젝트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQsxBP/btsimDAQj9j/5t7z0WvvBTmWa6Q3tB9iN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQsxBP%2FbtsimDAQj9j%2F5t7z0WvvBTmWa6Q3tB9iN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;219&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gradle 프로젝트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gradle 프로젝트라면 위 사진 경로에 jar 파일이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;없다면 아래 명령어를 실행하여 jar 파일 만든다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;./gradlew bootJar&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zvddC/btsirTpnoxq/go0RKljeeyavvZKx3MDLWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zvddC/btsirTpnoxq/go0RKljeeyavvZKx3MDLWk/img.png&quot; data-alt=&quot;maven 프로젝트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zvddC/btsirTpnoxq/go0RKljeeyavvZKx3MDLWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzvddC%2FbtsirTpnoxq%2Fgo0RKljeeyavvZKx3MDLWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;621&quot; height=&quot;297&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;maven 프로젝트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maven 프로젝트라면 위 사진 경로에 jar 파일이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;없다면 아래 명령어를 실행하여 jar 파일 만든다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;mvn package&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 아래 명령어를 통해 서버를 실행한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;java -jar &quot;-Dserver.port=포트번호&quot; ./build/libs/파일.jar&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보면 알 수 있듯이 공백을 기준으로 맨 마지막에는 jar파일 경로를 적어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;랜덤포트라서 VM 옵션을 추가하지 않아도 되면 아래 명령어처럼 입력하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;java -jar ./build/libs/파일.jar&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Spring</category>
      <category>SpringBoot 서버 여러개 띄우기</category>
      <category>동일한 서버 여러개 띄우기</category>
      <category>로컬에서 스프링부트 여러개 실행하기</category>
      <category>스프링부트 서버 여러개 실행하기</category>
      <category>스프링부트 여러개 서버 띄우기</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/151</guid>
      <comments>https://ssdragon.tistory.com/151#entry151comment</comments>
      <pubDate>Fri, 2 Jun 2023 17:05:15 +0900</pubDate>
    </item>
    <item>
      <title>SpringBoot 랜덤포트로 실행하기</title>
      <link>https://ssdragon.tistory.com/150</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  상황&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1개의 SpringBoot 서버를 여러개로 실행해야 함&lt;/li&gt;
&lt;li&gt;따라서 포트번호를 서버마다 다르게 지정해야 함&lt;/li&gt;
&lt;li&gt;수동으로 지정하는 것은 매번 관리하는 것이 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  해결 방법&lt;/h2&gt;
&lt;pre id=&quot;code_1684135008440&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 0&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;application.yml 또는 application.properties 에서 SpringBoot 서버 포트를 랜덤으로 사용하겠다고 설정&lt;/li&gt;
&lt;li&gt;위 예시코드는 yml&lt;/li&gt;
&lt;li&gt;이러면 포트가 중복되어도 해당 포트를 우회하여 다른 값을 설정하게 됨&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring/Spring</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/150</guid>
      <comments>https://ssdragon.tistory.com/150#entry150comment</comments>
      <pubDate>Mon, 15 May 2023 16:19:03 +0900</pubDate>
    </item>
    <item>
      <title>선착순 쿠폰 발급을 위한 redis 분산락</title>
      <link>https://ssdragon.tistory.com/149</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;선착순 쿠폰 발급을 위한 백엔드 시스템을 구현한 코드는 &lt;a href=&quot;https://github.com/Sangyong-Jeon/coupon_fifo-concurrency_issue&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브&lt;/a&gt;에서 볼 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  요구사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멀티 서버&lt;/li&gt;
&lt;li&gt;선착순 쿠폰 발급&lt;/li&gt;
&lt;li&gt;중복 발급 X&lt;/li&gt;
&lt;li&gt;짧은 시간 대용량 트래픽 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  구현 기술스택&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Language : Java 11&lt;/li&gt;
&lt;li&gt;Framework : Spring Boot 2.7.8&lt;/li&gt;
&lt;li&gt;Database : MySQL 8.0, JPA, QueryDSL, Redis&lt;/li&gt;
&lt;li&gt;API Documentation : Swagger 3.0.0&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  해결 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. DB Exclusive Lock(배타적 잠금)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n0CnU/btsezEco7Lo/cebDXaCEE6742fpSsvaJa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n0CnU/btsezEco7Lo/cebDXaCEE6742fpSsvaJa0/img.png&quot; data-alt=&quot;회원쿠폰 발급 로직&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n0CnU/btsezEco7Lo/cebDXaCEE6742fpSsvaJa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn0CnU%2FbtsezEco7Lo%2FcebDXaCEE6742fpSsvaJa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;94&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원쿠폰 발급 로직&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vmfh3/btseBdFmJoj/ovOwOXZxndw0JkKJLfFa80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vmfh3/btseBdFmJoj/ovOwOXZxndw0JkKJLfFa80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vmfh3/btseBdFmJoj/ovOwOXZxndw0JkKJLfFa80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVmfh3%2FbtseBdFmJoj%2FovOwOXZxndw0JkKJLfFa80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;465&quot; height=&quot;336&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1110&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;JPA Pessimistic Lock(비관적 락)을 이용하여 DB에 배타적 잠금 사용&lt;/li&gt;
&lt;li&gt;다른 트랜잭션에서 읽기, 수정, 삭제 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcR1eL/btseHX8NefR/SPurnQrEKEBWbVw1pm9kv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcR1eL/btseHX8NefR/SPurnQrEKEBWbVw1pm9kv0/img.png&quot; data-alt=&quot;SQL 배타적 잠금&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcR1eL/btseHX8NefR/SPurnQrEKEBWbVw1pm9kv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcR1eL%2FbtseHX8NefR%2FSPurnQrEKEBWbVw1pm9kv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;277&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SQL 배타적 잠금&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SQL을 보면 select for update 구문을 사용하는 것을 볼 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1683700370362&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({@QueryHint(name = &quot;javax.persistence.lock.timeout&quot;, value = &quot;5000&quot;)})
Optional&amp;lt;Coupon&amp;gt; findLockById(Long id);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배타적 잠금은 JpaRepository에 위 코드처럼 작성하여 사용 가능&lt;/li&gt;
&lt;li&gt;다른 트랜잭션이 대기하는 시간을 @QueryHints로 설정&lt;/li&gt;
&lt;li&gt;대기시간이 지나면 LockTimeoutException 발생&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. Redis Distribution Lock(분산 락)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btjWFA/btseAbnAGMj/1JmddifCc4G8oCUQeVrx01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btjWFA/btseAbnAGMj/1JmddifCc4G8oCUQeVrx01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btjWFA/btseAbnAGMj/1JmddifCc4G8oCUQeVrx01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtjWFA%2FbtseAbnAGMj%2F1JmddifCc4G8oCUQeVrx01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;76&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VviDz/btseATUXFSJ/7UBCFn68Owkq6E1QLCbSg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VviDz/btseATUXFSJ/7UBCFn68Owkq6E1QLCbSg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VviDz/btseATUXFSJ/7UBCFn68Owkq6E1QLCbSg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVviDz%2FbtseATUXFSJ%2F7UBCFn68Owkq6E1QLCbSg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;364&quot; height=&quot;282&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;636&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;배타적 잠금에서는 해당 쿠폰 레코드를 Lock 했기에, 해당 쿠폰을 사용하는 다른 서비스에서 데드락 걸릴 가능성 존재&lt;/li&gt;
&lt;li&gt;따라서 Redis를 이용한 분산락으로 변경하여 해결하였음&lt;/li&gt;
&lt;li&gt;Redis Client는 대표적으로 Redisson과 Lettuce가 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lettuce : Spin Lock을 사용함. 이는 계속 Lock을 얻으려고 시도하기에 많은 부하 발생&lt;/li&gt;
&lt;li&gt;Redisson : Pub-Sub 기반으로 분산 락 제공함. 따라서 Lettuce보다 부하가 적음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Pub-Sub(&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;Publisher-Subscriber)&lt;/span&gt;이란?&lt;/b&gt;&lt;br /&gt;메세지 큐 시스템으로 볼 수 있음. Pub을 구독한 Sub들에게 메세지를 알려주는 것. 따라서 여기서는 Pub이 Lock 점유를 끝냈으면 Sub에게 끝냈다고 알려주기에 Sub은 Lock을 얻으려고 시도하지 않음.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1. 코드 구현&lt;/h4&gt;
&lt;pre id=&quot;code_1683799009508&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.redisson:redisson-spring-boot-starter:3.17.0'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683799060867&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public CreateMemberCouponResponse createMemberCoupon(CreateMemberCouponCommand command) {
    RLock lock = redissonClient.getLock(couponLockName);

    try {
        if (!lock.tryLock(10, 3, TimeUnit.SECONDS)) {
            throw new RuntimeException(&quot;Lock 획득 실패&quot;);
        }
		
        // Lock 획득했으므로 아래부터는 비즈니스 로직 처리
        if (isNotStock(command.getCouponId())) {
            throw new CouponNotRemainException();
        }

        if (isDuplicateCoupon(command)) {
            throw new DuplicateCouponException();
        }

        updateCouponStatePort.decreaseRemainQuantity(command.getCouponId());
        MemberCoupon memberCoupon = createMemberCouponPort.createMemberCoupon(command.getMemberId(), command.getCouponId());
        return new CreateMemberCouponResponse(memberCoupon.getId(), command.getCouponId(), memberCoupon.getCreateDateTime());
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        if (lock != null &amp;amp;&amp;amp; lock.isLocked()) {
            lock.unlock();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2666&quot; data-origin-height=&quot;1482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GSb8z/btseRhGWvCX/U5KVpxZmU1BdEcfdfASaeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GSb8z/btseRhGWvCX/U5KVpxZmU1BdEcfdfASaeK/img.png&quot; data-alt=&quot;왼쪽 : 배타적 잠금, 오른쪽 : Redis 분산락 적용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GSb8z/btseRhGWvCX/U5KVpxZmU1BdEcfdfASaeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGSb8z%2FbtseRhGWvCX%2FU5KVpxZmU1BdEcfdfASaeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;383&quot; data-origin-width=&quot;2666&quot; data-origin-height=&quot;1482&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;왼쪽 : 배타적 잠금, 오른쪽 : Redis 분산락 적용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 코드와 비교하기위해 사진도 첨부했다. Redis 분산락을 얻기 위한 getLock()과 tryLock()이 생기면서 try/catch 문이 생겨서 코드의 복잡성이 증가했다. 해당 코드는 &lt;a href=&quot;https://github.com/Sangyong-Jeon/coupon_fifo-concurrency_issue/commit/ba92e2e9f5511cb32a847e13c5f2bc65726bc0ab#diff-2156e1d41e211f3160cab507a992c9fc40f9b881229ad590649238eaa6cea477&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;  끝내면서&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트래픽이 적고, 다른 서비스에서 해당 쿠폰을 많이 사용하지 않으며, &lt;u&gt;다른 라이브러리를 의존하지 않고&lt;/u&gt; 끝내려면 배타적 잠금을 사용&lt;/li&gt;
&lt;li&gt;트래픽이 많고, 다른 서비스에서 해당 쿠폰을 많이 사용한다면 Redis 분산락 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 방법으로는 대기열 아키텍처 방식도 있다. 대규모 동시 요청이 들어온다면 Redis 분산락도 많은 부하가 생길 수 있다. 따라서 대기열을 만들어 처리하는 방법이 있다. 이 대기열 방식은 콘서트나 티켓 예매 사이트와 아래 사이트를 참고하여 알게되었는데 나중에 한번 만들어보고 싶다.&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://dev.gmarket.com/46&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;지마켓 대기열 시스템&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://xetown.com/topics/1708137&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;접속대기 시스템&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=MTSn93rNPPE&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;우아한형제들 선착순 이벤트 서버 생존기! 47만 RPM에서 살아남다?!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev-jj.tistory.com/entry/%ED%94%84%EB%A1%9C%EB%AA%A8%EC%85%98%EC%9D%84-%EB%8C%80%EB%B9%84%ED%95%9C-%EB%8C%80%EA%B8%B0%EC%97%B4-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0-Redis-WebSocket-Spring?category=828965&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로모션을 대비한 대기열 시스템 구성하기(Redis, WebSocket, Spring)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;참고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://redis.io/docs/manual/patterns/distributed-locks/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Redis 공식문서&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Spring/Spring</category>
      <category>redis</category>
      <category>Redis 분산락</category>
      <category>레디스</category>
      <category>레디스 분산락</category>
      <category>쿠폰 발급 redis</category>
      <category>쿠폰 발급 분산락</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/149</guid>
      <comments>https://ssdragon.tistory.com/149#entry149comment</comments>
      <pubDate>Thu, 11 May 2023 19:03:02 +0900</pubDate>
    </item>
    <item>
      <title>Swagger 3.0 documentationPluginsBootstrapper 에러 해결하기</title>
      <link>https://ssdragon.tistory.com/148</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  에러 발생 원인&lt;/h2&gt;
&lt;pre id=&quot;code_1683088889430&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 에러는 SpringBoot 2.6 버전 이후 spring.mvc.pathmatch.matching-strategy 값이 ant_path_matcher에서 path_pattern_parser로 변경되면서 발생하는 오류이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 아래와 같이 application.yml 또는 application.properties를 고치면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1683089094273&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher&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;이렇게 몇달전에 해결하여 Swagger3.0을 잘 사용하였고, 현재 다시 프로젝트를 실행해보니 똑같은 에러가 나오기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 해당 swagger 라이브러리를 제공하고 있는 springfox의 깃허브에 들어가서 관련 이슈를 찾아보니 여러 사람들이 겪고 있던 문제여서 해결방법이 있었다.&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;1. application.yml을 수정한다&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;h3 data-ke-size=&quot;size23&quot;&gt;2. IntelliJ IDEA를 사용하고 있다면 캐시 무효화/다시시작을 한다&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KbIJ1/btsdJgJe0J1/cwmHKCncKYWCi6a9Jk1TuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KbIJ1/btsdJgJe0J1/cwmHKCncKYWCi6a9Jk1TuK/img.png&quot; data-alt=&quot;인텔리제이 캐시 무효화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KbIJ1/btsdJgJe0J1/cwmHKCncKYWCi6a9Jk1TuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKbIJ1%2FbtsdJgJe0J1%2FcwmHKCncKYWCi6a9Jk1TuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;326&quot; height=&quot;407&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;972&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인텔리제이 캐시 무효화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 한글버전의 인텔리제이여서 캐시 무효화로 나와있지만 영어로는 Invalidate Cache/Restart 라고 되어있을 것이다.&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. WebMvcEndpointHandlerMapping 스프링 빈 설정하기&lt;/h3&gt;
&lt;pre id=&quot;code_1683089364037&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier,
                                                                     ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes,
                                                                     CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
    List&amp;lt;ExposableEndpoint&amp;lt;?&amp;gt;&amp;gt; allEndpoints = new ArrayList&amp;lt;&amp;gt;();
    Collection&amp;lt;ExposableWebEndpoint&amp;gt; webEndpoints = webEndpointsSupplier.getEndpoints();
    allEndpoints.addAll(webEndpoints);
    allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
    allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
    String basePath = webEndpointProperties.getBasePath();
    EndpointMapping endpointMapping = new EndpointMapping(basePath);
    boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
    return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}

private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
    return webEndpointProperties.getDiscovery().isEnabled()
            &amp;amp;&amp;amp; (StringUtils.hasText(basePath)
            || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드는 SpringBoot Actuator의 WebMvcEndpointHandlerMapping 빈을 정의하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Bean은 Actuator 엔드포인트를 Spring Web MVC 요청 매핑에 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 해결 방법이 내 코드를 크게 수정하지 않고 Swagger 에러를 해결할 수 있는 방법이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 여러 해결방법들이 있었지만 이 것으로 해결되었기에 여기까지만 작성하고 이 방법들은 springfox 깃허브 이슈에서 확인하였으므로 아래 참고 URL을 보길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://github.com/springfox/springfox/issues/3791&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/springfox/springfox/issues/3791&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/예외,에러</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/148</guid>
      <comments>https://ssdragon.tistory.com/148#entry148comment</comments>
      <pubDate>Wed, 3 May 2023 13:57:33 +0900</pubDate>
    </item>
    <item>
      <title>[Clean Code] 3장 함수</title>
      <link>https://ssdragon.tistory.com/147</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  들어가면서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금껏 개발하면서 함수(메서드)를 어떻게 만들어야할지 많은 고민을 했었다. SRP(단일책임원칙)을 지켜야하므로 함수는 한 가지 책임만 가지게 만들어 유지보수성을 높여야한다고 하지만 생각과는 다르게 개발이 되었던 경우가 많았다. 예전에 읽었던 책을 빠르게 읽어보면서 다시끔 클린코드를 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 프로그램이든 가장 기본적인 단위가 함수이다. 아래 질문을 가지고 이번 장을 살펴보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;함수가 읽기 쉽고 이해하기 쉬운 이유는 무엇일까?&lt;/li&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;의도를 분명히 표현하는 함수를 어떻게 구현할 수 있을까?&lt;/li&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;함수에 어떤 속성을 부여해야 처음 읽는 사람이 프로그램 내부를 직관적으로 파악할 수 있을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✏️ #1, 작게 만들어라!&lt;/h2&gt;
&lt;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;blockquote data-ke-style=&quot;style2&quot;&gt;함수가 작을수록 더 좋다는 증거는 없지만, 100줄 넘는 함수보다 10줄의 함수가 더욱 파악하기 쉽다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 작게 만들라는 규칙이 가장 중요하다고 생각된다. 작게 만들기위해서는 각 함수가 SRP를 지키며 명확하게 작성해야하기 때문이다. 그래서 이야기를 풀듯이 개발할 수 있다.&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-1, 블록과 들여쓰기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;if문, else문, while문 등에 들어가는 블록은 한 줄이어야 한다&lt;/li&gt;
&lt;li&gt;블록 안에서 호출하는 함수 이름을 적절히 짓는다면, 코드를 이해하기 쉬움&lt;/li&gt;
&lt;li&gt;중첩 구조가 생길만큼 함수가 커져서는 안된다&lt;/li&gt;
&lt;li&gt;들여쓰기 수준은 1~2단을 넘어서면 안된다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 규칙을 지킨다면 함수는 읽고 이해하기 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;들여쓰기는 가독성에서도 중요한 부분이다. if문 3중 중첩을 본적이 있는가? 정말 힘들다. 처음 개발할 당시에도 이런식으로 작성했지만 추후 유지보수 때 파악하기 힘들어 주석을 남겼었는데 지금 생각해보면 주석을 써야할만큼 복잡했다는 의미가 된다. 우아한 테크코스에서도 들여쓰기는 2단을 넘기지 말아야 한다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✏️ #2, 한 가지만 해라!&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수는 한 가지를 해야 한다&lt;/li&gt;
&lt;li&gt;그 한 가지를 잘 해야 한다&lt;/li&gt;
&lt;li&gt;그 한 가지만을 해야 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;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;✏️ #3, 함수 당 추상화 수준은 하나로!&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수가 '한 가지' 작업만 하려면 함수 내 모든 문장이 추상화 수준이 동일해야 한다&lt;/li&gt;
&lt;li&gt;추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다&lt;/li&gt;
&lt;li&gt;특정 표현이 근본 개념인지 아니면 세부사항인지 구분하기 어렵기 때문이다&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;`getHtml()` 은 추상화가 가장 높고, `PathParser.render(pagepath);`는 중간이고, `.append(&quot;\n&quot;)`는 가장 낮음&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  #3-1, 위에서 아래로 코드 읽기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드는 위에서 아래로 이야기처럼 읽혀야 좋음&lt;/li&gt;
&lt;li&gt;핵심은 짧으면서도 '한 가지'만하는 함수&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;✏️ #4, 서술적인 이름을 사용하라!&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수가 작고 단순할수록 서술적인 이름을 고르기 쉬워짐&lt;/li&gt;
&lt;li&gt;&quot;코드를 읽으면서 짐작했던 기능을 그대로 수행한다면 깨끗한 코드&quot;&lt;/li&gt;
&lt;li&gt;길고 서술적인 이름이 짧고 어려운 이름보다 좋음&lt;/li&gt;
&lt;li&gt;길고 서술적인 이름이 길고 서술적인 주석보다 좋음&lt;/li&gt;
&lt;li&gt;이름을 붙일 때는 일관성이 있어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✏️ #5, 함수 인수&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수에서 이상적인 인수 개수는 0개이며, 다음은 1개(단항), 다음은 2개(이항)이다&lt;/li&gt;
&lt;li&gt;3개(삼항) 이상은 가능한 피하자&lt;/li&gt;
&lt;li&gt;인수가 적을수록 이해하기 더 쉽다&lt;/li&gt;
&lt;li&gt;입력 인수를 변환하는 함수라면 변환 결과는 반환값으로 돌려주자&lt;/li&gt;
&lt;li&gt;함수에 플래그 인수(boolean)을 넘기는 것은 좋지 않음&lt;/li&gt;
&lt;li&gt;이항 함수는 위험이 따른다는 것을 이해하고 가능하면 단항 함수로 변경하는 것이 좋음&lt;/li&gt;
&lt;li&gt;삼항 함수는 이항 함수보다 순서, 무시 등 문제가 2배 늘어나는 것을 생각하자&lt;/li&gt;
&lt;li&gt;인수가 2~3개 이상 필요하다면 독자적인 클래스를 만들어보자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. `makeCircle(double x, double y, double radius)` -&amp;gt; `makeCircle(Point center, double radius)`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수의 의도나 인수의 순서와 의도를 제대로 표현하려면 좋은 함수 이름이 필수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`write(name)` : 이름이 무엇이든 쓴다&lt;/li&gt;
&lt;li&gt;`writeField(name)` : 이름이 필드 이름이라는 것을 알 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수 이름에 키워드를 추가해보자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`assertEquals(expected, actual)`보다 `assertExpectedEqualsActual(expected, actual)`를 작성하면 인수의 순서를 기억할 필요가 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✏️ #6, 부수 효과를 일으키지 마라!&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수는 한 가지 일만 해야하는데 여러 작업을 하게되면 부수 효과가 일어남&lt;/li&gt;
&lt;li&gt;부수 효과로 숨겨진 경우에는 혼란이 매우 커짐&lt;/li&gt;
&lt;li&gt;예를 들면, 비밀번호 검증 함수인데 실패하면 세션 정보를 지워버리는 부수효과를 만들면 예상치 못한 혼란을 초래할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✏️ #7, 오류 코드보다 예외를 사용하라!&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되어 깔끔해짐&lt;/li&gt;
&lt;li&gt;try/catch 블록은 좋지않으며, 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞어서 좋지않으므로 블록을 별도 함수로 뽑아내자&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;✏️ #8, 반복하지 마라!&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복은 문제&lt;/li&gt;
&lt;li&gt;코드 길이가 늘며, 알고리즘 변경 시 여러 곳을 손봐야하며, 한 곳을 빠뜨리면 오류 발생&lt;/li&gt;
&lt;li&gt;중복을 없애면 가독성 크게 높아짐&lt;/li&gt;
&lt;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;✏️ #9, 함수를 어떻게 짜죠?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소프트웨어를 개발하는 것은 글짓기와 비슷함&lt;/li&gt;
&lt;li&gt;먼저 생각을 기록하고 읽기 좋게 다듬는다&lt;/li&gt;
&lt;li&gt;초안은 서투르고 어수선하므로 원하는 대로 읽힐 때까지 다듬고 고치고 정리함&lt;/li&gt;
&lt;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;  마치면서&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;진짜 목표는 &quot;시스템이라는 이야기를 풀어가는 것&quot;을 명심하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성하는 함수가 분명하고 정확한 언어로 깔끔하게 맞아떨어지면 이야기를 풀기 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Clean Code / 로버트 C.마틴 지음 / 박재호, 이해영 옮김 / 인사이트&lt;/p&gt;</description>
      <category>도서</category>
      <category>Cleancode</category>
      <category>클린코드</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/147</guid>
      <comments>https://ssdragon.tistory.com/147#entry147comment</comments>
      <pubDate>Thu, 27 Apr 2023 17:53:37 +0900</pubDate>
    </item>
    <item>
      <title>Decorator(데코레이터) 패턴이란?</title>
      <link>https://ssdragon.tistory.com/146</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  데코레이터 패턴이란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체에 추가 요소를 동적으로 더할 수 있는 패턴&lt;/li&gt;
&lt;li&gt;데코레이터를 사용하면 서브클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;966&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVglwE/btsaqPVsDNo/8SEocQJTPLEhbkpT7kGJ5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVglwE/btsaqPVsDNo/8SEocQJTPLEhbkpT7kGJ5k/img.png&quot; data-alt=&quot;데코레이터 패턴 대표적인 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVglwE/btsaqPVsDNo/8SEocQJTPLEhbkpT7kGJ5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVglwE%2FbtsaqPVsDNo%2F8SEocQJTPLEhbkpT7kGJ5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;453&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;966&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데코레이터 패턴 대표적인 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 그림에서는 구성 요소의 형식으로 Component를 추상 클래스로 만들었는데, 데코레이터 패턴에서는 특정한 추상 구성 요소를 지정하지 않기 때문에 인터페이스를 사용해도 된다. 따라서 기존의 코드 구조가 추상 클래스면 추상 클래스로 진행하고, 아니라면 인터페이스로 만들면 된다. &lt;b&gt;여기서는 추상 클래스로 생각하고 진행하겠다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✏️ Component&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 기능을 뜻하는 추상 클래스 or 인터페이스&lt;/li&gt;
&lt;li&gt;데코레이터들은 이 클래스를 상속하고 있기에 서로를 감쌀 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✏️ ConcreteComponent&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 기능(Component)을 구현(정의)하는 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✏️ Decorator&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Component를 상속한 클래스들을 장식하는 기능에 대한 인터페이스 or 추상클래스&lt;/li&gt;
&lt;li&gt;데코레이터에는 구성 요소(Component)의 레퍼런스를 포함한 인스턴스 변수가 있음&lt;/li&gt;
&lt;li&gt;데코레이터는 자신이 장식할 구성 요소와 같은 인터페이스 또는 추상 클래스 상속&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✏️ ConcreteDecoratorA, ConcreteDecoratorB&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데코레이터는 Component 상태를 확장할 수 있음 (Component에 동적으로 추가될 수 있는 추가 행동을 정의)&lt;/li&gt;
&lt;li&gt;데코레이터가 감싸고 있는 Component 객체용 인스턴스 변수가 있음&lt;/li&gt;
&lt;li&gt;데코레이터가 새로운 메서드를 추가할 수 있지만, 일반적으로 Component에 있던 메서드를 별도의 작업으로 처리해서 새로운 기능을 추가함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  예시 코드 작성해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커피 주문 서비스를 위해 커피를 데코레이터 패턴으로 만들어보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;904&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blZkh2/btsa6cPJ4iy/4kp8NzDvtmMs0SHhq9el2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blZkh2/btsa6cPJ4iy/4kp8NzDvtmMs0SHhq9el2k/img.png&quot; data-alt=&quot;커피 데코레이터 패턴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blZkh2/btsa6cPJ4iy/4kp8NzDvtmMs0SHhq9el2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblZkh2%2Fbtsa6cPJ4iy%2F4kp8NzDvtmMs0SHhq9el2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;619&quot; height=&quot;417&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;904&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;커피 데코레이터 패턴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커피 종류마다 구성 요소를 나타내는 구상 클래스 (HouseBlend, Decaf, Espresso)&lt;/li&gt;
&lt;li&gt;각각의 추가 요소를 나타내는 데코레이터 (Milk, Mocha, Whip)&lt;/li&gt;
&lt;li&gt;데코레이터 패턴은 데코레이터로 감싸는 객체의 형식과 같으므로 CondimentDecorator가 Beverage를 상속받음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구성 요소가 들어갈 자리에 자기가 들어갈 수 있어야하기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✏️ Beverage 추상 클래스&lt;/h3&gt;
&lt;pre id=&quot;code_1681797320698&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public abstract class Beverage {

    String description;

    public String getDescription() {
        return description;
    }

    public abstract int cost();
}&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;✏️ Espresso 구현 클래스&lt;/h3&gt;
&lt;pre id=&quot;code_1681797387901&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Espresso extends Beverage {

    public Espresso() {
        description = &quot;에스프레소&quot;;
    }

    @Override
    public int cost() {
        return 1000;
    }
}&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;description은 Beverage로부터 상속받는 인스턴스 변수&lt;/li&gt;
&lt;li&gt;구현 클래스에서는 cost()를 구현해줌&lt;/li&gt;
&lt;li&gt;나머지 HouseBlend와 Decaf 클래스도 동일&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;✏️ CondimentDecorator 추상 클래스&lt;/h3&gt;
&lt;pre id=&quot;code_1681797467134&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public abstract class CondimentDecorator extends Beverage {

    Beverage beverage;

    public abstract String getDescription();
}&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;각 데코레이터가 감쌀 음료를 나타내는 Beverage 객체를 인스턴수 변수인 beverage에 저장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커피를 지정할 때는 데코레이터에서 어떤 커피든 감쌀 수 있도록 Beverage 슈퍼클래스 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모든 추가 요소 데코레이터에 getDescription()을 새로 구현하도록 만들기 위해 추상 메서드 추가&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;✏️&amp;nbsp;Mocha 구현 클래스&lt;/h3&gt;
&lt;pre id=&quot;code_1681797586730&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Mocha extends CondimentDecorator {

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public int cost() {
        return beverage.cost() + 500;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + &quot;, 모카&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mocha 생성자는 Beverage 레퍼런스가 들어와있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 데코레이터는 커피에서 추가적으로 넣는 요소이기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;cost()에는 커피 가격에 모카를 추가한 가격을 구현&lt;/li&gt;
&lt;li&gt;getDescription()에는 커피 이름과 아이템 이름도 같이 구현&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;✏️&amp;nbsp;테스트해보기&lt;/h3&gt;
&lt;pre id=&quot;code_1681797801246&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Beverage espresso = new Espresso();
System.out.println(espresso.getDescription() + &quot; : &quot; + espresso.cost());
// 에스프레소 : 1000

Beverage decaf = new Decaf();
decaf = new Mocha(decaf);
decaf = new Whip(decaf);
System.out.println(decaf.getDescription() + &quot; : &quot; + decaf.cost());
// 디카페인, 모카, 휘핑 : 2500

Beverage houseBlend = new HouseBlend();
houseBlend = new Milk(houseBlend);
houseBlend = new Milk(houseBlend);
houseBlend = new Whip(houseBlend);
System.out.println(houseBlend.getDescription() + &quot; : &quot; + houseBlend.cost());
// 하우스 블렌드 커피, 우유, 우유, 휘핑 : 3000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 &lt;a href=&quot;https://github.com/Sangyong-Jeon/design-patterns/tree/main/src/main/java/com/example/designpatterns/decorator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 클릭하면 나옴&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래밍/디자인패턴</category>
      <category>Decorator Pattern</category>
      <category>Decorator 패턴</category>
      <category>Design Pattern</category>
      <category>데코레이터 패턴</category>
      <category>디자인 패턴</category>
      <category>디자인패턴</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/146</guid>
      <comments>https://ssdragon.tistory.com/146#entry146comment</comments>
      <pubDate>Tue, 18 Apr 2023 15:12:23 +0900</pubDate>
    </item>
    <item>
      <title>오라클 클라우드 MFA 분실로 인한 로그인 문제</title>
      <link>https://ssdragon.tistory.com/145</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스마트폰을 바꾸면서 기존에 있었던 Oracle Cloud MFA 키를 가져오지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키를 이동시키는 방법은 잘 모르니 OCI에 접속해서 MFA 키를 비활성화하거나 대체 로그인을 설정했어야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 MFA 비활성화도 하지 않고, 대체 로그인도 설정하지 않아서 오라클 클라우드에 로그인 할 수 없었다.&lt;/p&gt;
&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;/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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2196&quot; data-origin-height=&quot;1130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGW5ce/btr62ZmQADb/PQdN5jLywTGU1CdO7OVNnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGW5ce/btr62ZmQADb/PQdN5jLywTGU1CdO7OVNnK/img.png&quot; data-alt=&quot;오라클 클라우드 로그인 1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGW5ce/btr62ZmQADb/PQdN5jLywTGU1CdO7OVNnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGW5ce%2Fbtr62ZmQADb%2FPQdN5jLywTGU1CdO7OVNnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;668&quot; height=&quot;344&quot; data-origin-width=&quot;2196&quot; data-origin-height=&quot;1130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오라클 클라우드 로그인 1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 계정 이름을 적어 로그인을 시도한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;1266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4pW8P/btr61h2T38R/kn6uOrt81qEuBGJibhREy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4pW8P/btr61h2T38R/kn6uOrt81qEuBGJibhREy1/img.png&quot; data-alt=&quot;오라클 클라우드 로그인 2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4pW8P/btr61h2T38R/kn6uOrt81qEuBGJibhREy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4pW8P%2Fbtr61h2T38R%2Fkn6uOrt81qEuBGJibhREy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;429&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;1266&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오라클 클라우드 로그인 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 이메일과 비밀번호를 적어 로그인을 시도한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;1386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beeVWC/btr60DrkmOJ/6Vp5qNIDtLocp9G50zXXh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beeVWC/btr60DrkmOJ/6Vp5qNIDtLocp9G50zXXh1/img.png&quot; data-alt=&quot;오라클 클라우드 MFA 키 로그인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beeVWC/btr60DrkmOJ/6Vp5qNIDtLocp9G50zXXh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeeVWC%2Fbtr60DrkmOJ%2F6Vp5qNIDtLocp9G50zXXh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;508&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;1386&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오라클 클라우드 MFA 키 로그인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 2차 인증으로 MFA 인증을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MFA는 Multi-Factor Authentiacation으로 다중 인증 기능이라고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 사용하면 내 휴대폰에 설치된 앱을 통해 비밀번호를 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 비밀번호는 30초마다 변경되기에 해킹에 매우매우 안전하다.&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;어플은 Oracle Mobile Authenticator이다.&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;IMG_8734.PNG&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WjqwM/btr65r4gsco/KiZqrypscLFe9S5mrGFcx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WjqwM/btr65r4gsco/KiZqrypscLFe9S5mrGFcx1/img.png&quot; data-alt=&quot;오라클 클라우드 Authenticator 앱&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WjqwM/btr65r4gsco/KiZqrypscLFe9S5mrGFcx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWjqwM%2Fbtr65r4gsco%2FKiZqrypscLFe9S5mrGFcx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;220&quot; height=&quot;476&quot; data-filename=&quot;IMG_8734.PNG&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오라클 클라우드 Authenticator 앱&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8735.PNG&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjM1zF/btr67ZNiGZB/ECbs2C8otzi8RonC9ajlb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjM1zF/btr67ZNiGZB/ECbs2C8otzi8RonC9ajlb0/img.png&quot; data-alt=&quot;오라클 클라우드 Authenticator 앱&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjM1zF/btr67ZNiGZB/ECbs2C8otzi8RonC9ajlb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjM1zF%2Fbtr67ZNiGZB%2FECbs2C8otzi8RonC9ajlb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;218&quot; height=&quot;472&quot; data-filename=&quot;IMG_8735.PNG&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오라클 클라우드 Authenticator 앱&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 나는 MFA키가 없었으니까 대체 로그인 방법 표시를 클릭했지만 설정하지 않았기 때문에 로그인을 할 수 없어서 고객센터에 문의를 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;1568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dH0oTI/btr61ShAIQQ/LeZLRGlWovT6uqYT2KzJvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dH0oTI/btr61ShAIQQ/LeZLRGlWovT6uqYT2KzJvk/img.png&quot; data-alt=&quot;오라클 클라우드 대체 로그인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dH0oTI/btr61ShAIQQ/LeZLRGlWovT6uqYT2KzJvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdH0oTI%2Fbtr61ShAIQQ%2FLeZLRGlWovT6uqYT2KzJvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;396&quot; height=&quot;566&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;1568&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오라클 클라우드 대체 로그인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 오라클 클라우드 MFA 분실로 인한 로그인 문제이다.&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;1. 오라클 고객센터 이메일 문의 (X)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2월에 이 방법을 시도했다가 실패했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;oraclehelp_ww@oracle.com 문의했다가 c2csupport_ww@oracle.com 여기로 Cloud 문의하라고 해서 보냈는데 아무것도 못해서 화났다. 몇주씩 기다리고 그랬는데 못했다. 화나서 계정 삭제시키려고 했는데 계정 삭제도 못했다. 문의를 해서 삭제시켜준댔는데 안됐다. 그래서 화나서 카드 재발급 받고 다시 회원가입해서 사용하려고 했었는데 그냥 냅뒀다.&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. &amp;nbsp;홈페이지 라이브 채팅 (X)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오라클 클라우드에서 클라우드 서버 방치하고 있다고 메일이 와서 뭔가 불안했다. 아무도 로그인 할 수 없었지만 그래도 불안했다. 그래서 다시 아이디를 되찾아보기 위한 노력을 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2474&quot; data-origin-height=&quot;2646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/53qo9/btr65oNc7dv/0VZukNU15vaBHPKQX4ZsTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/53qo9/btr65oNc7dv/0VZukNU15vaBHPKQX4ZsTk/img.png&quot; data-alt=&quot;오라클 홈페이지 라이브 채팅&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/53qo9/btr65oNc7dv/0VZukNU15vaBHPKQX4ZsTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F53qo9%2Fbtr65oNc7dv%2F0VZukNU15vaBHPKQX4ZsTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;566&quot; height=&quot;605&quot; data-origin-width=&quot;2474&quot; data-origin-height=&quot;2646&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오라클 홈페이지 라이브 채팅&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Live Chat 의 경우에는 실제로 유료 구매 고객에게만 사용할 수 있는 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Free Tier를 사용하고 있는 나는 사용할 수 없는 기능이라고 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Live Chat으로 문의하던 도중 아이디를 부르고나니 무료 계정은 이 라이브 채팅 지원을 사용할 수 없다고 들었다.&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;그래서 HUB 팀에 문의하라고 URL 남겨주는 &lt;b&gt;진짜 서러웠다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HUB 팀 URL : &lt;a href=&quot;https://www.oracle.com/uk/support/contact.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.oracle.com/uk/support/contact.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;3. 오라클 HUB 팀 지원센터 (O)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 오라클 서포트 연락처 사이트에서&amp;nbsp;Korea 를 검색하면 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 오라클 글로벌 지원 한국 고객센터 전화번호는 +82.1588.8501 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전화를 거니 영어 엄청 잘하시는 여성분이 친절하게 가르쳐주신다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 때 감동이었다&lt;/b&gt;. (서러운 경험을 받고 친절함을 겪어서일까?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차근차근 하나씩 문제를 듣고 해결방안을 실시간으로 알려주는데 나는 빛을 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, Oracle Cloud Infrastructure 로그인을 MFA 분실로 인해 못하는 것이기 때문에 기술 지원을 받아야 한다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://support.oracle.com/portal/?lang=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;My Oracle Support&lt;/a&gt; 에 접속해서 기술 지원에 대한 서비스 요청을 해야 한다고 가르쳐주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Oracle Cloud 계정과 동일한 이메일을 가진 Oracle 계정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Oracle 계정으로 로그인해서 Cloud 문의를 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 홈페이지 로그인을 해서 들어가면 아래 사진이 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlTkJD/btr60wzBCAd/fJoGKu6CrjSptuFUreJMKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlTkJD/btr60wzBCAd/fJoGKu6CrjSptuFUreJMKK/img.png&quot; data-alt=&quot;My Oracle Support&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlTkJD/btr60wzBCAd/fJoGKu6CrjSptuFUreJMKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlTkJD%2Fbtr60wzBCAd%2FfJoGKu6CrjSptuFUreJMKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;687&quot; height=&quot;238&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;My Oracle Support&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 요청 탭을 클릭해서 기술적 서비스 요청을 시도한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적 SR 생성 버튼을 클릭해서 문의를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2538&quot; data-origin-height=&quot;1554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u92Qi/btr62GgTF0w/1FcUuOjQhBf6CKdLo8zN31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u92Qi/btr62GgTF0w/1FcUuOjQhBf6CKdLo8zN31/img.png&quot; data-alt=&quot;My Oracle Support 서비스 요청 문의하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u92Qi/btr62GgTF0w/1FcUuOjQhBf6CKdLo8zN31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu92Qi%2Fbtr62GgTF0w%2F1FcUuOjQhBf6CKdLo8zN31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;419&quot; data-origin-width=&quot;2538&quot; data-origin-height=&quot;1554&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;My Oracle Support 서비스 요청 문의하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적 SR 생성을 누르면 위 페이지가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성해야하는 부분은 빨간색 상자로 표시해놨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Cloud MFA 로그인 문제라면 위 옵션을 그대로 선택해도 될 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요에 따라서 변경을 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 서비스 유형이 너무 많았기에 무엇을 선택해야하는지 몰라서 비기술적 SR 생성했다가 이렇게 문의하지말라고 한마디 듣고 그쪽에서 자동으로 기술지원으로 변경해줬다. 고마웠다...&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;1946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VHfhf/btr60DdRz1d/Y8tCM9UyMZ2KbWvOAQw0S1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VHfhf/btr60DdRz1d/Y8tCM9UyMZ2KbWvOAQw0S1/img.png&quot; data-alt=&quot;My Oracle Support 서비스 요청&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VHfhf/btr60DdRz1d/Y8tCM9UyMZ2KbWvOAQw0S1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVHfhf%2Fbtr60DdRz1d%2FY8tCM9UyMZ2KbWvOAQw0S1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;601&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;1946&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;My Oracle Support 서비스 요청&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트 추가 버튼을 클릭해서 해당 내용에 대한 답변을 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진처럼 최신순으로 정렬되기에 대화하듯이 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Oracle 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;p data-ke-size=&quot;size16&quot;&gt;나는 Oracle Cloud MFA 분실로 인해 OCI에 로그인을 못했었지만, 문의를 통해 MFA 초기화해서 로그인을 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오라클 또는 오라클 클라우드에 관한 문제는 My Oracle Support 를 통해서 문의를 남기고 해결을 받자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그게 아니라면 오라클 한국 지원센터에 전화를 해서 상담받도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 저 HUB팀 전화 아니었으면 지금도 해결못했을거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 감사하다.&lt;/p&gt;</description>
      <category>개발 etc</category>
      <category>My Oracle Support</category>
      <category>Oracle Cloud MFA 분실</category>
      <category>Oracle Cloud 로그인</category>
      <category>오라클 기술 지원</category>
      <category>오라클 문의</category>
      <category>오라클 클라우드 MFA 분실</category>
      <category>오라클 클라우드 계정 찾기</category>
      <category>오라클 클라우드 기술 지원</category>
      <category>오라클 클라우드 로그인 문제</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/145</guid>
      <comments>https://ssdragon.tistory.com/145#entry145comment</comments>
      <pubDate>Fri, 31 Mar 2023 11:21:01 +0900</pubDate>
    </item>
    <item>
      <title>Observer(옵저버) 패턴이란?</title>
      <link>https://ssdragon.tistory.com/144</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  옵저버 패턴이란?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1586&quot; data-origin-height=&quot;1010&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuHTyw/btr6TnhboOo/GDIiH86BGQ56FYZRcfQPV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuHTyw/btr6TnhboOo/GDIiH86BGQ56FYZRcfQPV0/img.png&quot; data-alt=&quot;옵저버 패턴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuHTyw/btr6TnhboOo/GDIiH86BGQ56FYZRcfQPV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuHTyw%2Fbtr6TnhboOo%2FGDIiH86BGQ56FYZRcfQPV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;494&quot; height=&quot;315&quot; data-origin-width=&quot;1586&quot; data-origin-height=&quot;1010&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;옵저버 패턴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주제(subject)가 변경될 때마다 옵저버(observer)들에게 변화를 알려주는 디자인 패턴&lt;/li&gt;
&lt;li&gt;한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의함&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;주제와 객체를 따로 두는 경우도 있음. 즉, 주제가 객체를 관찰하다가 변화가 생기면 옵저버들에게 알려주는 것&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제는 옵저버들이 인터페이스를 구현한다는 것을 제외하면 옵저버에 대해 아무것도 모른다. 따라서 이들 사이의 결합은 &lt;b&gt;느슨한 결합(Loose Coupling)&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;옵저버는 언제든지 새로 추가 및 제거 가능&lt;/li&gt;
&lt;li&gt;새로운 형식의 옵저버를 추가할 때도 주제는 변경할 필요 X&lt;/li&gt;
&lt;li&gt;주제와 옵저버는 서로 독립적으로 재사용 O&lt;/li&gt;
&lt;li&gt;주제나 옵저버가 달라져도 서로에게 영향 X&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;&amp;nbsp;  예시 코드 작성해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Producer(프로듀서)의 데이터가 변경될 때마다 Consumer(컨슈머)들에게 변화를 알려주는 예시를 작성해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;1160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wpI3K/btr60u7VrEV/tlcmMzJGKkCJIYhHdEIpQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wpI3K/btr60u7VrEV/tlcmMzJGKkCJIYhHdEIpQ1/img.png&quot; data-alt=&quot;예시 코드 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wpI3K/btr60u7VrEV/tlcmMzJGKkCJIYhHdEIpQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwpI3K%2Fbtr60u7VrEV%2FtlcmMzJGKkCJIYhHdEIpQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;485&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;1160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 코드 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주제는 여러개의 옵저버가 있을 수 있음&lt;/li&gt;
&lt;li&gt;주제는 상태가 바뀔때마다 notifyObservers()로 옵저버들에게 알려줘야 함&lt;/li&gt;
&lt;li&gt;옵저버가 되려면 Observer 인터페이스만 구현하면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Subject 인터페이스&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 등록, 제거, 변화를 알려주는 메서드가 있음&lt;/p&gt;
&lt;pre id=&quot;code_1680154324902&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Producer 클래스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Subject 인터페이스를 구현한 클래스&lt;/li&gt;
&lt;li&gt;상태값으로 dataA, dataB를 가짐&lt;/li&gt;
&lt;li&gt;전체 상태값을 변경하는 setData() 사용 시, notifyObservers()로 옵저버들에게 변화를 알려줌&lt;/li&gt;
&lt;li&gt;상태값 각각의 getter 메서드도 구현되어 있음. 이는 옵저버들이 풀 방식으로 상태값을 직접 가져오기 위함&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;옵저버 패턴을 사용하면 주제가 데이터를 보내는 푸쉬(push) 방식과 옵저버들 각각 원하는 데이터만 직접 가져오는 풀(pull) 방식이 있다. 일반적으로 풀 방식이 더 옳은 방식으로 간주하고 있다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1680154398890&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Producer implements Subject {
    private final List&amp;lt;Observer&amp;gt; observers;
    private String dataA;
    private String dataB;

    public Producer() {
        observers = new ArrayList&amp;lt;&amp;gt;();
    }

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        observers.forEach(Observer::update);
    }

    public void setData(String dataA, String dataB) {
        this.dataA = dataA;
        this.dataB = dataB;
        notifyObservers();
    }

    public String getDataA() {
        return dataA;
    }

    public String getDataB() {
        return dataB;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Observer 인터페이스&lt;/h4&gt;
&lt;pre id=&quot;code_1680154684200&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Observer {
    void update();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ConsumerA 클래스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Observer 인터페이스를 구현한 클래스&lt;/li&gt;
&lt;li&gt;주제를 저장하지 않아도 되지만, 추후 옵저버 목록에서 탈퇴할 때 유용함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1680154730873&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ConsumerA implements Observer {
    private String dataA;
    private Producer producer;

    public ConsumerA(Producer producer) {
        this.producer = producer;
        producer.registerObserver(this);
    }

    @Override
    public void update() {
        dataA = producer.getDataA();
        System.out.println(&quot;ConsumerA가 수정됨 : &quot; + dataA);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ConsumerB 클래스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ConsumerA와는 다르게 dataB만을 필요로하는 클래스&lt;/li&gt;
&lt;li&gt;따라서 주제가 변경되면 주제에서 원하는 데이터만 직접 가져오는 풀 방식이 더 좋음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1680154892083&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ConsumerB implements Observer {
    private String dataB;
    private Producer producer;

    public ConsumerB(Producer producer) {
        this.producer = producer;
        producer.registerObserver(this);
    }

    @Override
    public void update() {
        dataB = producer.getDataB();
        System.out.println(&quot;ConsumerB가 수정됨 : &quot; + dataB);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테스트해보기&lt;/h4&gt;
&lt;pre id=&quot;code_1680154985268&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ObserverTest {
    @Test
    void start() {
        Producer producer = new Producer();
        ConsumerA consumerA = new ConsumerA(producer);
        ConsumerB consumerB = new ConsumerB(producer);

        producer.setData(&quot;AAA&quot;, &quot;BBB&quot;);

        producer.removeObserver(consumerA); // consumerA 옵저버 제거

        producer.setData(&quot;CCC&quot;, &quot;DDD&quot;);
    }
}
/*
ConsumerA가 수정됨 : AAA
ConsumerB가 수정됨 : BBB
ConsumerB가 수정됨 : DDD
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 &lt;u&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/Sangyong-Jeon/design-patterns/tree/main/src/main/java/com/example/designpatterns/observer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;&lt;/b&gt;&lt;/u&gt;를 클릭하면 나옴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  결론&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주제와 옵저버 모두 인터페이스를 사용해서 느슨한 결합을 만듦&lt;/li&gt;
&lt;li&gt;옵저버 패턴에서는 구성을 활용해서 옵저버들을 관리하므로 확장성 좋음&lt;/li&gt;
&lt;li&gt;옵저버들은 서로 독립적이기 때문에 하나의 옵저버가 문제 발생하더라도 다른 옵저버는 영향이 없음&lt;/li&gt;
&lt;li&gt;옵저버 객체가 많아질수록 모든 옵저버 객체에 대한 처리를 해야하므로 성능 저하될 가능성 있음&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/디자인패턴</category>
      <category>Observer pattern</category>
      <category>observer pattern 예시코드</category>
      <category>디자인 패턴</category>
      <category>옵저버 패턴</category>
      <category>옵저버 패턴 예시코드</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/144</guid>
      <comments>https://ssdragon.tistory.com/144#entry144comment</comments>
      <pubDate>Thu, 30 Mar 2023 15:08:43 +0900</pubDate>
    </item>
    <item>
      <title>Java Stream(스트림)은 원본 데이터를 변경할 수 있다!?</title>
      <link>https://ssdragon.tistory.com/143</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;나의 의문점을 말하기전에, 스트림이 무엇인지 간단하게 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;--%--%EC%-A%A-%ED%-A%B-%EB%A-%BC-Stream-%--%EC%-D%B-%EB%-E%--%-F&quot; data-ke-size=&quot;size26&quot;&gt;1. 스트림(Stream) 이란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 8부터 도입된 데이터 처리 기능&lt;/li&gt;
&lt;li&gt;데이터 소스에서 데이터를 추상화하여 처리할 수 있도록 도와줌&lt;/li&gt;
&lt;li&gt;컬렉션, 배열, 파일 등의 데이터 소스에서 데이터를 읽고, 중간 처리 및 최종 처리하여 원하는 결과를 만듦&lt;/li&gt;
&lt;li&gt;스트림은 기존 데이터 소스의 요소를 가져와 중간 처리 연산을 수행하고 새로운 Stream으로 반환함&lt;/li&gt;
&lt;li&gt;중간 처리 연산은 lazy evaluation 방식으로 동작하기에 최종 처리 연산이 호출될 때만 수행됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 중간 처리 연산을 호출해도 즉각적인 연산이 되지 않음&lt;/li&gt;
&lt;li&gt;최종 연산이 수행되어야 스트림의 요소들이 중간 처리 연산을 거쳐 최종 연산에서 소모됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수형 프로그래밍 개념을 적용하여 작성했기에 가독성과 유지보수성이 높음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;원본 데이터 소스를 변경하지 않고, 새로운 데이터 소스를 생성하여 데이터를 처리함&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;--%--%EC%B-%-C%EC%A-%--%EC%--%B-%EC%--%B-%--%EC%-B%-C%--%EC%-B%--%EB%B-%B-%--%EB%-D%B-%EC%-D%B-%ED%--%B-%--%EC%--%-C%EC%-A%A-%EA%B-%--%--%EB%B-%--%EA%B-%BD%EB%--%--%EB%-A%--%--%EA%B-%BD%EC%-A%B-&quot; data-ke-size=&quot;size26&quot;&gt;2. 최종연산 시 원본 데이터 소스가 변경되는 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스트림 특징 중 &quot;&lt;b&gt;원본 데이터 소스를 변경하지 않는다&lt;/b&gt;&quot;를 읽었는데, 이 의문점은 뭐란 말인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 해당 특징을 간단하게 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1679904803188&quot; class=&quot;processing&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; names = new ArrayList&amp;lt;&amp;gt;();
names.add(&quot;myomyo&quot;);
names.add(&quot;aaa&quot;);
names.add(&quot;bbb&quot;);

Stream&amp;lt;String&amp;gt; strStream = names.stream().map(String::toUpperCase);
names.forEach(System.out::println);
/*
myomyo
aaa
bbb
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 names를 대문자로 변환하는 중간 처리 연산을 수행한 Stream을 만들고, names를 출력했지만 소문자로 출력된다. 이것이&lt;span&gt;&amp;nbsp;&lt;/span&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;그렇다면 &quot;원본 데이터 소스가 변경된거 아닌가?&quot;라는 의문점은 어디에서 온것인가?&lt;/p&gt;
&lt;pre id=&quot;code_1679904803189&quot; class=&quot;processing&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// given
List&amp;lt;String&amp;gt; names1 = new ArrayList&amp;lt;&amp;gt;();
names1.add(&quot;myomyo&quot;);
names1.add(&quot;aaa&quot;);

List&amp;lt;String&amp;gt; names2 = new ArrayList&amp;lt;&amp;gt;();
names2.add(&quot;bbb&quot;);

List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; namesList = new ArrayList&amp;lt;&amp;gt;();
namesList.add(names1);
namesList.add(names2);

// when
namesList.stream()
		.filter(names -&amp;gt; names.size() == 2)
		.forEach(names -&amp;gt; names.add(&quot;new&quot;));

// then
namesList.stream()
		.flatMap(Collection::stream)
		.forEach(System.out::println);
/*
myomyo
aaa
new
bbb
*/&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;given에서 테스트할 데이터를 생성&lt;/li&gt;
&lt;li&gt;when에서 namesList에 들어있는 List의 사이즈가 2인 것만 필터링해서 그 List에 &quot;new&quot; 추가&lt;/li&gt;
&lt;li&gt;then에서 namesList가 가지고 있는 List의 모든 요소를 출력&lt;/li&gt;
&lt;li&gt;결과는 원본 데이터인 namesList에 &quot;new&quot;가 추가되어 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 스트림의 최종연산인 forEach로 반복하는 것이 참조 객체인 List이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;참조를 통해 객체를 변경하는 것&lt;/b&gt;이므로 당연히 &quot;new&quot;가 추가되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 아래 예시코드처럼 클래스의 인스턴스를 가진 리스트도 당연히 원본 데이터 수정이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679904803189&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class A {
    int age;
    public A(int age) {
        this.age = age;
    }
}

A a1 = new A(1);
A a2 = new A(1);

List&amp;lt;A&amp;gt; AList = new ArrayList&amp;lt;&amp;gt;();
AList.add(a1);
AList.add(a2);

AList.stream().forEach(a -&amp;gt; a.age = 2);

AList.forEach(a -&amp;gt; System.out.println(a.age));
/*
2
2
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조된 객체를 변경하는 것이므로 막을 방법은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업을 수행할 수 있다하더라도 이것은 사용해도 좋은걸까 의문점이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스트림은 원래의 데이터 소스를 변경하지 않는 것이 side-effects(부작용)를 만들지 않는다고 배웠기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 A클래스를 가진 리스트가 있을 때, age가 20 넘는 것들에 대해서만 age를 +1 해주는 예시를 만들어본다.&lt;/p&gt;
&lt;pre id=&quot;code_1679904803190&quot; class=&quot;tp&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;A&amp;gt; AList = new ArrayList&amp;lt;&amp;gt;();

// 스트림 예시
AList.stream()
    .filter(a -&amp;gt; a.age &amp;gt; 20)
    .forEach(a -&amp;gt; a.age = a.age + 1);
    
// 컬렉션 for loop 예시
AList.forEach(a -&amp;gt; {
    if(a.age &amp;gt; 20) {
        a.age = a.age + 1;
    }
}

// 일반 for loop 예시
for(A a : AList) {
    if(a.age &amp;gt; 20) {
        a.age = a.age + 1;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;age에 관련된 메서드를 만들면 더욱 간결하게 만들 수 있지만 일단 그것은 논외로 친다.&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;그렇기에 스트림의 forEach문으로 참조 객체의 상태를 변경하는 것이 아닐까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;--%--%EC%B-%-C%EC%A-%--%EC%--%B-%EC%--%B-%--forEach%EB%A-%-C%--%EC%B-%B-%EC%A-%B-%--%EA%B-%-D%EC%B-%B-%EC%-D%--%--%EC%--%--%ED%--%-C%EB%A-%BC%--%EB%B-%--%EA%B-%BD%ED%--%--%EB%-A%--%--%EA%B-%--%EC%-D%B-%--%EC%A-%-B%EC%-D%--%EA%B-%-C%-F&quot; data-ke-size=&quot;size26&quot;&gt;3. 최종연산 forEach로 참조 객체의 상태를 변경하는 것이 좋을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 스트림에서 반복할 수 있는 연산은 peek()라는 중간연산과 forEach() 최종연산이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;peek()는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;u&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/Stream.html#peek(java.util.function.Consumer)&quot;&gt;공식문서&lt;/a&gt;&lt;/u&gt;&lt;/b&gt;에 따르면 주로 디버깅을 위해 존재하는 것이라 명시했기에 잘 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 반복은 forEach() 최종연산을 통해 할 수 밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 해당 의문점을 찾아보기 위해 stackoverflow의 질문과 답변을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/30778017/modifying-objects-within-stream-in-java8-while-iterating&quot;&gt;https://stackoverflow.com/questions/30778017/modifying-objects-within-stream-in-java8-while-iterating&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/22753755/how-to-add-elements-of-a-java8-stream-into-an-existing-list/22755225#22755225&quot;&gt;https://stackoverflow.com/questions/22753755/how-to-add-elements-of-a-java8-stream-into-an-existing-list/22755225#22755225&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;----%--%EB%B-%--%EB%B-%B-%ED%--%--%EB%-A%--%--%EB%-F%--%EC%--%--%--Java-%EC%-D%--%--Stream%--%EB%--%B-%--%EA%B-%-D%EC%B-%B-%EB%A-%BC%--%EB%B-%--%EA%B-%BD%ED%--%B-%EB%-F%--%--%EB%--%A-%EA%B-%-C%-F&quot; data-ke-size=&quot;size23&quot;&gt;3-1. 반복하는 동안 Java8의 Stream 내 객체를 변경해도 될까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 코드가 있을 때 이렇게 사용해도 될까?&lt;/p&gt;
&lt;pre id=&quot;code_1679904803191&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;users.stream().forEach(u -&amp;gt; u.setProperty(&quot;value&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가능하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 스트림 내부의 객체 상태를 수정할 수 있지만 대부분의 경우 스트림 소스의 상태를 수정하면 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#NonInterference&quot;&gt;스트림 공식문서의 non-interference&lt;/a&gt;&lt;/b&gt;&lt;/u&gt;에 따르면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;스트림을 사용하면 ArrayList와 같은 안전하지 않은 스레드 컬렉션을 포함하여 다양한 데이터 소스에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;병렬 집계 연산&lt;/b&gt;을 실행할 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;이는 스트림 파이프라인을 실행하는 동안 데이터 소스와의 간섭을 방지할 수 있는 경우에만 가능하다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;escape-hatch 연산인 iterator()와 spliterator()를 제외하고는 터미널 연산이 호출될 때 실행이 시작되고 터미널 연산이 완료되면 실행이 종료된다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;대부분의 데이터 소스에서 간섭을 방지한다는 것은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;스트림 파이프라인을 실행하는 동안 데이터 소스가 전혀 수정되지 않도록 하는 것을 의미&lt;/b&gt;한다. 여기에는 동시 수정을 처리하도록 특별히 설계된 concurrent collections를 소스로 하는 스트림은 예외이다. 동시 스트림 소스는 Spliterator가 CONCURRENT 특성을 보고하는 스트림 소스이다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병렬 집계 연산을 하려면 데이터 소스와의 간섭을 방지할 수 있는 경우에만 가능&lt;/li&gt;
&lt;li&gt;데이터 소스 간섭 방지는 보통 스트림 파이프라인을 실행하는 동안 데이터 소스가 전혀 수정되지 않도록 하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 내가 병렬 집계 연산을 사용하지 않는다면 사용해도&lt;span&gt;&amp;nbsp;&lt;/span&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;/p&gt;
&lt;pre id=&quot;code_1679904803191&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  List&amp;lt;User&amp;gt; users = getUsers();

// 사용해도 괜찮은 예시 #1
  users.stream().forEach(u -&amp;gt; u.setProperty(value));
//                       ^    ^^^^^^^^^^^^^
//                        \__/

// 사용하면 위험한 예시 #1
  users.stream().forEach(u -&amp;gt; users.remove(u));
//^^^^^                       ^^^^^^^^^^^^
//     \_____________________/

// 사용하면 위험한 예시 #2
  List&amp;lt;Integer&amp;gt; list = IntStream.range(0, 10).boxed().collect(Collectors.toList());

  list.stream()
      .filter(i -&amp;gt; i &amp;gt; 5)
      .forEach(i -&amp;gt; list.remove(i));  //throws NullPointerException&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아까 non-interference에서 말했듯이 데이터 소스가 전혀 수정되지 않도록 하는 것을 의미한다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 사용해도 괜찮은 예시 #1에서는 데이터 소스를 수정하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그에 따른 또 다른 대답은 여기서 볼 수 있었다. (&lt;u&gt;&lt;b&gt;&lt;a href=&quot;https://stackoverflow.com/questions/35377840/how-to-call-setter-in-chain-of-stream/35377979#35377979&quot;&gt;링크&lt;/a&gt;&lt;/b&gt;&lt;/u&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1679904803191&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;User&amp;gt; users = getUsers();

// #1. user의 나이가 20이상인 것만 변경할 때
List&amp;lt;User&amp;gt; newUsers = users.stream()
			.filter(u -&amp;gt; u.age &amp;gt;= 20)
			.collect(toList());
                            
newUsers.forEach(u -&amp;gt; u.setProperty(value));

// #2. 위 코드를 하나로 합쳐 부작용이 생길 수 있는 코드
users.stream()
	.filter(u -&amp;gt; u.age &amp;gt;= 20)
	.forEach(u -&amp;gt; u.setProperty(value));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 나이 20이상인 user를 모은 새로운 리스트를 만들고 그 리스트를 컬렉션의 forEach로 반복해서 변경한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 기존 리스트인 users를 재사용한다면 '부작용이 생길 수 있는 코드'처럼 작성하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 답변을 한 사람은 &quot;&lt;b&gt;코드의 길이는 안전 및 유지 관리성과 관련이 없다&lt;/b&gt;&quot; 라고 말을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#1 코드보다는 #2 코드가 깔끔해보여서 이렇게 작성하고 싶은 욕심이 마구마구 생기지만 stackoverflow와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;b&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#NonInterference&quot;&gt;공식문서의 side-effects&lt;/a&gt;&lt;/b&gt;&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;부분을 보면 #1이 안전하다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;side-effects(부작용)에 대한 공식문서 내용은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1679904803191&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 불필요한 부작용 사용하는 코드
ArrayList&amp;lt;String&amp;gt; results = new ArrayList&amp;lt;&amp;gt;();
stream.filter(s -&amp;gt; pattern.matcher(s).matches())
	.forEach(s -&amp;gt; results.add(s));  // Unnecessary use of side-effects!
    
// 부작용이 없는 코드
List&amp;lt;String&amp;gt;results = stream.filter(s -&amp;gt; pattern.matcher(s).matches())
			.collect(Collectors.toList());  // No side-effects!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 내용은 문자열 스트림에서 지정된 정규식과 일치하는 문자열을 검색하고 일치 항목을 목록에 넣는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요한 코드에 대한 공식문서 내용은 아래와 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 코드는 불필요하게 부작용을 사용한다. 병렬로 실행하면 ArrayList의 non-thread-safety로 인해 잘못된 결과가 발생할 수 있고, 필요한 동기화(synchronization)를 추가하면 경합이 발생하여 병렬 처리의 이점을 훼손할 수 있다. 또한 여기서 부작용을 사용하는 것은 전혀 불필요하며, forEach()를 더 안전하고 효율적이며 병렬화에 적합한 축소 연산으로 간단히 대체할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 forEach에 로직을 추가하면 동시성 보장이 어렵고 가독성이 떨어지며, 스트림의 의도를 벗어나게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;forEach는 종료를 하기 위한 연산이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로직을 수행하는 역할은 중간 연산이 해야하는 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 최종 연산인 forEach가 중간 연산의 책임을 하게 된다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&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 id=&quot;--%--%EB%--%--%EB%AC%B-%--%EA%B-%B-%EB%-B%--%EA%B-%-C%--%EC%-A%--%EC%--%BD%ED%--%--%EA%B-%B-&quot; data-ke-size=&quot;size26&quot;&gt;4. 너무 길어서 요약하기&lt;/h2&gt;
&lt;pre id=&quot;code_1679904803191&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;A&amp;gt; aList = new ArrayList&amp;lt;&amp;gt;();

// #1
aList.stream()
	.filter(a -&amp;gt; a.age &amp;gt;= 20)
	.forEach(a -&amp;gt; a.age += 1); // side-effects 발생 가능

// #2
List&amp;lt;A&amp;gt; updateAList = aList.stream()
		.filter(a -&amp;gt; a.age &amp;gt;= 20)
		.collect(toList());
updateAList.forEach(a -&amp;gt; a.age += 1); // side-effects 발생 X&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;디버깅을 위한 peek() 연산에서 객체의 상태를 변경하는 것은 좋지 않음&lt;/li&gt;
&lt;li&gt;Stream의 forEach()는 최종 연산이다&lt;/li&gt;
&lt;li&gt;Stream의 forEach()에 로직을 구현하는 것은 중간 연산의 책임을 하는 것&lt;/li&gt;
&lt;li&gt;이펙티브 자바 46에 따르면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;forEach&lt;span&gt;&amp;nbsp;&lt;/span&gt;연산은 최종 연산 중 기능이 가장 적고 '덜' 스트림하기에 계산 결과를 보고할 때(print 기능)만 사용하고 계산하는 데는 쓰지 말자&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라고 함&lt;/li&gt;
&lt;li&gt;forEach를 통해 원본 데이터 소스를 변경하면 side-effects가 발생할 수 있음&lt;/li&gt;
&lt;li&gt;스트림을 통한 병렬(paraller) 연산은 사용할만한 상황이 매우 적음 (잘못하면 큰일)&lt;/li&gt;
&lt;li&gt;병렬 연산이 매우 적더라도 side-effects가 있는 로직을 구현한다면 나중에 먼 미래에 큰일날 수도?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;--%--%EB%--%--%EC%-D%--%--%EC%--%-D%EA%B-%--&quot; data-ke-size=&quot;size26&quot;&gt;5. 나의 생각&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적을수록 의문점이 들어 글 한번 쓰기 어려웠다.&amp;nbsp;대부분 스트림의 병렬 연산을 사용하지 않으므로 큰 에러없이 Stream의 forEach()를 통해 원본 데이터 소스를 변경하거나 여러 로직을 구현했을 것이다. 그게 가장 쉬운 방법이면서 보기도 편하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 전체 유저를 가져오고 그 중 일부만 상태값을 변경하고 전체 유저를 반환하는 코드를 작성해야 한다고 생각해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1679904803192&quot; class=&quot;routeros&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;User&amp;gt; users = getUsers();
users.stream()
	.filter(u -&amp;gt; u.getAge() &amp;gt;= 20)
	.forEach(u -&amp;gt; u.setAge(u.getAge()+1));
return users;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 ArrayList일 때 users의 내부 상태를 변경했기에 ConcurrentModificationException이 발생할 수도 있다는 걸 생각하면서 안전하게 데이터 소스를 수정할 수 있는지 고려해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 side-effects 없이 만드려면 어떻게 해야할까?&lt;/p&gt;
&lt;pre id=&quot;code_1679904803192&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;User&amp;gt; users = getUsers();
// 1번째 방법
List&amp;lt;User&amp;gt; updateUsers = users.stream()
		.map(u -&amp;gt; {
			if(u.getAge() &amp;gt;= 20) 
				return new User(u.getAge()+1);
			return u; 
		})
		.collect(toList());
return updateUsers;

// 2번째 방법
List&amp;lt;User&amp;gt; updateUsers = users.stream()
		.filter(u -&amp;gt; u.getAge() &amp;gt;= 20)
		.collect(toList());
updateUsers.forEach(u -&amp;gt; u.setAge(u.getAge() + 1));

List&amp;lt;User&amp;gt; nonUpdateUsers = users.stream()
		.filter(u -&amp;gt; u.getAge() &amp;lt; 20)
		.collect(toList());

List&amp;lt;User&amp;gt; newUsers = new ArrayList&amp;lt;&amp;gt;();
newUsers.addAll(updateUsers);
newUsers.addAll(nonUpdateUsers);
return newUsers;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 방법을 사용하든지 상대적으로 가독성이나 코드가 길어지고 불편해지는 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 보기 싫을 수도 있다. 나도 1번째나 2번째 코드보다는 side-effects가 있는 코드가 더 보기 좋아보인다.&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;스트림의 forEach()를 사용할 때 안전한 데이터 소스 수정을 고려해야 함&lt;/li&gt;
&lt;li&gt;side-effects 없이 데이터 소스를 수정하려면 코드가 길어지고 가독성이 떨어짐&lt;/li&gt;
&lt;li&gt;병렬 연산을 대부분 하지 않으니 side-effects 있는 방식을 사용해도 괜찮아보임&lt;/li&gt;
&lt;li&gt;혹시 모르니 Stream API를 더 깊이 이해하고 안전한 코드 작성을 위해 노력하는 것이 중요&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Java</category>
      <category>java stream</category>
      <category>java 스트림</category>
      <category>Stream</category>
      <category>스트림</category>
      <category>자바 stream</category>
      <category>자바 스트림</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/143</guid>
      <comments>https://ssdragon.tistory.com/143#entry143comment</comments>
      <pubDate>Mon, 27 Mar 2023 17:14:02 +0900</pubDate>
    </item>
    <item>
      <title>인덱스(index)란?</title>
      <link>https://ssdragon.tistory.com/141</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 인덱스란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB 테이블에 대한 검색 속도를 향상시켜주는 자료구조&lt;/li&gt;
&lt;li&gt;인덱스는 보통 B-Tree로 구현되며, 다른 것으로는 Hash Table, Bitmap 등이 있음&lt;/li&gt;
&lt;li&gt;인덱스를 테이블의 특정 컬럼에 생성하면 해당 컬럼의 값을 정렬하여 별도의 메모리 공간에 데이터를 물리적 주소와 함께 저장함&lt;/li&gt;
&lt;li&gt;인덱스는 insert, update, delete 와 같은 데이터 변경 작업을 느리게 할 수 있지만, select와 같은 검색 작업은 빠른 수행 가능&lt;/li&gt;
&lt;li&gt;인덱스는 적절하게 설계하고 유지보수하는 것이 DB 성능 향상에 중요한 역할을 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면, 책에서의 목차 또는 색인이라 볼 수 있다. 목차를 보고 빠른 내용을 찾을 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1. 왜 인덱스를 사용하면 검색 속도가 빠를까?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 where 문으로 특정 조건 데이터를 찾기 위해서는 전체 테이블 스캔 작업이 필요함&lt;/li&gt;
&lt;li&gt;인덱스를 이용하면 데이터들이 정렬되어 있기에 조건에 맞는 데이터를 더 빠르게 탐색 가능&lt;/li&gt;
&lt;li&gt;또한, order by문이나 min/max 같은 경우도 이미 정렬되어 있기에 빠른 수행 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;전체 테이블 스캔(Full Table Scan) : 테이블에 존재하는 모든 데이터를 읽으면서 조건 비교&lt;br /&gt;인덱스 스캔(Index Scan) : 인덱스를 구성하는 컬럼의 값을 기반으로 데이터를 추출하는 액세스 기법&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Hash Table이란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간단하고 빠른 검색이 가능한 자료구조&lt;/li&gt;
&lt;li&gt;등호 연산에 최적화되어 있으며, key-value 구조로 데이터 저장&lt;/li&gt;
&lt;li&gt;인덱스에서는 컬럼 값이 key, 저장된 레코드의 물리적 주소가 value&lt;/li&gt;
&lt;li&gt;해시 함수를 이용해 key를 해시값으로 변환하고, 이를 이용해 빠른 데이터 검색 가능&lt;/li&gt;
&lt;li&gt;해시 함수를 이용할 때 충돌 발생 시, 일반적으로 해시 테이블은 각각의 슬롯에 연결리스트 사용해서 충돌 처리(Separate Chaining)&lt;/li&gt;
&lt;li&gt;해시 테이블은 검색 속도가 매우 빠르지만, 범위 검색에는 적합하지 않음&lt;/li&gt;
&lt;li&gt;범위 검색이 자주 일어나는 경우 B-Tree와 같은 자료구조를 사용하는 것이 좋음&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-1. Hash Table에서 범위 검색은 왜 비효율적일까?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;범위 검색 수행 시, 일반적으로 연속된 인덱스 엔트리를 찾아야함&lt;/li&gt;
&lt;li&gt;해시 테이블은 해시함수에 의해 무작위로 분산되어 저장되기에 인덱스 엔트리들이 서로 물리적으로 연속되어 저장되지 않을 수 있음&lt;/li&gt;
&lt;li&gt;이 경우, 연속된 인덱스 엔트리를 찾기 위해 모든 인덱스 엔트리를 검색해야 하기 때문에 비효율적&lt;/li&gt;
&lt;li&gt;따라서, 해시 테이블은 주로 동등비교(equality search)에 사용하며, 범위 검색에는 비효율적&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;인덱스 엔트리(Index Entry)란?&lt;br /&gt;- DB 인덱스에서 각각의 인덱스 키 값에 대한 정보를 담고 있는 데이터 레코드를 말함&lt;br /&gt;- 인덱스 엔트리에는 인덱스 키 값과 해당하는 레코드의 위치 정보가 포함&lt;br /&gt;- 인덱스 엔트리는 인덱스 자료구조의 종류에 따라 다양한 방식으로 구현됨&lt;br /&gt;- B-Tree에서는 인트리가 키와 포인터로 이루어짐&lt;br /&gt;- Hash Table에서는 해시값과 해당 키에 대응되는 데이터 레코드의 위치 정보로 이루어짐&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. B-Tree란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자주 사용되는 데이터를 효율적으로 검색하기 위해 설계된 트리 구조&lt;/li&gt;
&lt;li&gt;B-Tree는 Balanced Tree의 약자로, 트리의 균형을 유지하여 검색 속도를 높이는 것이 핵심&lt;/li&gt;
&lt;li&gt;DB 인덱스를 구현하는 데 많이 사용되며, 대용량 데이터를 저장 및 검색하는 데 매우 효율적&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1. B-Tree 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;균형 잡힌 트리&lt;/li&gt;
&lt;li&gt;여러 개의 자식 노드를 가질 수 있음&lt;/li&gt;
&lt;li&gt;각 노드에는 여러 개의 데이터가 저장될 수 있음&lt;/li&gt;
&lt;li&gt;데이터 삽입, 삭제, 검색할 때 모든 연산은 트리의 높이에 비례&lt;/li&gt;
&lt;li&gt;평균적으로 시간 복잡도는 O(log n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 인덱스 사용 시 주의사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자주 검색하지 않는 컬럼이면 인덱스 생성 및 관리 비용이 더 늘어날 수 있음&lt;/li&gt;
&lt;li&gt;중복 인덱스 방지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일 컬럼을 기준으로 여러 개의 인덱스 생성하지 않도록 주의&lt;/li&gt;
&lt;li&gt;중복된 인덱스는 불필요한 저장공간을 차지하며 데이터 삽입, 삭제, 수정 시에도 불필요한 비용 발생 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인덱스는 공간적 비용이 발생하므로, 너무 많은 인덱스는 성능 저하가 올 수 있음&lt;/li&gt;
&lt;li&gt;필드의 크기가 크면 인덱스의 크기도 커짐&lt;/li&gt;
&lt;li&gt;인덱스를 지정하면 데이터 삽입, 수정, 및 삭제의 성능이 감소될 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-1. 범위 조건 검색일 때 인덱스 사용 주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스는 특정 열에 대한 검색을 빠르게 수행할 수 있도록 테이블의 데이터를 정렬하여 저장함&lt;/li&gt;
&lt;li&gt;인덱스의 효율성은 범위 조건 검색의 시작점과 종료점을 찾는 것에 크게 영향을 받음&lt;/li&gt;
&lt;li&gt;예를 들면, `WHERE age BETWEEN 20 AND 30` 과 같은 쿼리 실행 시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`age` 열에서 20~30까지의 범위를 찾아야 함&lt;/li&gt;
&lt;li&gt;만약 인덱스에서 20~30까지의 값이 연속적으로 저장되어 있다면 인덱스를 효율적으로 사용 가능&lt;/li&gt;
&lt;li&gt;만약 인덱스에 20,25,30,35와 같이 불규칙하게 저장되어 있다면 인덱스를 사용할 수 있으나 매우 많은 페이지를 탐색해야 하므로 검색 성능이 떨어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위 예시를 이유로 범위 조건 검색에서는 인덱스의 효율성이 상대적으로 떨어질 수 있음&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데이터베이스</category>
      <author>묠니르묘묘</author>
      <guid isPermaLink="true">https://ssdragon.tistory.com/141</guid>
      <comments>https://ssdragon.tistory.com/141#entry141comment</comments>
      <pubDate>Thu, 23 Mar 2023 14:04:45 +0900</pubDate>
    </item>
  </channel>
</rss>