<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>이모저모 개발 블로그</title>
    <link>https://hj0216.tistory.com/</link>
    <description>GitHub: github.com/HJ0216</description>
    <language>ko</language>
    <pubDate>Wed, 20 May 2026 00:50:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>HJ0216</managingEditor>
    <image>
      <title>이모저모 개발 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/5930571/attach/ea96999518ed45fb83424b95ac1348e6</url>
      <link>https://hj0216.tistory.com</link>
    </image>
    <item>
      <title>[AWS] 과금의 이유를 알아보자...2</title>
      <link>https://hj0216.tistory.com/1014</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;loopy3_18.png&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NDoF5/btsNrzuVNXp/0eSbiKOfYHKr8uDhRzsWJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NDoF5/btsNrzuVNXp/0eSbiKOfYHKr8uDhRzsWJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NDoF5/btsNrzuVNXp/0eSbiKOfYHKr8uDhRzsWJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNDoF5%2FbtsNrzuVNXp%2F0eSbiKOfYHKr8uDhRzsWJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-filename=&quot;loopy3_18.png&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과금이 처음이라면 무섭지만, 두 번째라면 덜 무섭달까요..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;소플의 처음 만난 AWS&lt;/a&gt;를 다 읽고&amp;nbsp; &lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000210532528&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS 교과서&lt;/a&gt;까지 다 읽었습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;개인적으로 난이도는 교과서가 더 높은 것 같아서 AWS가 처음이시라면 가볍게 소플의.. 를 읽고 나서 조금 더 자세히 알아보기위해 교과서까지 같이 보시면 좋은 것 같습니다. 특히 네트워크 부분이 기초를 쌓기에 좋다고 생각합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;AWS 과금이 될 수도 있다는 몇몇 구간이 있었는데, 다음날 반영되다보니 프리 티어인데 어떤 이유로 과금되었는지 기억이 안나더라구요.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;그래서 책을 다 읽은 김에 한 번에 정리해봅니다,,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1399&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MfLOR/btsNpwknOZm/2sCKdKp5zorMfhcM4BYwkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MfLOR/btsNpwknOZm/2sCKdKp5zorMfhcM4BYwkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MfLOR/btsNpwknOZm/2sCKdKp5zorMfhcM4BYwkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMfLOR%2FbtsNpwknOZm%2F2sCKdKp5zorMfhcM4BYwkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1399&quot; height=&quot;188&quot; data-origin-width=&quot;1399&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 번과 유사하게 EC2와 RDS와 VPC가 원인으로 들어가 있군요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. EC2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Amazon Elastic Compute Cloud NatGateway&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;$0.059 per NAT Gateway Hour: &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;2 Hrs&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Nat(&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Network Address Translation&lt;/span&gt;)Gateway: &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;네트워크 주소 변환 서비스&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * 프라이빗 서브넷에서 외부 인터넷으로 통신하는 관문 역할&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;프라이빗&lt;span&gt; &lt;/span&gt;&lt;/span&gt;서브넷은 외부 인터넷 구간과 단절된 독립된 네트워크이지만 외부 인터넷 구간과 통신이 필요할 때는 NAT 게이트웨이를 통해 프라이빗 IP 주소를 퍼블릭 IP 주소로 변환하여 외부 인터넷 구간과 통신할 수 있음 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;AWS 네트워크 서비스를 공부할 때, 아래와 같은 구조를 테스트해 보기 위해 NAT Gateway를 생성했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uDQm7/btsNryQjHrb/CI9xg04RpjgkrK9ZzxeZ10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uDQm7/btsNryQjHrb/CI9xg04RpjgkrK9ZzxeZ10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uDQm7/btsNryQjHrb/CI9xg04RpjgkrK9ZzxeZ10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuDQm7%2FbtsNryQjHrb%2FCI9xg04RpjgkrK9ZzxeZ10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;828&quot; height=&quot;309&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table id=&quot;1d367b9e-70f4-80f7-98ee-f5291f223329&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;1d367b9e-70f4-80c4-acb4-eb8d5d9e58d0&quot;&gt;
&lt;td id=&quot;dBt{&quot;&gt;출발지&lt;/td&gt;
&lt;td id=&quot;yu{F&quot;&gt;목적지&lt;/td&gt;
&lt;td id=&quot;BU^&amp;gt;&quot;&gt;통신 가능 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1d367b9e-70f4-8052-96b1-f31e08d27001&quot;&gt;
&lt;td id=&quot;dBt{&quot;&gt;퍼블릭 서브넷&lt;/td&gt;
&lt;td id=&quot;yu{F&quot;&gt;외부 인터넷&lt;/td&gt;
&lt;td id=&quot;BU^&amp;gt;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1d367b9e-70f4-8033-aad1-d7b647512560&quot;&gt;
&lt;td id=&quot;dBt{&quot;&gt;외부 인터넷&lt;/td&gt;
&lt;td id=&quot;yu{F&quot;&gt;퍼블릭 인터넷&lt;/td&gt;
&lt;td id=&quot;BU^&amp;gt;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1d367b9e-70f4-805d-97e5-ff9b50e4fc22&quot;&gt;
&lt;td id=&quot;dBt{&quot;&gt;프라이빗 인터넷&lt;/td&gt;
&lt;td id=&quot;yu{F&quot;&gt;외부 인터넷&lt;/td&gt;
&lt;td id=&quot;BU^&amp;gt;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1d367b9e-70f4-80ad-88b1-f5508aae71a7&quot;&gt;
&lt;td id=&quot;dBt{&quot;&gt;외부 인터넷&lt;/td&gt;
&lt;td id=&quot;yu{F&quot;&gt;프라이빗 인터넷&lt;/td&gt;
&lt;td id=&quot;BU^&amp;gt;&quot;&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;Amazon Elastic Compute Cloud running Linux/UNIX&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;$0.052 per On Demand Linux t3.medium Instance Hour: &lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;14.6 Hrs&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보지 않아도 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 프리티어에서 무료로 사용할 수 있는 인스턴스 유형이 아닌 t3.medium 인스턴스를 썼기 때문이죠^^..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 프리티어는&amp;nbsp; &lt;span style=&quot;background-color: #d3e3fd; color: #001d35; text-align: start;&quot;&gt;t2.micro 또는 t3.micro &lt;span style=&quot;background-color: #ffffff; color: #001d35; text-align: start;&quot;&gt;&amp;nbsp;인스턴스가 한달에 750시간의 사용 시간이 제공됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;2. RDS&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Amazon Relational Database Service for MySQL Community Edition&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp;- USD 0.052 per db.t3.micro Multi-AZ instance hour (or partial hour) running MySQL: &lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;1.276 Hrs&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기억이 납니다. 기억이..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FailOver 테스트를 해볼 때였을까요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi AZ는 고가용성을 위해 데이터베이스를 두 개의 가용 영역(AZ)에 걸쳐 복제하는 옵션인데, 추가 비용이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Amazon Relational Database Service Provisioned Storage&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;$0.131 per GB-month of provisioned GP3 storage running MySQL: &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;0.292 GB-Mo&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0f141a;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;RDS 설정했을 때, 스토리지 유형을 GP3로 설정하면 나옵니다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0f141a;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;프리티어 친구들은 GP2를 사용해야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0f141a;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;* &lt;span style=&quot;background-color: #ffffff; color: #001d35; text-align: start;&quot;&gt;provisioning: 사용자가 요청한 IT 자원을 사용할 수 있는 상태로 준비하는 것&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Amazon Relational Database Service Provisioned Storage&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp;- $0.262 per GB-month of provisioned GP3 storage for Multi-AZ deployments running MySQL: &lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;0.354 GB-Mo&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;단위 당 비용이 가장 비싼 친구^^...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-AZ용 GP3 스토리지 요금입니다.&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;p data-ke-size=&quot;size16&quot;&gt;3. &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Virtual Private Cloud&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Amazon Virtual Private Cloud Public IPv4 Addresses&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- $&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;0.005 per In-use public IPv4 address per hour: &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;14.329 Hrs&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번에도 한 번 청구되었던 기억이 나네요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이번에는 직접 원인을 찾아가보는 과정을 추가해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPC &amp;rarr; Amazon VPC IP Address Manager &amp;rarr; 퍼블릭 IP 인사이트(과금의 원인에 대한 인사이트를 주겠다는 의미일까요..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2가 아닌 서비스가 범인이며, 저의 경우에는 RDS였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 한 번 옮겨 적어보는 문의 사항에 대한 답변..&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;프리티어를 사용하고 계실 경우, EC2용 퍼블릭 IPv4 주소에 대해 월 750시간의 무료 혜택이 제공되지만, 안타깝게도, RDS와 같은 다른 서비스와 연결되어 사용된 퍼블릭 IPv4 주소에 대해선 프리티어 혜택이 제공되지 않습니다.&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;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Chat GPT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;a href=&quot;https://kimjingo.tistory.com/180&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kimjingo.tistory.com/180&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745135300664&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;[AWS] VPC NAT Gateway 구성하기&quot; data-og-description=&quot;AWS에서 NAT Gateway란? NAT 게이트웨이는 NAT(Network Address Translation, 네트워크 주소 변환) 서비스입니다. 프라이빗 서브넷의 인스턴스가 VPC 외부의 서비스에 연결할 수 있지만 외부 서비스에서 이러한 &quot; data-og-host=&quot;kimjingo.tistory.com&quot; data-og-source-url=&quot;https://kimjingo.tistory.com/180&quot; data-og-url=&quot;https://kimjingo.tistory.com/180&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ktRjI/hyYFFvRylS/WkEJfRKp7aWYgUKQlUH5Pk/img.png?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/PWKgP/hyYFzbiGix/kblAEiviToVFQVZKxoDm20/img.png?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/cLNZdf/hyYG10MKF2/8SOFXDh9avXP4Wo6m0H2dK/img.png?width=1011&amp;amp;height=762&amp;amp;face=0_0_1011_762&quot;&gt;&lt;a href=&quot;https://kimjingo.tistory.com/180&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kimjingo.tistory.com/180&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ktRjI/hyYFFvRylS/WkEJfRKp7aWYgUKQlUH5Pk/img.png?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/PWKgP/hyYFzbiGix/kblAEiviToVFQVZKxoDm20/img.png?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/cLNZdf/hyYG10MKF2/8SOFXDh9avXP4Wo6m0H2dK/img.png?width=1011&amp;amp;height=762&amp;amp;face=0_0_1011_762');&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;[AWS] VPC NAT Gateway 구성하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AWS에서 NAT Gateway란? NAT 게이트웨이는 NAT(Network Address Translation, 네트워크 주소 변환) 서비스입니다. 프라이빗 서브넷의 인스턴스가 VPC 외부의 서비스에 연결할 수 있지만 외부 서비스에서 이러한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kimjingo.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jaeyumn.tistory.com/342&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jaeyumn.tistory.com/342&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745136255138&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;[AWS] Amazon Relational Database Service Provisioned Storage 비용&quot; data-og-description=&quot;AWS로 서버를 설정하고 배포한 후 청구서에 비용이 조금씩 올라가기 시작했다. $0.131 per GB-month of provisioned GP3 storage running MySQL 이 녀석이 문제였다. 지금은 $0.00 per GB-month of provisioned GP2 storage under m&quot; data-og-host=&quot;jaeyumn.tistory.com&quot; data-og-source-url=&quot;https://jaeyumn.tistory.com/342&quot; data-og-url=&quot;https://jaeyumn.tistory.com/342&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zLHiz/hyYFDdI70B/5VkraWcg2fKSZRWHJKvsQK/img.png?width=800&amp;amp;height=102&amp;amp;face=0_0_800_102,https://scrap.kakaocdn.net/dn/3nNrR/hyYFAVASNT/JZJKewow0e7qs5BiFgguO1/img.png?width=800&amp;amp;height=102&amp;amp;face=0_0_800_102,https://scrap.kakaocdn.net/dn/cq7Ntj/hyYIkEzknI/1oCRDlWEgXz96oEyM6Lj1k/img.png?width=2067&amp;amp;height=265&amp;amp;face=0_0_2067_265&quot;&gt;&lt;a href=&quot;https://jaeyumn.tistory.com/342&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jaeyumn.tistory.com/342&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zLHiz/hyYFDdI70B/5VkraWcg2fKSZRWHJKvsQK/img.png?width=800&amp;amp;height=102&amp;amp;face=0_0_800_102,https://scrap.kakaocdn.net/dn/3nNrR/hyYFAVASNT/JZJKewow0e7qs5BiFgguO1/img.png?width=800&amp;amp;height=102&amp;amp;face=0_0_800_102,https://scrap.kakaocdn.net/dn/cq7Ntj/hyYIkEzknI/1oCRDlWEgXz96oEyM6Lj1k/img.png?width=2067&amp;amp;height=265&amp;amp;face=0_0_2067_265');&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;[AWS] Amazon Relational Database Service Provisioned Storage 비용&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AWS로 서버를 설정하고 배포한 후 청구서에 비용이 조금씩 올라가기 시작했다. $0.131 per GB-month of provisioned GP3 storage running MySQL 이 녀석이 문제였다. 지금은 $0.00 per GB-month of provisioned GP2 storage under m&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jaeyumn.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://shortcuts.tistory.com/53#google_vignette&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://shortcuts.tistory.com/53#google_vignette&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745136471192&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;[해결] AWS 퍼블릭 IPv4 유료화: 프리티어인데 과금되었다면&quot; data-og-description=&quot;2024년 2월 1일부터 AWS의 Public IPv4 주소 사용이 유료화되었다. https://aws.amazon.com/ko/about-aws/whats-new/2024/02/aws-free-tier-750-hours-free-public-ipv4-addresses/ IPv4 주소가 고갈되면서 IPv6로의 이전을 장려하기 위&quot; data-og-host=&quot;shortcuts.tistory.com&quot; data-og-source-url=&quot;https://shortcuts.tistory.com/53#google_vignette&quot; data-og-url=&quot;https://shortcuts.tistory.com/53&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oILSE/hyYIdZJuYF/thtFpughCjeYyzoqLANSy0/img.png?width=800&amp;amp;height=267&amp;amp;face=0_0_800_267,https://scrap.kakaocdn.net/dn/bwqN2b/hyYH60CUc1/rqg5pFQPAsvrZK9jVbfcv1/img.png?width=800&amp;amp;height=267&amp;amp;face=0_0_800_267,https://scrap.kakaocdn.net/dn/ctkCm0/hyYIbHClL7/cFxrfiqpkIKnPUAQ8qIhQK/img.png?width=1421&amp;amp;height=475&amp;amp;face=0_0_1421_475&quot;&gt;&lt;a href=&quot;https://shortcuts.tistory.com/53#google_vignette&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://shortcuts.tistory.com/53#google_vignette&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oILSE/hyYIdZJuYF/thtFpughCjeYyzoqLANSy0/img.png?width=800&amp;amp;height=267&amp;amp;face=0_0_800_267,https://scrap.kakaocdn.net/dn/bwqN2b/hyYH60CUc1/rqg5pFQPAsvrZK9jVbfcv1/img.png?width=800&amp;amp;height=267&amp;amp;face=0_0_800_267,https://scrap.kakaocdn.net/dn/ctkCm0/hyYIbHClL7/cFxrfiqpkIKnPUAQ8qIhQK/img.png?width=1421&amp;amp;height=475&amp;amp;face=0_0_1421_475');&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;[해결] AWS 퍼블릭 IPv4 유료화: 프리티어인데 과금되었다면&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2024년 2월 1일부터 AWS의 Public IPv4 주소 사용이 유료화되었다. https://aws.amazon.com/ko/about-aws/whats-new/2024/02/aws-free-tier-750-hours-free-public-ipv4-addresses/ IPv4 주소가 고갈되면서 IPv6로의 이전을 장려하기 위&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;shortcuts.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1014</guid>
      <comments>https://hj0216.tistory.com/1014#entry1014comment</comments>
      <pubDate>Sun, 20 Apr 2025 17:14:27 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] AWS SDK, CLI</title>
      <link>https://hj0216.tistory.com/1013</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; 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;SDK&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Software Developement Kit, 애플리케이션을 만들기 위한 소프트웨어 개발 도구의 집합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shared Credentials 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 사용하는 컴퓨터에 AWS 자격 증명을 설정함으로써, 해당 컴퓨터를 사용하는 사람은 별도의 설정 없이 SDK나 CLI를 사용하여 AWS 서비스를 사용할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/sdkref/latest/guide/file-format.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.aws.amazon.com/ko_kr/sdkref/latest/guide/file-format.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743940314905&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;공유 config 및 credentials 파일을 사용하여 AWS SDKs 및 도구 전역 구성 - AWS SDKs 및 도구&quot; data-og-description=&quot;애플리케이션이 여러 애플리케이션을 실행하는 서버에 있는 경우 기본 프로파일 대신 항상 명명된 프로파일을 사용하는 것이 좋습니다. 기본 프로필은 환경의 모든 AWS 애플리케이션에서 자동&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/ko_kr/sdkref/latest/guide/file-format.html&quot; data-og-url=&quot;https://docs.aws.amazon.com/ko_kr/sdkref/latest/guide/file-format.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/sdkref/latest/guide/file-format.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/ko_kr/sdkref/latest/guide/file-format.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;공유 config 및 credentials 파일을 사용하여 AWS SDKs 및 도구 전역 구성 - AWS SDKs 및 도구&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;애플리케이션이 여러 애플리케이션을 실행하는 서버에 있는 경우 기본 프로파일 대신 항상 명명된 프로파일을 사용하는 것이 좋습니다. 기본 프로필은 환경의 모든 AWS 애플리케이션에서 자동&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;IAM 사용자 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IAM - 사용자 - 사용자 생성 클릭 - 사용자 이름 입력 - 권한 옵션 선택 - 사용자 생성 클릭&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 폴더 생성 후, config 또는 credentials 파일 생성&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Linux/Mac: ~/.aws/config 또는 ~/.aws/credentials&lt;br /&gt;Window: %USERPROFILE%/.aws/&amp;nbsp;config 또는&amp;nbsp;%USERPROFILE%/.aws/&amp;nbsp;credentials&lt;/blockquote&gt;
&lt;pre id=&quot;code_1743941824063&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[default]
region = ap-northeast-2
aws_access_key_id = YOUR_AWS_ACCESS_KEY
aws_secret_access_key = YOUR_AWS_SECRET_KEY&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS CLI 설치 및 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743943593915&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;최신 버전의 AWS CLI 설치 또는 업데이트 - AWS Command Line Interface&quot; data-og-description=&quot;이전 버전에서 업데이트하는 경우 unzip 명령을 실행하면 기존 파일을 덮어쓸지 묻는 메시지가 표시됩니다. 스크립트 자동화와 같은 경우에 이러한 프롬프트를 건너뛰려면 unzip에 대한 -u 업데이&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html&quot; data-og-url=&quot;https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;최신 버전의 AWS CLI 설치 또는 업데이트 - AWS Command Line Interface&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이전 버전에서 업데이트하는 경우 unzip 명령을 실행하면 기존 파일을 덮어쓸지 묻는 메시지가 표시됩니다. 스크립트 자동화와 같은 경우에 이러한 프롬프트를 건너뛰려면 unzip에 대한 -u 업데이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AWS SDK와 CLI 사용 시, 명령을 실행하려는 서비스에 대한 권한이 공유 자격 증명에 설정되어있어야 함&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://awscli.amazonaws.com/v2/documentation/api/latest/reference/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://awscli.amazonaws.com/v2/documentation/api/latest/reference/index.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743943712536&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;aws &amp;mdash; AWS CLI 2.25.11 Command Reference&quot; data-og-description=&quot;Synopsis aws [options] [parameters] Use aws command help for information on a specific command. Use aws help topics to view a list of available help topics. The synopsis for each command shows its parameters and their usage. Optional parameters are shown i&quot; data-og-host=&quot;awscli.amazonaws.com&quot; data-og-source-url=&quot;https://awscli.amazonaws.com/v2/documentation/api/latest/reference/index.html&quot; data-og-url=&quot;https://awscli.amazonaws.com/v2/documentation/api/latest/reference/index.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://awscli.amazonaws.com/v2/documentation/api/latest/reference/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://awscli.amazonaws.com/v2/documentation/api/latest/reference/index.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;aws &amp;mdash; AWS CLI 2.25.11 Command Reference&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Synopsis aws [options] [parameters] Use aws command help for information on a specific command. Use aws help topics to view a list of available help topics. The synopsis for each command shows its parameters and their usage. Optional parameters are shown i&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;awscli.amazonaws.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1013</guid>
      <comments>https://hj0216.tistory.com/1013#entry1013comment</comments>
      <pubDate>Thu, 10 Apr 2025 21:05:10 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] Lambda</title>
      <link>https://hj0216.tistory.com/1012</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Serverless&lt;/blockquote&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; * 이벤트 기반 실행: HTTP 요청, DB 변경, 파일 업로드 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 비용 효율적: 필요할 때만 실행되며, 실행 시간에 따라 비용이 청구됨 -&amp;gt; 트래픽이 자주 변하는 애플리케이션에 이상적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 작은 코드 단위 -&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;blockquote data-ke-style=&quot;style2&quot;&gt;AWS Lambda&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Lambda 함수 생성 및 실행&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 함수 생성 클릭&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; * 이벤트 이름, 이벤트 JSON 입력 후 저장&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;blockquote data-ke-style=&quot;style2&quot;&gt;Lambda 함수 삭제&lt;/blockquote&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;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1012</guid>
      <comments>https://hj0216.tistory.com/1012#entry1012comment</comments>
      <pubDate>Wed, 9 Apr 2025 19:34:02 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] DynamoDB</title>
      <link>https://hj0216.tistory.com/1011</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;DynamoDB&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서 제공하는 매우 빠르고 확장 가능한 완전 관리형 클라우드 NoSQL 데이터 베이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * NoSQL: RDB의 데이터 일관성 제약 일부 완화, 수평적으로 확장 가능한 성능, 스키마 없는 데이터 모델에 최적화. 몇 가지 방법으로 데이터를 효율적으로 쿼리할 수 있으나, 그 외에는 쿼리 비용이 높고 속도가 느림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Read 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Eventually Consistent Read: 기본, 최근 완료된 쓰기 작업의 결과가 반영되지 않을 수도 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Strongly Consistent Read: 가장 최신 데이터로 응답, 네트워크 지연 또는 중단 발생 시, 사용이 어려움&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 과금 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 스토리지 요금&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 읽기/쓰기 용량 유닛 요금&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 1읽기 용량 유닛 = 초당 최대 2건의 읽기 작업 제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 1쓰기 용량 유닛 = 초당 최대 1건의 쓰기 작업 제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(프리티어: 매월 25GB 스토리지 및 읽기/쓰기 용량 유닛이 각 25개씩 제공)&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;DynamoDB 기본 구성&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Table&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Attribute1&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Attribute2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Item1&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Value&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Item2&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Value&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Table: Item의 집합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Item: Attribute의 집합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Attribute: Key(문자열)-Value 방식의 데이터&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;DynamoDB 데이터 타입&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; * Number: 양수, 음수 또는 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * String: 문자열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Binary: 압축 텍스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Boolean: true/false&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Null: null&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; * List: 순서가 지정된 값 모음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Map: 정렬되지 않은 이름-값 쌍의 모음&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; * String Set, Number Set, Binary Set&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;DynamoDB 파티션&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DynamoDB가 데이터를 저장하는 곳&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Primary Key&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Table 내에서 각 Item을 구분하는 고유 식별자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 단순 기본 키: 파티션 키만 사용 / 복합 기본 키: 파티션 키와 정렬 키를 함께 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Partition Key&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 데이터가 저장되어 있는 파티션을 결정하기 위한 키&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 내부 해시 함수에 대한 입력으로 파티션 키 값을 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 출력에 따라 항목을 저장할 파티션이 결정됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Sort Key&lt;/p&gt;
&lt;p data-ke-size=&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;style2&quot;&gt;보조 인덱스&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Local Secondary Index: 테이블과 파티션 키는 동일하지만 정렬 키는 다른 인덱스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 테이블 당 최대 5개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 파티션 키와 다른 정렬 키를 사용해서 테이블 내에 아이템을 찾을 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 예: 테이블의 파티션 키: Name, 정렬 키: Subject -&amp;gt; LSI: 파티션 키: Name, 정렬 키: Score&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Global Secondary Index: 파티션 키 및 정렬 키가 테이블의 파티션 키 및 정렬 키와 다를 수 있는 인덱스&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 테이블 당 최대 20개&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 다른 값으로 파티션을 나눌 수 있고, 해당하는 파티션 내에서 아이템들을 정렬할 수 있음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 예: 테이블의 파티션 키: Name, 정렬 키: Subject -&amp;gt; GSI: 파티션 키: &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Subject&lt;span&gt; &lt;/span&gt;&lt;/span&gt;, 정렬 키: Score&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;blockquote data-ke-style=&quot;style2&quot;&gt;DynamoDB 데이터 조회 방식&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* &lt;i&gt;&lt;u&gt;&lt;b&gt;Query&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;: 기본 키 값을 기반으로 항목을 찾는 방식&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* Scan: 테이블 또는 보조 인덱스의 모든 항목을 읽어와서 필터링하는 방식&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Query 방식은 학번을 곧바로 부르는 방식, Scan은 한 명씩 불러 학번이 일치하는지 확인하는 방식으로 데이터를 조회할 때는 되도록이면 Query를 사용하는 것이 좋음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;DynamoDB 테이블 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 테이블 생성 클릭&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;3. 테이블 생성 클릭&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;DynamoDB 데이터 입력&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;1. 작업 - Create Item&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp; * 새 속성 추가를 통해 값 입력&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;2. 항목 생성 클릭&lt;/span&gt;&lt;/span&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;DynamoDB 데이터 조회&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;1. 표 항목 탐색 클릭&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;2. 쿼리 선택 - 테이블 또는 인덱스 선택 - 파티션 키 입력 후 실행 클릭&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;3. 스캔 선택 - 필터 클릭 - 속성 이름, 유형, 조건, 값 입력 후 실행 클릭&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &amp;nbsp; * Query 방식은 학번을 곧바로 부르는 방식, Scan은 한 명씩 불러 학번이 일치하는지 확인하는 방식으로 데이터를 조회할 때는 되도록이면 Query를 사용하는 것이 좋음 &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;style2&quot;&gt;DynamoDB 테이블 삭제&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;1. 테이블 선택 후 삭제 클릭&lt;/span&gt;&lt;/span&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;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1011</guid>
      <comments>https://hj0216.tistory.com/1011#entry1011comment</comments>
      <pubDate>Tue, 8 Apr 2025 19:30:28 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] CloudFront</title>
      <link>https://hj0216.tistory.com/1010</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; 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;CloudFront: CDN 서비스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 서비스와 CloudFront 데이터 전송 무료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;CDN(Content Delivery Network): 콘턴츠 전송 네트워크, 콘텐츠를 전 세계로 빠르게 전송할 수 있게 해주는 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * CDN이 전 세계로 컨텐츠를 빠르게 전송할 수 있는 원리 = 캐싱&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* CloudFront&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Distribution&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 각 배포는 고유의 도메인을 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * Route53을 사용해서 자신의 도메인과 연결 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Origin&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 원본 파일을 가져오는 위치(기본: S3, 커스텀: EC2, ELB, 외부 서버 등)&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;772&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uGD65/btsNafptzu9/h1P7pPc6cWmbrTPMNQvZjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uGD65/btsNafptzu9/h1P7pPc6cWmbrTPMNQvZjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uGD65/btsNafptzu9/h1P7pPc6cWmbrTPMNQvZjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuGD65%2FbtsNafptzu9%2Fh1P7pPc6cWmbrTPMNQvZjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;772&quot; height=&quot;441&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;S3를 Origin으로 하는 CloudFront에서 파일 가져오기&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;CloudFront 배포 만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. CloudFront - CloudFront 배포 생성 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Origin Domain 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 사용자가 CloudFront를 통해 버킷의 객체를 가져오려고 할 때, Edge Location에 해당 객체가 캐싱이 되고 빠르게 전송될 수 있게 됨&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. Create new OAC 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 이름 입력 - create 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 웹 애플리케이션 방화벽(WAF) 설정(유료)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 기본값 루트 객체: CloudFront 배포의 URL로 접속할 경우에 기본적으로 나오게 될 객체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 배포 생성 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 정책 복사 클릭: CloudFront가 S3 버킷 객체에 접근할 수 있도록 S3 버킷 정책 업데이트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. S3 - 버킷 - 권한 탭 - 버킷 정책 편집 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 정책을 직접 만들고 싶은 경우: &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Amazon S3 오리진에 대한 액세스 제한&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 버킷 ARN, CloudFront ARN 값 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11. 객체 URL로 들어갈 경우, AccessDenied 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12. 배포 - 배포 도메인 이름 복사 - 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;13. 개발자 도구 - network 탭 - reload page 클릭 후, docs 탭에 CloudFront 주소 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Header - x-cache: Miss from cloudfront(캐시를 가져올 경우, Hit / 캐시에 없을 경우, Miss)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CloudFront 삭제&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;2. 삭제(비활성화 클릭 후 삭제가 활성화되기까지 시간이 좀 걸림)&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1010</guid>
      <comments>https://hj0216.tistory.com/1010#entry1010comment</comments>
      <pubDate>Mon, 7 Apr 2025 19:29:58 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] S3</title>
      <link>https://hj0216.tistory.com/1009</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;S3(Simple Storage Service)&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;무제한 파일 저장 스토리지&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* URL을 통해 손쉽게 파일 공유&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 정작 웹사이트 호스팅 가능&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 객체 스토리지 특성 상 일부만 수정이 불가능하므로 최종 파일을 저장하는 용도로 사용&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 빠른 속도로 Read/Write하는 작업에는 부적합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 이미지, 동영상, 문서 등 미디어 파일을 보관하거나 로그 파일을 보관하기에 적합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * AWS 서비스 데이터 백업 등 데이터 백업에도 적합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구성&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Bucket: 객체를 담는 최상위 단위&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Object: Bucket에 담는 데이터 단위(파일)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Metadata: Object에 대한 여러 가지 정보를 담고 있는 데이터&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Policy: Bucket과 Object에 대한 접근을 통제하는 권한 정보&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* S3 스토리지 클래스&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스토리지를 저장하는 형태&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 종류&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Standard: 최소 3개의 가용 영역에 데이터 저장, 자주 액세스하는 데이터 전용(내구성, 가용성, 성능)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Standard IA(Infrequent Access): 자주 접근하진 않지만 필요할 때 빠르게 액세스해야 하는 데이터에 적합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 최소 3개의 가용 영역에 객체 중복 저장(고가용성, 복원력)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * One-zone IA: 단일 가용 역역에만 데이터 저장(복원력 X)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Glacier: 자주 접근하지 않는 데이터를 저렴한 비용으로 보관&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 객체 라이프 사이클 관리&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 1. &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Standard 클래스 저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. 30일 경과 후, 자주 사용하지 않게 되면 Standard IA 클래스로 변경&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 3. &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Standard IA 클래스에서 &lt;/span&gt;30일 경과 후, 데이터 아카이빙 목적으로 Glacier 클래스로 변경&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Object Storage&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 객체로 관리하는 저장소&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;객체 구성&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 아이디&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 데이터&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 메타데이터&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 규모와 유연성이 필요한 애플리케이션 구축 시 적합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 분석, 백업, 아카이브 목적으로 사용 가능&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 데이터 일부 수정 불가 -&amp;gt; 전체 덮어쓰기로 수정&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;File Storage&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 파일로 관리하는 저장소&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 애플리케이션이 공유 파일에 액세스하거나 파일 시스템이 있어야 하는 경우에 적합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 파일 시스템을 통해 데이터를 파일 단위로 저장&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 파일의 일부분만 수정 가능&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 예시: NAS, 사용자 홈 디렉터리 등&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Block Storage&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 블록(저장 공간을 나누는 단위)으로 관리하는 저장소&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 데이터를 작은 블록 단위로 나누어 저장하며, 각 블록은 고유한 주소 또는 식별자를 가짐&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 파일 시스템이나 메타 데이터 없이 순수 데이터 블록만 저장&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 낮은 지연 시간 -&amp;gt; 고성능 애플리케이션에서 사용하기 적합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 예시: 데이터베이스 시스템, 가상 머신, 클라우드 스토리지 등&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; 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;992&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edHGnb/btsMS2Q4ro3/XiokOOFGAF8NnpkbBGYu10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edHGnb/btsMS2Q4ro3/XiokOOFGAF8NnpkbBGYu10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edHGnb/btsMS2Q4ro3/XiokOOFGAF8NnpkbBGYu10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedHGnb%2FbtsMS2Q4ro3%2FXiokOOFGAF8NnpkbBGYu10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;992&quot; height=&quot;459&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Object Storage&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;File Storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;데이터를 객체 단위로 관리&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;데이터를 파일 단위로 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;객체 데이터 일부분만 수정 불가능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;파일 내용 일부분만 수정 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;운영체제에서 직접 접근 불가&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;운영체제에서 직접 접근 가능(&lt;b&gt;볼륨 마운트&lt;/b&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;큰 확장에 유연하게 대처 가능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;큰 확장에 대처하기 어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;다수의 사본을 분산시켜 저장&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;사본 저장하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 볼륨 마운트: 외부 저장소(예: 하드 드라이브, SSD, 네트워크 스토리지, 클라우드 스토리지)를 운영체제의 특정 경로(마운트 포인트)에 연결하여 일반적인 파일 시스템처럼 사용할 수 있도록 하는 것(예: &lt;b&gt;Windows&lt;/b&gt;: D:\, E:\ 같은 드라이브 문자로 마운트됨)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;S3 버킷 생성&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 버킷 이름 입력&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 버킷 이름은 DNS형식으로 전 세계에서 유일해야 함&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 객체 소유권 선택&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * ACL 비활성화: 버킷의 모든 객체 소유권을 현재 AWS 계정이 소유&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 퍼블릭 액세스 차단 설정 선택&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 버킷 만들기 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;S3 버킷에 파일 업로드 및 다운로드&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 업로드 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 파일 추가 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 업로드 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 다운로드 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;S3 버킷에 폴더 생성&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 폴더 개념: 파일 이름의 Prefix 개념&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 폴더 만들기 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 폴더 이름 입력&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 폴더 만들기 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 파일 - 작업 - 복사 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;5. 대상 - S3 찾아보기 - 폴더 클릭 - 대상 선택&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;6. 복사 클릭&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;MobaXTerm으로 S3 다루기&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Cyberduck: FTP, SFTP 등의 다양한 형태로 원격 서버에 접속해서 사용할 수 있는 스토리지 브라우저&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. IAM - 사용자 - 사용자 추가&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 사용자 이름 입력&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 권한 옵션 - 직접 정책 연결 - AmazonS3FullAccess 선택&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 사용자 생성&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;5. 보안 자격 증명 탭 - 액세스 키 - 액세스 키 만들기 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;6. 사용 사례 선택&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * CLI: 키와 시크릿 키를 통해 접속할 때 사용하는 옵션&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;7. 액세스 키 만들기 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/AWS-%F0%9F%93%9A-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%ED%88%B4%EB%A1%9C-S3-%EC%A0%91%EC%86%8D%ED%95%B4%EC%84%9C-%EA%B0%84%ED%8E%B8%ED%95%98%EA%B2%8C-%EB%8B%A4%EB%A3%A8%EA%B8%B0-MobaXterm&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/AWS-%F0%9F%93%9A-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%ED%88%B4%EB%A1%9C-S3-%EC%A0%91%EC%86%8D%ED%95%B4%EC%84%9C-%EA%B0%84%ED%8E%B8%ED%95%98%EA%B2%8C-%EB%8B%A4%EB%A3%A8%EA%B8%B0-MobaXterm&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743913623875&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;[AWS]   클라이언트 툴로 S3 접속해서 간편하게 다루기 [MobaXterm]&quot; data-og-description=&quot;S3 클라이언트 툴 웹 콘솔 브라우저로 S3 서비스를 다루기에는 한국인 정서에는 좀 많이 느린편이라 답답하다. 그래서 보통 aws cli 커맨드로 S3를 다루는데, 그래도 간단한 작업 같은 경우 GUI 환경&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/AWS-%F0%9F%93%9A-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%ED%88%B4%EB%A1%9C-S3-%EC%A0%91%EC%86%8D%ED%95%B4%EC%84%9C-%EA%B0%84%ED%8E%B8%ED%95%98%EA%B2%8C-%EB%8B%A4%EB%A3%A8%EA%B8%B0-MobaXterm&quot; data-og-url=&quot;https://inpa.tistory.com/entry/AWS-%F0%9F%93%9A-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%ED%88%B4%EB%A1%9C-S3-%EC%A0%91%EC%86%8D%ED%95%B4%EC%84%9C-%EA%B0%84%ED%8E%B8%ED%95%98%EA%B2%8C-%EB%8B%A4%EB%A3%A8%EA%B8%B0-MobaXterm&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ejk5nO/hyYA4PXbJQ/nHqS3Ktk1IUOTxLXB5Xhe1/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/c0L9zw/hyYCfwyNvh/EHiKtyrBLNm2xNDlt6fbK1/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/sCjUg/hyYBhBMyZO/e6SKsl0WnKiE5SE8ak8CB1/img.png?width=1196&amp;amp;height=760&amp;amp;face=0_0_1196_760&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/AWS-%F0%9F%93%9A-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%ED%88%B4%EB%A1%9C-S3-%EC%A0%91%EC%86%8D%ED%95%B4%EC%84%9C-%EA%B0%84%ED%8E%B8%ED%95%98%EA%B2%8C-%EB%8B%A4%EB%A3%A8%EA%B8%B0-MobaXterm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/AWS-%F0%9F%93%9A-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%ED%88%B4%EB%A1%9C-S3-%EC%A0%91%EC%86%8D%ED%95%B4%EC%84%9C-%EA%B0%84%ED%8E%B8%ED%95%98%EA%B2%8C-%EB%8B%A4%EB%A3%A8%EA%B8%B0-MobaXterm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ejk5nO/hyYA4PXbJQ/nHqS3Ktk1IUOTxLXB5Xhe1/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/c0L9zw/hyYCfwyNvh/EHiKtyrBLNm2xNDlt6fbK1/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/sCjUg/hyYBhBMyZO/e6SKsl0WnKiE5SE8ak8CB1/img.png?width=1196&amp;amp;height=760&amp;amp;face=0_0_1196_760');&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;[AWS]   클라이언트 툴로 S3 접속해서 간편하게 다루기 [MobaXterm]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;S3 클라이언트 툴 웹 콘솔 브라우저로 S3 서비스를 다루기에는 한국인 정서에는 좀 많이 느린편이라 답답하다. 그래서 보통 aws cli 커맨드로 S3를 다루는데, 그래도 간단한 작업 같은 경우 GUI 환경&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;connection lost due to error 10054&lt;br /&gt;* 비밀번호 확인&lt;br /&gt;&lt;br /&gt;connection failed due to error 75795 &lt;br /&gt;* 버킷 접근 권한 확인&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;S3 정적 웹사이트 호스팅&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 속성 탭 - 정적 웹 사이트 호스팅 - 편집 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 활성화 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 인덱스 문서 등 입력&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 변경 사항 저장 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 버킷 최상위 경로에 index.html 업로드&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 403 Forbidden&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 버킷 객체에 대한 접근 권한 문제&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 권한 탭 - 퍼블릭 액세스 차단 - 편집 클릭 - 차단 해제 후 변경 사항 저장 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 버킷 정책(버킷과 객체들에 대한 접근 권한 설정, 각 정책은 JSON 형태로 작성) - 편집 - 버킷 ARN 복사 - 정책 생성기 클릭 - Select type of Policy: S3 Bucket Policy - Principal(리소스에 대한 액세스가 허용되거나 거부되는 사용자, 계정, 서비스 또는 기타 Entity를 지정하는 옵션): *(모든 사용자) - Select Actions: GetObject - ARN 입력 후 '/*(버킷 내 모든 객체에 접근할 수 있도록 하기 위함)' 추가 - Add Statement 클릭 - Generate Policy 클릭 - JSON 복사 후 붙여넣기 - 변경 사항 저장 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;S3 버킷 삭제&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 버킷을 삭제하기 위해서 버킷이 비어있어야 함&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 버킷 선택&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 비어 있음 클릭(= 버킷 비우기)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 비어 있음 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 종료 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;5. 삭제 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;6. 버킷 삭제 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* 버킷 이름은 DNS 형식으로 전 세계에서 유일해야 하며, 삭제하면 곧바로 같은 이름으로 버킷을 만들기 어려울 수 있음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1009</guid>
      <comments>https://hj0216.tistory.com/1009#entry1009comment</comments>
      <pubDate>Sun, 6 Apr 2025 13:33:00 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] CloudWatch</title>
      <link>https://hj0216.tistory.com/1008</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&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;CloudWatch&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 모니터링 서비스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 로그 모니터링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 지표 수집 및 모니터링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 알람&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 이벤트 모니터링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;CloudWatch 알람 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. EC2 생성&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. 알람 생성할 지표의 더 보기 메뉴(세로 점 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;5. 종모양 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 지표 및 조건 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 새 주제 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 경보 이름 및 설명 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 확인 후, 경보 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. SNS 구독 보기에서 이메일 확인 메일 처리 상태 확인&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1008</guid>
      <comments>https://hj0216.tistory.com/1008#entry1008comment</comments>
      <pubDate>Mon, 31 Mar 2025 19:29:30 +0900</pubDate>
    </item>
    <item>
      <title>java.lang.AssertionError: No value at JSON path &amp;quot;$.userId&amp;quot;</title>
      <link>https://hj0216.tistory.com/1007</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  오류 &lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1743207659050&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.AssertionError: No value at JSON path &quot;$.userId&quot;
	at org.springframework.test.util.JsonPathExpectationsHelper.evaluateJsonPath(JsonPathExpectationsHelper.java:351)
	at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:148)
	at org.springframework.test.web.servlet.result.JsonPathResultMatchers.lambda$value$2(JsonPathResultMatchers.java:112)
	at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:214)
	at com.mini_prioject.display_board.controller.UserControllerTest.createUser_WhenValidRequest(UserControllerTest.java:81)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)&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;style2&quot;&gt;&lt;b&gt; ✍️원인 &lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockito는 같은 객체(request)가 전달될 때만 응답을 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 mockMvc에서 보낸 JSON 요청을 Spring이 다시 객체로 만들면, 원래 request와 다른 객체로 인식됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;style2&quot;&gt;&lt;b&gt; ✅해결 &lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 객체 일치 대신 any(JoinRequest.class)를 사용&lt;/p&gt;
&lt;pre id=&quot;code_1743208070810&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;given(userService.join(any(JoinRequest.class))).willReturn(response);&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  참고 &lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chap GPT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Java with Error</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1007</guid>
      <comments>https://hj0216.tistory.com/1007#entry1007comment</comments>
      <pubDate>Sat, 29 Mar 2025 09:28:35 +0900</pubDate>
    </item>
    <item>
      <title>java.lang.AssertionError: Status expected:&amp;lt;201&amp;gt; but was:&amp;lt;401&amp;gt;</title>
      <link>https://hj0216.tistory.com/1006</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt; 오류&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1743190878625&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.AssertionError: Status expected:&amp;lt;201&amp;gt; but was:&amp;lt;401&amp;gt;
	at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:61)
	at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:128)
	at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:640)
	at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:214)
	at com.mini_prioject.display_board.controller.UserControllerTest.createUserSuccessTest(UserControllerTest.java:77)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;✍️원인&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;By default, tests annotated with @WebMvcTest will also auto-configure Spring Security&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666; text-align: left;&quot;&gt;@WebMvcTest&lt;/span&gt; 사용 시, 스프링 시큐리티가 자동으로 구성하는 Configuration 파일들을 불러와서 사용&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자동으로 구성되는 SpringBootWebSecurityConfiguration이 권한을 요청하지만 인증 권한을 가진 사용자로 테스트하지 않아 오류 발생&lt;/p&gt;
&lt;pre id=&quot;code_1743200857496&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {

	@Bean
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
		http.authorizeRequests()
     	.anyRequest().authenticated()
        .and()
        .formLogin()
        .and()
        .httpBasic();
		return http.build();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;@SpringBootTest&lt;br /&gt;통합 테스트에서 주로 사용&lt;br /&gt;애플리케이션의 모든 컴포넌트들을 로드&lt;br /&gt;실제 애플리케이션 환경과 유사한 환경에서 테스트&lt;br /&gt;&lt;br /&gt;@WebMvcTest&lt;br /&gt;주로 웹 계층만 테스트할 때 사용(컨트롤러 단위 테스트)&lt;br /&gt;주로 컨트롤러(Controller)와 관련된 빈들만 로드하므로, 더 가벼우며 빠른 테스트가 가능(securityConfig 로드 x)&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;✅해결&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 권한을 가진 User 생성( &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;@WithMockUser&lt;/span&gt; )&lt;/p&gt;
&lt;pre id=&quot;code_1743201212885&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@WithMockUser(username = &quot;testUser&quot;, roles = &quot;USER&quot;)
@WebMvcTest(UserController.class)
class UserControllerTest {  }&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;실제 DB에서 가져온 사용자 정보로 테스트 수행(@WithUserDetails)&lt;/p&gt;
&lt;pre id=&quot;code_1743201717945&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 테스트 User를 반환하는 로직
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return User.builder()
            .username(&quot;testUser&quot;)
            .password(new BCryptPasswordEncoder().encode(&quot;password123&quot;))
            .roles(&quot;USER&quot;)
            .build();
    }
}

// 실제 DB에 저장된 User를 반환하는 로직
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findByUsername(username)
            .map(user -&amp;gt; User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .roles(user.getRole())
                .build())
            .orElseThrow(() -&amp;gt; new UsernameNotFoundException(&quot;User not found&quot;));
    }
}

@WebMvcTest(UserController.class)
@Import(SecurityConfig.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @DisplayName(&quot;Principal 내부 값 테스트&quot;)
    @WithUserDetails(value = &quot;testUser&quot;)
    void testWithUserDetails() throws Exception {
        mockMvc.perform(get(&quot;/api/v1/profile&quot;))
               .andExpect(status().isOk())
               .andExpect(jsonPath(&quot;$.username&quot;).value(&quot;testUser&quot;));
    }
}&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;style2&quot;&gt;@WithMockUser - 인증된 사용자&lt;br /&gt;@WithAnonymousUser - 미인증 사용자 (principal에서 &quot;anonymous&quot;가 들어가있음)&lt;br /&gt;@WithUserDetails - 메서드가 principal 내부의 값을 직접 사용하는 경우 (별도의 사전 설정 필요)&lt;br /&gt;&amp;nbsp; - WithUserDetails를 사용하면 UserDetailsService가 동작해서 Principal을 설정해줌&lt;br /&gt;&amp;nbsp; - loadUserByUsername()이 직접 UserDetails를 반환&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;blockquote data-ke-style=&quot;style2&quot;&gt; 참고&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://suhyeon-developer.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://suhyeon-developer.tistory.com/38&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743201112670&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] Controller Unit Test에서 발생한 401, 403 에러를 해결해보자! (+ Spring Security)&quot; data-og-description=&quot;❗️컨트롤러 테스트코드를 작성하는 와중에 401과 403 에러를 마주쳤다. 해결하는건 크게 어렵지 않았다!!! 컨트롤러 단위 테스트 진행 과정 먼저, 사용한 기술은 이러하다. ▶︎ Spring Data JPA , Spr&quot; data-og-host=&quot;suhyeon-developer.tistory.com&quot; data-og-source-url=&quot;https://suhyeon-developer.tistory.com/38&quot; data-og-url=&quot;https://suhyeon-developer.tistory.com/38&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pMPoq/hyYvhW274d/NbkUqvpSwKKuTkQKYHxVeK/img.jpg?width=800&amp;amp;height=464&amp;amp;face=0_0_800_464,https://scrap.kakaocdn.net/dn/hwtFh/hyYvvnpYC8/R8TOQW6YGvqYCvpqypnM10/img.jpg?width=800&amp;amp;height=464&amp;amp;face=0_0_800_464,https://scrap.kakaocdn.net/dn/ecSuez/hyYxKKw2k3/xbN9JFb0Y7UL79iycyl5cK/img.png?width=2644&amp;amp;height=336&amp;amp;face=0_0_2644_336&quot;&gt;&lt;a href=&quot;https://suhyeon-developer.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://suhyeon-developer.tistory.com/38&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pMPoq/hyYvhW274d/NbkUqvpSwKKuTkQKYHxVeK/img.jpg?width=800&amp;amp;height=464&amp;amp;face=0_0_800_464,https://scrap.kakaocdn.net/dn/hwtFh/hyYvvnpYC8/R8TOQW6YGvqYCvpqypnM10/img.jpg?width=800&amp;amp;height=464&amp;amp;face=0_0_800_464,https://scrap.kakaocdn.net/dn/ecSuez/hyYxKKw2k3/xbN9JFb0Y7UL79iycyl5cK/img.png?width=2644&amp;amp;height=336&amp;amp;face=0_0_2644_336');&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] Controller Unit Test에서 발생한 401, 403 에러를 해결해보자! (+ Spring Security)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;❗️컨트롤러 테스트코드를 작성하는 와중에 401과 403 에러를 마주쳤다. 해결하는건 크게 어렵지 않았다!!! 컨트롤러 단위 테스트 진행 과정 먼저, 사용한 기술은 이러하다. ▶︎ Spring Data JPA , Spr&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;suhyeon-developer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743201170393&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;[Spring]Security적용 후 Controller 테스트코드 작성 시 발생했던 오류들 (Feat. Junit5, csrf)&quot; data-og-description=&quot;Controller에서 모든 Board를 조회하는 메소드를 테스트 코드를 작성하던 중 일어난 일이다. 우선 코드를 먼저보자 사실 이 로직상은 아무 문제가 없다.하지만 다음과 같이 401 Unauthorized가 발생하는 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&quot; data-og-url=&quot;https://velog.io/@tjdtn0219/SpringSecurity적용-후-Controller-테스트코드-작성-시-발생했던-오류들-Feat.-Junit5-csrf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kiarF/hyYyQQ2FGD/pZmqf1ovPnrhUHoKGAzjp0/img.png?width=637&amp;amp;height=120&amp;amp;face=0_0_637_120,https://scrap.kakaocdn.net/dn/tTYGh/hyYyN7QYBN/WYmTIiTNz8KB51YjKv7C51/img.png?width=637&amp;amp;height=120&amp;amp;face=0_0_637_120,https://scrap.kakaocdn.net/dn/bm16IU/hyYxLo9GKG/Y2e1eb7k2HEDPQpdkVCmKk/img.png?width=2072&amp;amp;height=914&amp;amp;face=0_0_2072_914&quot;&gt;&lt;a href=&quot;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kiarF/hyYyQQ2FGD/pZmqf1ovPnrhUHoKGAzjp0/img.png?width=637&amp;amp;height=120&amp;amp;face=0_0_637_120,https://scrap.kakaocdn.net/dn/tTYGh/hyYyN7QYBN/WYmTIiTNz8KB51YjKv7C51/img.png?width=637&amp;amp;height=120&amp;amp;face=0_0_637_120,https://scrap.kakaocdn.net/dn/bm16IU/hyYxLo9GKG/Y2e1eb7k2HEDPQpdkVCmKk/img.png?width=2072&amp;amp;height=914&amp;amp;face=0_0_2072_914');&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;[Spring]Security적용 후 Controller 테스트코드 작성 시 발생했던 오류들 (Feat. Junit5, csrf)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Controller에서 모든 Board를 조회하는 메소드를 테스트 코드를 작성하던 중 일어난 일이다. 우선 코드를 먼저보자 사실 이 로직상은 아무 문제가 없다.하지만 다음과 같이 401 Unauthorized가 발생하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Java with Error</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1006</guid>
      <comments>https://hj0216.tistory.com/1006#entry1006comment</comments>
      <pubDate>Sat, 29 Mar 2025 07:45:54 +0900</pubDate>
    </item>
    <item>
      <title>java.lang.AssertionError: Status expected:&amp;lt;201&amp;gt; but was:&amp;lt;403&amp;gt;</title>
      <link>https://hj0216.tistory.com/1005</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt; 오류&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1743161253559&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.AssertionError: Status expected:&amp;lt;201&amp;gt; but was:&amp;lt;403&amp;gt;
	at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:61)
	at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:128)
	at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:640)
	at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:214)
	at com.mini_prioject.display_board.controller.UserControllerTest.createUserSuccessTest(UserControllerTest.java:60)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;✍️원인&lt;/b&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;@WebMvcTest 또는 MockMvc를 사용할 때는 &lt;b&gt;Spring Security가 자동으로 CSRF 검사를 활성화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 CSRF 토큰이 없는 요청은 &lt;b&gt;403 Forbidden 오류가 발생&lt;/b&gt;할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CSRF(Cross-Site Request Forgery)&lt;/b&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 공격자가 악의적인 코드를 심어놓은 사이트를 만들어놓고, 로그인 된 사용자가 클릭하게 만들어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;사용자 의지와 무관한 요청을 발생&lt;/b&gt;&lt;/span&gt;시키는 공격&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;사용자는 로그인 한 상태&lt;/b&gt;고 쿠키, 권한을 갖고있기 때문에 공격자가 위조한 웹사이트에 방문하게 되면 사용자 모르게 악의적인 POST, DELETE 요청을 정상 수행하도록 만들어버리는 공격&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 이를 해결하기 위해 스프링 시큐리티에서는 &quot;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;CSRF 토큰&lt;/b&gt;&lt;/span&gt;&quot; 을 이용해 토큰 값을 비교해서 일치하는 경우에만 메서드를 처리하도록 만든다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;@SpringBootTest&lt;br /&gt;@Controller, @Service, @Component, @Bean 등등 모두 다 불러와서 실제 환경과 동일하게 테스트 하고자 할 때 사용&lt;br /&gt;&lt;br /&gt;@WebMvcTest&lt;br /&gt;Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests&amp;nbsp; (i.e.@Controller, @ControllerAdvice, @JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans&amp;nbsp;but not&amp;nbsp;@Component, @Service or @Repository beans).&lt;br /&gt;&lt;br /&gt;By default, tests annotated with&amp;nbsp;@WebMvcTest will also auto-configure Spring Security and MockMvc&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;@MockitoBean으로 주입해준 UserService 빈은 #0이 뒤에 붙어서 스프링 빈으로 저장&lt;br /&gt;UserService 빈은 MockitoMock을 통해 반들어져있고 내부는 다 null로 채워져있으므로,텅 비어있는 userService 빈을 실제 동작하는 것처럼 흉내내기 위해서 Mocking(given)을 설정&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;&lt;b&gt;✅해결&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MockMvc 요청에 .with(csrf()) 추가&lt;/p&gt;
&lt;pre id=&quot;code_1743189729382&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mockMvc.perform(post(&quot;/api/v1/users&quot;)
        .with(csrf())
        .contentType(MediaType.APPLICATION_JSON)
        .content(objectMapper.writeValueAsString(request)))
    .andExpect(status().isCreated());&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;csrf() 옵션&lt;br /&gt;&lt;br /&gt;Creates a RequestPostProcessor that&amp;nbsp;will&amp;nbsp;automatically&amp;nbsp;populate&amp;nbsp;a&amp;nbsp;valid&amp;nbsp;CsrfToken&amp;nbsp;in&amp;nbsp;the&amp;nbsp;request.&lt;br /&gt;Returns: the SecurityMockMvcRequestPostProcessors.CsrfRequestPostProcessor for further customizations.&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;BeforeEach에 설정&lt;/p&gt;
&lt;pre id=&quot;code_1743190699078&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Autowired
private MockMvc mockMvc;

@Autowired
WebApplicationContext webApplicationContext;

@BeforeEach
void setUp() {
this.mockMvc = MockMvcBuilders
    .webAppContextSetup(webApplicationContext)
    .apply(springSecurity())
    .defaultRequest(post(&quot;/**&quot;).with(csrf()))
    .defaultRequest(put(&quot;/**&quot;).with(csrf()))
    .defaultRequest(delete(&quot;/**&quot;).with(csrf()))
    .build();
}&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  참고 &lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@shwncho/Spring-security-6-Controller-test-403-Forbidden-%EC%97%90%EB%9F%ACfeat.-csrf&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@shwncho/Spring-security-6-Controller-test-403-Forbidden-%EC%97%90%EB%9F%ACfeat.-csrf&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743167252397&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;[Spring security 6] Controller test 403 Forbidden 에러(feat. csrf)&quot; data-og-description=&quot;Spring Boot 3.x 버전을 사용하면서 Spring security도 6.x를 사용하게 됐다. 그런데 Spring Boot 2.x 버전과 Spring security 5.x 버전을 사용하면서 나타나지 않았던 문제가 생겼다.보통 csrf랑 연관되서 403 에러가&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@shwncho/Spring-security-6-Controller-test-403-Forbidden-%EC%97%90%EB%9F%ACfeat.-csrf&quot; data-og-url=&quot;https://velog.io/@shwncho/Spring-security-6-Controller-test-403-Forbidden-에러feat.-csrf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dgzKCv/hyYxJrk4p5/4toIzOEXObGNVSHDaeP290/img.png?width=480&amp;amp;height=464&amp;amp;face=0_0_480_464,https://scrap.kakaocdn.net/dn/kCDwo/hyYvmjJ3MW/9AnQRT7TCmohOrHkfEL9Y0/img.png?width=480&amp;amp;height=464&amp;amp;face=0_0_480_464,https://scrap.kakaocdn.net/dn/czNFD9/hyYvuaXGYM/Zl4WweaL3kIfSK3JhUQEV1/img.png?width=2072&amp;amp;height=914&amp;amp;face=0_0_2072_914&quot;&gt;&lt;a href=&quot;https://velog.io/@shwncho/Spring-security-6-Controller-test-403-Forbidden-%EC%97%90%EB%9F%ACfeat.-csrf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@shwncho/Spring-security-6-Controller-test-403-Forbidden-%EC%97%90%EB%9F%ACfeat.-csrf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dgzKCv/hyYxJrk4p5/4toIzOEXObGNVSHDaeP290/img.png?width=480&amp;amp;height=464&amp;amp;face=0_0_480_464,https://scrap.kakaocdn.net/dn/kCDwo/hyYvmjJ3MW/9AnQRT7TCmohOrHkfEL9Y0/img.png?width=480&amp;amp;height=464&amp;amp;face=0_0_480_464,https://scrap.kakaocdn.net/dn/czNFD9/hyYvuaXGYM/Zl4WweaL3kIfSK3JhUQEV1/img.png?width=2072&amp;amp;height=914&amp;amp;face=0_0_2072_914');&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;[Spring security 6] Controller test 403 Forbidden 에러(feat. csrf)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot 3.x 버전을 사용하면서 Spring security도 6.x를 사용하게 됐다. 그런데 Spring Boot 2.x 버전과 Spring security 5.x 버전을 사용하면서 나타나지 않았던 문제가 생겼다.보통 csrf랑 연관되서 403 에러가&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://suhyeon-developer.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://suhyeon-developer.tistory.com/38&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743167237651&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] Controller Unit Test에서 발생한 401, 403 에러를 해결해보자! (+ Spring Security)&quot; data-og-description=&quot;❗️컨트롤러 테스트코드를 작성하는 와중에 401과 403 에러를 마주쳤다. 해결하는건 크게 어렵지 않았다!!! 컨트롤러 단위 테스트 진행 과정 먼저, 사용한 기술은 이러하다. ▶︎ Spring Data JPA , Spr&quot; data-og-host=&quot;suhyeon-developer.tistory.com&quot; data-og-source-url=&quot;https://suhyeon-developer.tistory.com/38&quot; data-og-url=&quot;https://suhyeon-developer.tistory.com/38&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pMPoq/hyYvhW274d/NbkUqvpSwKKuTkQKYHxVeK/img.jpg?width=800&amp;amp;height=464&amp;amp;face=0_0_800_464,https://scrap.kakaocdn.net/dn/hwtFh/hyYvvnpYC8/R8TOQW6YGvqYCvpqypnM10/img.jpg?width=800&amp;amp;height=464&amp;amp;face=0_0_800_464,https://scrap.kakaocdn.net/dn/ecSuez/hyYxKKw2k3/xbN9JFb0Y7UL79iycyl5cK/img.png?width=2644&amp;amp;height=336&amp;amp;face=0_0_2644_336&quot;&gt;&lt;a href=&quot;https://suhyeon-developer.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://suhyeon-developer.tistory.com/38&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pMPoq/hyYvhW274d/NbkUqvpSwKKuTkQKYHxVeK/img.jpg?width=800&amp;amp;height=464&amp;amp;face=0_0_800_464,https://scrap.kakaocdn.net/dn/hwtFh/hyYvvnpYC8/R8TOQW6YGvqYCvpqypnM10/img.jpg?width=800&amp;amp;height=464&amp;amp;face=0_0_800_464,https://scrap.kakaocdn.net/dn/ecSuez/hyYxKKw2k3/xbN9JFb0Y7UL79iycyl5cK/img.png?width=2644&amp;amp;height=336&amp;amp;face=0_0_2644_336');&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] Controller Unit Test에서 발생한 401, 403 에러를 해결해보자! (+ Spring Security)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;❗️컨트롤러 테스트코드를 작성하는 와중에 401과 403 에러를 마주쳤다. 해결하는건 크게 어렵지 않았다!!! 컨트롤러 단위 테스트 진행 과정 먼저, 사용한 기술은 이러하다. ▶︎ Spring Data JPA , Spr&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;suhyeon-developer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743189158161&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;[Spring]Security적용 후 Controller 테스트코드 작성 시 발생했던 오류들 (Feat. Junit5, csrf)&quot; data-og-description=&quot;Controller에서 모든 Board를 조회하는 메소드를 테스트 코드를 작성하던 중 일어난 일이다. 우선 코드를 먼저보자 사실 이 로직상은 아무 문제가 없다.하지만 다음과 같이 401 Unauthorized가 발생하는 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&quot; data-og-url=&quot;https://velog.io/@tjdtn0219/SpringSecurity적용-후-Controller-테스트코드-작성-시-발생했던-오류들-Feat.-Junit5-csrf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kiarF/hyYyQQ2FGD/pZmqf1ovPnrhUHoKGAzjp0/img.png?width=637&amp;amp;height=120&amp;amp;face=0_0_637_120,https://scrap.kakaocdn.net/dn/tTYGh/hyYyN7QYBN/WYmTIiTNz8KB51YjKv7C51/img.png?width=637&amp;amp;height=120&amp;amp;face=0_0_637_120,https://scrap.kakaocdn.net/dn/bm16IU/hyYxLo9GKG/Y2e1eb7k2HEDPQpdkVCmKk/img.png?width=2072&amp;amp;height=914&amp;amp;face=0_0_2072_914&quot;&gt;&lt;a href=&quot;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@tjdtn0219/SpringSecurity%EC%A0%81%EC%9A%A9-%ED%9B%84-Controller-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8D%98-%EC%98%A4%EB%A5%98%EB%93%A4-Feat.-Junit5-csrf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kiarF/hyYyQQ2FGD/pZmqf1ovPnrhUHoKGAzjp0/img.png?width=637&amp;amp;height=120&amp;amp;face=0_0_637_120,https://scrap.kakaocdn.net/dn/tTYGh/hyYyN7QYBN/WYmTIiTNz8KB51YjKv7C51/img.png?width=637&amp;amp;height=120&amp;amp;face=0_0_637_120,https://scrap.kakaocdn.net/dn/bm16IU/hyYxLo9GKG/Y2e1eb7k2HEDPQpdkVCmKk/img.png?width=2072&amp;amp;height=914&amp;amp;face=0_0_2072_914');&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;[Spring]Security적용 후 Controller 테스트코드 작성 시 발생했던 오류들 (Feat. Junit5, csrf)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Controller에서 모든 Board를 조회하는 메소드를 테스트 코드를 작성하던 중 일어난 일이다. 우선 코드를 먼저보자 사실 이 로직상은 아무 문제가 없다.하지만 다음과 같이 401 Unauthorized가 발생하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://0soo.tistory.com/188&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://0soo.tistory.com/188&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743189375042&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;@WebMvcTest Security 401  403 응답 해결방법 - csrf&quot; data-og-description=&quot;@WebMvcTest Security 401 403 응답 해결 @WebMvcTest는 MVC와 관련된 애노테이션(Controller, ControllerAdvice, Filter, WebMvcConfigurer 등..)이 적용된 Bean들만 불러오고, @Component, @Service, @Repository와 같은 Bean들은 불러오&quot; data-og-host=&quot;0soo.tistory.com&quot; data-og-source-url=&quot;https://0soo.tistory.com/188&quot; data-og-url=&quot;https://0soo.tistory.com/188&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bIfWaG/hyYvtQJJtS/u8rvgL6DMh4RkVIHhHWRt0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/Dqtte/hyYxDSfAYx/wFFjua1ghKpFzWAb5ajTw1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://0soo.tistory.com/188&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://0soo.tistory.com/188&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bIfWaG/hyYvtQJJtS/u8rvgL6DMh4RkVIHhHWRt0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/Dqtte/hyYxDSfAYx/wFFjua1ghKpFzWAb5ajTw1/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;@WebMvcTest Security 401 403 응답 해결방법 - csrf&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;@WebMvcTest Security 401 403 응답 해결 @WebMvcTest는 MVC와 관련된 애노테이션(Controller, ControllerAdvice, Filter, WebMvcConfigurer 등..)이 적용된 Bean들만 불러오고, @Component, @Service, @Repository와 같은 Bean들은 불러오&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;0soo.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kth990303.tistory.com/408&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kth990303.tistory.com/408&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743189883447&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;[Spring Security] MockMvc에서 csrfToken을 이용해 403 Forbidden을 해결해보자&quot; data-og-description=&quot;스프링 시큐리티를 이용하면 기본적으로 csrf() 옵션이 설정된다. 그렇기 때문에 GET Method를 제외한 POST, DELETE 등이 제대로 실행되지 않고 403 Forbidden 에러를 내뱉을 수 있다. 위 에러는 아래 테스트&quot; data-og-host=&quot;kth990303.tistory.com&quot; data-og-source-url=&quot;https://kth990303.tistory.com/408&quot; data-og-url=&quot;https://kth990303.tistory.com/408&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/byyjHd/hyYvuaY66E/8xMWGkKeKarBwfYvGU1K9k/img.png?width=800&amp;amp;height=232&amp;amp;face=0_0_800_232,https://scrap.kakaocdn.net/dn/chiVc1/hyYxFWOC7T/usZhqWXmpvrqIX4Itanq11/img.png?width=800&amp;amp;height=232&amp;amp;face=0_0_800_232,https://scrap.kakaocdn.net/dn/B9xSV/hyYyU0b76l/ykp1zHXLvcqWOMFwMyUJ6k/img.png?width=1642&amp;amp;height=814&amp;amp;face=0_0_1642_814&quot;&gt;&lt;a href=&quot;https://kth990303.tistory.com/408&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kth990303.tistory.com/408&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/byyjHd/hyYvuaY66E/8xMWGkKeKarBwfYvGU1K9k/img.png?width=800&amp;amp;height=232&amp;amp;face=0_0_800_232,https://scrap.kakaocdn.net/dn/chiVc1/hyYxFWOC7T/usZhqWXmpvrqIX4Itanq11/img.png?width=800&amp;amp;height=232&amp;amp;face=0_0_800_232,https://scrap.kakaocdn.net/dn/B9xSV/hyYyU0b76l/ykp1zHXLvcqWOMFwMyUJ6k/img.png?width=1642&amp;amp;height=814&amp;amp;face=0_0_1642_814');&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;[Spring Security] MockMvc에서 csrfToken을 이용해 403 Forbidden을 해결해보자&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티를 이용하면 기본적으로 csrf() 옵션이 설정된다. 그렇기 때문에 GET Method를 제외한 POST, DELETE 등이 제대로 실행되지 않고 403 Forbidden 에러를 내뱉을 수 있다. 위 에러는 아래 테스트&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kth990303.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sedangdang.tistory.com/303&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://sedangdang.tistory.com/303&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743189993322&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;@WebMvcTest 에서 Spring Security 적용, 401/403 에러 해결하기 - csrf&quot; data-og-description=&quot;요약 401 Unauthorized -&amp;gt; @WithMockUser, @WithMockUserDetails 사용 403 Forbidden -&amp;gt; with(csrf()) 추가 @WebMvcTest Annotation that can be used for a Spring MVC test that focuses only on Spring MVC components. Using this annotation will disable full aut&quot; data-og-host=&quot;sedangdang.tistory.com&quot; data-og-source-url=&quot;https://sedangdang.tistory.com/303&quot; data-og-url=&quot;https://sedangdang.tistory.com/303&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hqTQx/hyYxDq8Vrb/6oqUCbLJen20QUxuqNBDD0/img.png?width=360&amp;amp;height=360&amp;amp;face=0_0_360_360,https://scrap.kakaocdn.net/dn/cgBmNb/hyYxJZcUfL/eR93buiKVfJJVjMoRGM9l1/img.png?width=360&amp;amp;height=360&amp;amp;face=0_0_360_360&quot;&gt;&lt;a href=&quot;https://sedangdang.tistory.com/303&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sedangdang.tistory.com/303&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hqTQx/hyYxDq8Vrb/6oqUCbLJen20QUxuqNBDD0/img.png?width=360&amp;amp;height=360&amp;amp;face=0_0_360_360,https://scrap.kakaocdn.net/dn/cgBmNb/hyYxJZcUfL/eR93buiKVfJJVjMoRGM9l1/img.png?width=360&amp;amp;height=360&amp;amp;face=0_0_360_360');&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;@WebMvcTest 에서 Spring Security 적용, 401/403 에러 해결하기 - csrf&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;요약 401 Unauthorized -&amp;gt; @WithMockUser, @WithMockUserDetails 사용 403 Forbidden -&amp;gt; with(csrf()) 추가 @WebMvcTest Annotation that can be used for a Spring MVC test that focuses only on Spring MVC components. Using this annotation will disable full aut&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sedangdang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Java with Error</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1005</guid>
      <comments>https://hj0216.tistory.com/1005#entry1005comment</comments>
      <pubDate>Sat, 29 Mar 2025 04:26:48 +0900</pubDate>
    </item>
    <item>
      <title>Error creating bean with name 'jpaAuditingHandler':</title>
      <link>https://hj0216.tistory.com/1004</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt; 오류&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1743158612967&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Caused by: 
org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'jpaAuditingHandler': 
Cannot resolve reference to bean 'jpaMappingContext' while setting constructor argument; 
nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'jpaMappingContext': 
Invocation of init method failed; 
nested exception is java.lang.IllegalArgumentException: 
JPA metamodel must not be empty!&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;style2&quot;&gt;&lt;b&gt;✍️원인&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;jpaAuditing 기능을 사용하기 위해 &lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;@EnableJPaAuditing을 Application에 선언&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 테스트에서 컨테이너를 사용하는 경우, 기본 애플리케이션 클래스(@SpringBootApplication이 붙은 클래스)를 항상 로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application 클래스에 @EnableJpaAuditing이 설정되어 있어서, 모든 테스트에서 JPA 관련 빈(EntityManager, JpaRepository 등)이 필요하게 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통합 테스트(@SpringBootTest)는 JPA 관련 빈을 전부 로드하므로 문제 없지만, @WebMvcTest는 컨트롤러 관련 빈만 로드하므로&amp;nbsp;JPA 빈이 없어서 오류 발생함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;style2&quot;&gt;&lt;b&gt;✅해결&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. @MokitoBean 추가&lt;/p&gt;
&lt;pre id=&quot;code_1743160443846&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
@DisplayName(&quot;회원가입 성공 테스트&quot;)
@MockitoBean(JpaMetamodelMappingContext.class)
void createUserSuccessTest() throws Exception {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 Test에 @MokitoBean을 선언해야하는 번거러움 존재&lt;/p&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. JpaAuditingConfig 파일 분리&lt;/p&gt;
&lt;pre id=&quot;code_1743160534884&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableJpaAuditing
@Configuration
public class JpaAuditingConfig {

}&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt; 참고&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@suujeen/Error-creating-bean-with-name-jpaAuditingHandler&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@suujeen/Error-creating-bean-with-name-jpaAuditingHandler&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743160561549&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;Error creating bean with name 'jpaAuditingHandler':&quot; data-og-description=&quot;Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor a...&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@suujeen/Error-creating-bean-with-name-jpaAuditingHandler&quot; data-og-url=&quot;https://velog.io/@suujeen/Error-creating-bean-with-name-jpaAuditingHandler&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dxXo6x/hyYvvnpbt5/NscRtOS3hq0yCsp4CuHAs1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bIIEc8/hyYxJdJGZy/oH0700o7RZ2YR7qijIeUC0/img.png?width=662&amp;amp;height=840&amp;amp;face=0_0_662_840&quot;&gt;&lt;a href=&quot;https://velog.io/@suujeen/Error-creating-bean-with-name-jpaAuditingHandler&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@suujeen/Error-creating-bean-with-name-jpaAuditingHandler&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dxXo6x/hyYvvnpbt5/NscRtOS3hq0yCsp4CuHAs1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bIIEc8/hyYxJdJGZy/oH0700o7RZ2YR7qijIeUC0/img.png?width=662&amp;amp;height=840&amp;amp;face=0_0_662_840');&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;Error creating bean with name 'jpaAuditingHandler':&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor a...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/JPA with Error</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1004</guid>
      <comments>https://hj0216.tistory.com/1004#entry1004comment</comments>
      <pubDate>Fri, 28 Mar 2025 20:17:10 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] IAM</title>
      <link>https://hj0216.tistory.com/1003</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IAM(Identity and Access Management)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AWS 리소스에 대한 권한을 관리하는 서비스&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Root 계정 노출 없이 사용자에게 제한적인 권한을 부여하고 싶을 때 사용&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본 개념&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2IHwg/btsMSSBgvT0/IOkMDvljK4ts9NQZWBKZ2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2IHwg/btsMSSBgvT0/IOkMDvljK4ts9NQZWBKZ2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2IHwg/btsMSSBgvT0/IOkMDvljK4ts9NQZWBKZ2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2IHwg%2FbtsMSSBgvT0%2FIOkMDvljK4ts9NQZWBKZ2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;248&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* User: AWS 서비스 사용자(사람 또는 외부 애플리케이션)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* Group: User의 집합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* Role: AWS의 작업과 리소스에 대한 애겟스를 부여하는 권한 세트&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* Policy: 특정 AWS 요소의 기능을 사용하기 위한 정책&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* Permission: Policy의 집합&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용 방식&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Programmatic Access: AWS SDK 등을 이용하여 AWS 서비스에 API로 접근하는 방식&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; Access Key ID, Secret access key로 구성된 키가 발급되며 일반적으로 많이 사용&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * AWS Management Console Access는 브라우저를 통해 전용 Console login Link로 접속하는 방식&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;AWS Management Console에 제한된 로그인이 필요할 때 사용&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;IAM 사용자 추가&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;1. IAM - 액세스 관리 - 사용자 - 사용자 추가 클릭&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;2. 사용자 이름 입력 및 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;AWS Management Console에 대한 사용자 권한 액세스 제공 선택&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;3.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #0f141a; text-align: start;&quot;&gt;IAM 사용자를 생성하고 싶음 선택&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #0f141a; text-align: start;&quot;&gt;4. 권한 옵션 선택&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #0f141a; text-align: start;&quot;&gt;5. 검토 및 생성에서 권한 확인 후, 사용자 생성 클릭&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #0f141a; text-align: start;&quot;&gt;6. .cvs 파일 다운로드(사용자 이름 및 암호 확인)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;IAM 사용자로 로그인&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;1. &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;.cvs 파일에서 콘솔 로그인 URL로 접속 후 로그인&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;2. 오른쪽 상단 사용자 이름 확인&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0f141a;&quot;&gt;IAM 그룹 생성 및 사용자 추가&lt;/span&gt;&lt;span style=&quot;color: #0f141a;&quot;&gt;1. &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;IAM - 액세스 관리 - 사용자 그룹 - 그룹 생성 클릭&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 그룹 이름, 사용자 추가&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 권한 정책 연결&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 그룹 생성 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;⭐ 만일 사용자 권한을 EC2ReadOnly만 부여하더라도 사용자가 속한 그룹에 EC2 Full 권한이 있을 경우, EC2 생성 가능&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IAM 사용자 및 그룹 삭제&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;IAM - 액세스 관리 - 사용자&lt;span&gt; - 삭제&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;2. &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;IAM - 액세스 관리 - 사용자&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 그룹 - 삭제&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1003</guid>
      <comments>https://hj0216.tistory.com/1003#entry1003comment</comments>
      <pubDate>Mon, 24 Mar 2025 19:42:24 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] Route53</title>
      <link>https://hj0216.tistory.com/1002</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;DNS(Domain Name Service)&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;도메인 이름을 IP주소로 바꿔주는 시스템&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 레코드: 도메인에 연결된 IP 주소&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Route53&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구성&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 호스팅 영역(Hosted Zone): 레코드의 컨테이너&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 퍼블릭 호스팅 영역: 인터넷에서 트래픽을 라우팅하고자 하는 방법을 지정하는 레코드를 포함&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 프라이빗 호스팅 영역: VPC에서 트래픽을 라우팅하고자 하는 방법을 지정하는 레코드를 포함&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;관련 용어&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * DNS 쿼리: 도메인 이름을 IP주소로 변환하는 요청&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * DNS 장애 조치: Route53에 연결된 서버 또는 ELB에 장애가 발생할 경우 다른 대체 위치로 라우팅하는 기능&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;레코드 유형&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * A(Address): IPv4 주소&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * AAAA(Address): IPv6 주소&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * CNAME(Canonical Name): 별칭 레코드, 현재 레코드의 이름에 대한 DNS 쿼리를 다른 도메인 또는 하위 도메인으로 매핑&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * MX(Mail eXchange): 이메일을 전달하기 위한 레코드&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * NS(Name Server): 네임 서버 주소를 담고 있는 레코드&lt;b&gt;(자동 생성, 함부로 변경하면 안됨)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * SOA(Start of Authority): 권한 시작 레코드, 도메인에 대한 기본 DNS 정보 식별&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;(자동 생성, 함부로 변경하면 안됨)&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라우팅(&lt;b&gt;네트워크에서 경로를 선택하는 프로세스&lt;/b&gt;) 방식&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Latency based Routing(지연 시간 기반 라우팅): 지연 시간이 가장 낮은 리전의 IP 주소로 라우팅하는 방식&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Weighted &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;based Routing(가중치 기반 라우팅): IP 주소나 ELB DNS 주소에 각각 가중치를 부여하고, 가중치에 따라 라우팅하는 방식&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;nbsp; * Geo based Routing(지역 기반 라우팅): 지역에 따라서 각기 다른 IP 주소로 라우팅&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;호스팅 영역 생성&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. Route53 - 시작하기 - 호스팅 영역 생성 - 시작하기&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 도메인 이름 입력 후 호스팅 영역 생성 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 레코드의 NS, SOA 자동 생성 확인&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 레코드 생성 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;5. 레코드 이름, 레코드 유형, 값 입력 후 레코드 생성 클릭&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;호스팅 영역 삭제&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 호스팅 영역 삭제 전 NS, SOA 제외한 모든 레코드 삭제&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 영역 삭제&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1002</guid>
      <comments>https://hj0216.tistory.com/1002#entry1002comment</comments>
      <pubDate>Sun, 23 Mar 2025 19:34:59 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] 과금의 이유를 알아보자...</title>
      <link>https://hj0216.tistory.com/1001</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYpiJa/btsMSUZYeKi/CKTE3rVToRa1XP4Yz0hMVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYpiJa/btsMSUZYeKi/CKTE3rVToRa1XP4Yz0hMVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYpiJa/btsMSUZYeKi/CKTE3rVToRa1XP4Yz0hMVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYpiJa%2FbtsMSUZYeKi%2FCKTE3rVToRa1XP4Yz0hMVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/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 프리티어도.. 과금이 될 수 있다는 것을.. 체험해보고.. 왜 과금이 되었나는 정리해봅니다.. 어흑..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비용이 나온 건 슬프지만 왜 나왔나를 알아보기 위해 청구서를 살펴봅니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마음의 준비를 하고, 과금 정보 및 비용 관리에서 청구서를 클릭합니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Amazon Web Services Korea LLC 서비스별 요금을 눌러보며 이유를 찾아봅니다..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;1. &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Virtual Private Cloud&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * AWS 클라우드 내에서 사용자의 전용 네트워크 공간을 제공하는 서비스&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;$0.00 per In-use public IPv4 address per hour for EC2 Free Tier&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;$0.005 per In-use public IPv4 address per hour&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0f141a;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;이렇게 2개 적혀 있는 걸 보아선 Free Tier를 넘어서 쓴 것 같군요..&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0f141a;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;그렇다면 VPC 비용은 어디서 나왔을까요..&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0f141a;&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/about-aws/whats-new/2024/02/aws-free-tier-750-hours-free-public-ipv4-addresses/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://aws.amazon.com/ko/about-aws/whats-new/2024/02/aws-free-tier-750-hours-free-public-ipv4-addresses/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742601273823&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;company&quot; data-og-title=&quot;AWS 프리 티어, 이제 퍼블릭 IPv4에 대한 요금이 부과됨에 따라 750시간의 무료 퍼블릭 IPv4 주소 사&quot; data-og-description=&quot;오늘부터 AWS는 Amazon Elastic Compute Cloud용 AWS 프리 티어에 매달 750시간의 퍼블릭 IPv4 주소 사용량을 포함하도록 업데이트합니다(12개월 무료). Amazon EC2의 기존 또는 신규 AWS 프리 티어 고객인 경우 &quot; data-og-host=&quot;aws.amazon.com&quot; data-og-source-url=&quot;https://aws.amazon.com/ko/about-aws/whats-new/2024/02/aws-free-tier-750-hours-free-public-ipv4-addresses/&quot; data-og-url=&quot;https://aws.amazon.com/ko/about-aws/whats-new/2024/02/aws-free-tier-750-hours-free-public-ipv4-addresses/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b4tlgA/hyYvlqgbvu/O0jt7lXoKJaAOhKgI616b0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/myCgp/hyYvugqtKt/wsAfEW8UxrREUw77w0tyLK/img.png?width=179&amp;amp;height=109&amp;amp;face=0_0_179_109&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/about-aws/whats-new/2024/02/aws-free-tier-750-hours-free-public-ipv4-addresses/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aws.amazon.com/ko/about-aws/whats-new/2024/02/aws-free-tier-750-hours-free-public-ipv4-addresses/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b4tlgA/hyYvlqgbvu/O0jt7lXoKJaAOhKgI616b0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/myCgp/hyYvugqtKt/wsAfEW8UxrREUw77w0tyLK/img.png?width=179&amp;amp;height=109&amp;amp;face=0_0_179_109');&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;AWS 프리 티어, 이제 퍼블릭 IPv4에 대한 요금이 부과됨에 따라 750시간의 무료 퍼블릭 IPv4 주소 사&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;오늘부터 AWS는 Amazon Elastic Compute Cloud용 AWS 프리 티어에 매달 750시간의 퍼블릭 IPv4 주소 사용량을 포함하도록 업데이트합니다(12개월 무료). Amazon EC2의 기존 또는 신규 AWS 프리 티어 고객인 경우&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://utilgosu.co.kr/%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%EA%B8%B0%EA%B0%84%EC%97%90-aws-vpc-%EC%9A%94%EA%B8%88%EC%9D%B4-%EB%82%98%EC%98%A8-%EC%9D%B4%EC%9C%A0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://utilgosu.co.kr/%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%EA%B8%B0%EA%B0%84%EC%97%90-aws-vpc-%EC%9A%94%EA%B8%88%EC%9D%B4-%EB%82%98%EC%98%A8-%EC%9D%B4%EC%9C%A0/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742601551312&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;프리티어 기간에 aws vpc 요금이 나온 이유 - 아임크로&quot; data-og-description=&quot;AWS는 첫 사용 시 1년간 프리티어 혜택을 제공한다. 하지만 AWS가 워낙 많은 서비스를 제공하다 보니, 나 같은 초보자들은 자신도 모르게 프리티어 혜택을 초과하여 서비스를 이용할 때가 많다. 나&quot; data-og-host=&quot;utilgosu.co.kr&quot; data-og-source-url=&quot;https://utilgosu.co.kr/%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%EA%B8%B0%EA%B0%84%EC%97%90-aws-vpc-%EC%9A%94%EA%B8%88%EC%9D%B4-%EB%82%98%EC%98%A8-%EC%9D%B4%EC%9C%A0/&quot; data-og-url=&quot;https://utilgosu.co.kr/%ed%94%84%eb%a6%ac%ed%8b%b0%ec%96%b4-%ea%b8%b0%ea%b0%84%ec%97%90-aws-vpc-%ec%9a%94%ea%b8%88%ec%9d%b4-%eb%82%98%ec%98%a8-%ec%9d%b4%ec%9c%a0/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rFPuU/hyYr2exZJm/Xhj2kIC9Knk7kqtCGhgsPk/img.png?width=700&amp;amp;height=400&amp;amp;face=0_0_700_400,https://scrap.kakaocdn.net/dn/SnTAS/hyYr1GSsDO/aa8LakVX6OKDOLgKkDqWE0/img.png?width=700&amp;amp;height=400&amp;amp;face=0_0_700_400,https://scrap.kakaocdn.net/dn/bfsyhv/hyYvn9te6b/Wx25esEbfOf6YcEyQPiK2K/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400&quot;&gt;&lt;a href=&quot;https://utilgosu.co.kr/%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%EA%B8%B0%EA%B0%84%EC%97%90-aws-vpc-%EC%9A%94%EA%B8%88%EC%9D%B4-%EB%82%98%EC%98%A8-%EC%9D%B4%EC%9C%A0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://utilgosu.co.kr/%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%EA%B8%B0%EA%B0%84%EC%97%90-aws-vpc-%EC%9A%94%EA%B8%88%EC%9D%B4-%EB%82%98%EC%98%A8-%EC%9D%B4%EC%9C%A0/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rFPuU/hyYr2exZJm/Xhj2kIC9Knk7kqtCGhgsPk/img.png?width=700&amp;amp;height=400&amp;amp;face=0_0_700_400,https://scrap.kakaocdn.net/dn/SnTAS/hyYr1GSsDO/aa8LakVX6OKDOLgKkDqWE0/img.png?width=700&amp;amp;height=400&amp;amp;face=0_0_700_400,https://scrap.kakaocdn.net/dn/bfsyhv/hyYvn9te6b/Wx25esEbfOf6YcEyQPiK2K/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400');&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;프리티어 기간에 aws vpc 요금이 나온 이유 - 아임크로&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AWS는 첫 사용 시 1년간 프리티어 혜택을 제공한다. 하지만 AWS가 워낙 많은 서비스를 제공하다 보니, 나 같은 초보자들은 자신도 모르게 프리티어 혜택을 초과하여 서비스를 이용할 때가 많다. 나&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;utilgosu.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;AWS에서 퍼블릭(Public) IPv4 주소에 대한 새로운 요금이 도입됩니다. 2024년 2월 1일부터 서비스 연결 여부에 관계없이 모든 퍼블릭 IPv4 주소에 대해 시간당 IP당 0.005 USD의 요금이 부과됩니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;VPC는 여러 개의 서브넷으로 구성되어 있다.&amp;nbsp;&lt;br /&gt;이 서브넷 하나당 하나씩 IP주소를 갖는다. &lt;br /&gt;즉, 하나의 VPC라도 2개 이상의 IPv4 주소를 사용할 수 있다는 것이다.&lt;br /&gt;일반적으로 한 vpc에는 2개 이상의 서브넷 주소가 있기 때문에, 한 달 내내 vpc를 사용한다면 프리티어 제한 시간인 750시간을 초과할 수 밖에 없다.&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;&lt;b&gt;2. &lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;Data Transfer&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;Bandwidth&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;$0.000 per GB - regional data transfer under the monthly global free tier&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;$0.01 per GB - regional data transfer - in/out/between EC2 &lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;AZs or using elastic IPs or ELB&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;이유는 정확히 알 수 없으나..&amp;nbsp;&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;처음에 EC2를 생성했을 때, Region을 확인안하고 만들어서.. 가 본적도 없는 스톡홀름에 EC2를 열심히 만든 기억이 있습니다.. 이런식으로 스웨덴을 방문해보네요..&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ELB 보니까 생각났습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ELB의 목적 다른 AZ를 써야하지 않겠습니까..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그곳에서 발생한 것 같습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. &lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;Elastic Compute Cloud&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;EBS&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;$0.00 per GB-Month of snapshot data stored under monthly free tier&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6f6f9; color: #0f141a; text-align: start;&quot;&gt;$0.05 per GB-Month of snapshot data stored - Asia Pacific (Seoul)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스냅샷은 GB단위군요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인정합니다..&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;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;1381&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxOxFZ/btsMTY8h5q3/CFWtEq16BlUAcDwCaPU1Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxOxFZ/btsMTY8h5q3/CFWtEq16BlUAcDwCaPU1Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxOxFZ/btsMTY8h5q3/CFWtEq16BlUAcDwCaPU1Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxOxFZ%2FbtsMTY8h5q3%2FCFWtEq16BlUAcDwCaPU1Dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1381&quot; height=&quot;220&quot; data-origin-width=&quot;1381&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;750시간까지는 무료라던 VPC가 323시간만 인정되었기 때문에.. 우선 문의를 먼저 해보겠습니다..&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;&lt;b&gt;2025.03.24 업데이트&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;프리티어를 사용하고 계실 경우, EC2용 퍼블릭 IPv4 주소에 대해 월 750시간의 무료 혜택이 제공되지만, 안타깝게도, RDS와 같은 다른 서비스와 연결되어 사용된 퍼블릭 IPv4 주소에 대해선 프리티어 혜택이 제공되지 않습니다.&lt;br /&gt;&lt;br /&gt;이에 따라, 문의하신 약 450 시간의 VPC 사용량은 EC2가 아닌 RDS와 ELB에 연결되어 사용된 퍼블릭 IPv4 주소로 인해 발생하였으며, 해당 사용량에 대해 표준 요금이 부과된 점 참고해 주시기 바랍니다.&lt;br /&gt;&lt;br /&gt;프리티어 혜택이 적용되는 VPC 사용량은 청구서상에서 '$0.00 per In-use public IPv4 address per hour for EC2 Free Tier'와 같이 표기됩니다.&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;&lt;b&gt;1. 지원 센터 입장 &amp;zwj;♀️&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://support.console.aws.amazon.com/support/home#/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://support.console.aws.amazon.com/support/home#/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742603068102&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;https://support.console.aws.amazon.com/support/home#/&quot; data-og-description=&quot;&quot; data-og-host=&quot;support.console.aws.amazon.com&quot; data-og-source-url=&quot;https://support.console.aws.amazon.com/support/home#/&quot; data-og-url=&quot;https://support.console.aws.amazon.com/support/home#/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://support.console.aws.amazon.com/support/home#/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://support.console.aws.amazon.com/support/home#/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;https://support.console.aws.amazon.com/support/home#/&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;support.console.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 오른쪽 하단의 사례 생성✍️&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 단 돈 0.01달러라도 과금이 되었을 때 알 수 있는 방법&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;1. 예산 생성&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;을 통해 메일을 받아본다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 비용은 다음날 집계되므로 나오기 전에는 알 수 없다..  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@topasvga/3557&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://brunch.co.kr/@topasvga/3557&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742602869766&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;AWS 비용 알람 쉽게 받는 2가지 방법-2023&quot; data-og-description=&quot;비용 이상항목 모니터링 설정과 예산 상태 모니터링 설정법입니다. &amp;lt;1&amp;gt; '비용 이상 항목' &amp;nbsp;모니터링 설정 &amp;lt;2&amp;gt; 예산 상태 클릭해 등록하자. &amp;lt;1&amp;gt; '비용 이상 항목' &amp;nbsp;모니터링 설정 1 오른쪽 위 내계정 &quot; data-og-host=&quot;brunch.co.kr&quot; data-og-source-url=&quot;https://brunch.co.kr/@topasvga/3557&quot; data-og-url=&quot;https://brunch.co.kr/@topasvga/3557&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Y3rlV/hyYuctZvuR/QJKIMAdzLUexlklx2s7zzk/img.png?width=1901&amp;amp;height=847&amp;amp;face=0_0_1901_847,https://scrap.kakaocdn.net/dn/vvXY6/hyYvjFZyH4/JZThHruoNafCukXcGycyZK/img.png?width=1901&amp;amp;height=847&amp;amp;face=0_0_1901_847,https://scrap.kakaocdn.net/dn/21LRC/hyYvvsGA7e/121BgkIPlX7RVYw3xexwE1/img.png?width=1419&amp;amp;height=805&amp;amp;face=0_0_1419_805&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@topasvga/3557&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://brunch.co.kr/@topasvga/3557&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Y3rlV/hyYuctZvuR/QJKIMAdzLUexlklx2s7zzk/img.png?width=1901&amp;amp;height=847&amp;amp;face=0_0_1901_847,https://scrap.kakaocdn.net/dn/vvXY6/hyYvjFZyH4/JZThHruoNafCukXcGycyZK/img.png?width=1901&amp;amp;height=847&amp;amp;face=0_0_1901_847,https://scrap.kakaocdn.net/dn/21LRC/hyYvvsGA7e/121BgkIPlX7RVYw3xexwE1/img.png?width=1419&amp;amp;height=805&amp;amp;face=0_0_1419_805');&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;AWS 비용 알람 쉽게 받는 2가지 방법-2023&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;비용 이상항목 모니터링 설정과 예산 상태 모니터링 설정법입니다. &amp;lt;1&amp;gt; '비용 이상 항목' &amp;nbsp;모니터링 설정 &amp;lt;2&amp;gt; 예산 상태 클릭해 등록하자. &amp;lt;1&amp;gt; '비용 이상 항목' &amp;nbsp;모니터링 설정 1 오른쪽 위 내계정&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;brunch.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1001</guid>
      <comments>https://hj0216.tistory.com/1001#entry1001comment</comments>
      <pubDate>Sat, 22 Mar 2025 09:32:31 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] RDS</title>
      <link>https://hj0216.tistory.com/1000</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&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;RDS(Relational Database Service)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLSoIg/btsMNLoIAYQ/EDySIvwgsjdXOO4RXzPpuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLSoIg/btsMNLoIAYQ/EDySIvwgsjdXOO4RXzPpuk/img.png&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;675&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;49.02&quot; style=&quot;width: 48.448%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLSoIg/btsMNLoIAYQ/EDySIvwgsjdXOO4RXzPpuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLSoIg%2FbtsMNLoIAYQ%2FEDySIvwgsjdXOO4RXzPpuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;675&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBXCOP/btsMOuz3L2Z/fZMkJaOfKdyfKJkJQuUXC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBXCOP/btsMOuz3L2Z/fZMkJaOfKdyfKJkJQuUXC1/img.png&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;668&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.3892%;&quot; data-widthpercent=&quot;50.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBXCOP/btsMOuz3L2Z/fZMkJaOfKdyfKJkJQuUXC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBXCOP%2FbtsMOuz3L2Z%2FfZMkJaOfKdyfKJkJQuUXC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;703&quot; height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;다중 AZ 배포&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;읽기 전용 복제본&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;동기식 복제(높은 안정성)&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;비동기식 복제(높은 확장성)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;쓰기 및 읽기 작업은 오직 기본 인스턴스에서만 이루어짐&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;여러 개의 Read Replica를 생성 가능하여 읽기 성능 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;자동 백업은 대기 상태에서 수행&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;기본으로 제공된 백업 구성 X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;단일 지역 내에서 항상 2개의 가용성 영역을 확장&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;가용 영역, 교차 AZ 또는 교차 지역 내에 있을 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;기본 DB 엔진 버전 업그레이가 발생&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;DB 엔진 버전 업그레이드는 원본 인스턴스와 독립됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;문제가 감지되면 대기 모드로 자동 Failover&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;독립형 DB 인스턴스로 수동 승격될 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;RDS 인스턴스 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Aurora and RDS&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;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;데이터베이스 생성 방식 선택: 표준 생성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;엔진 옵션 선택&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;템플릿 선택&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * 배포 옵션 선택&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; * 템플릿에서 프리티어 선택 시, 배포 옵션 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #0f141a; text-align: start;&quot;&gt;단일 AZ DB 인스턴스 배포(인스턴스 1개)만 사용 가능&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;DB 인스턴스 식별자 입력&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;마스터 사용자 이름 및 암호 입력&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; * 암호 자동 생성 시, 데이터베이스 생성 후 자격 증명 세부 정보 보기에서 암호 확인 필수&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;인스턴스 구성 선택&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 템플릿에서 프리티어 선택 시, 인스턴스 유형 db.t3g.micro/ db.t4g.micro만 사용 가능&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;DB 인스턴스 다중 AZ 배포로 전환 및 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 작업 - 다중 AZ 배포로 전환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 다중 AZ로 전환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 로그 및 이벤트 탭: 다중 AZ로 변환하는 이벤트 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 구성 탭: 다중 AZ가 예로 변경되었는지 확인(= 같은 리전의 다른 가용 영역에 Standby DB 생성)&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;6. 장애 조치로 재부팅 체크 후 확인&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;7. 로그 및 이벤트 탭: Standby DB Failover 이벤트 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 Region에 읽기 전용 복제본 생성&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;2. 읽기 전용 복제본 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * DB 인스턴스 식별자 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 인스턴스 구성 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;대상 리전 선택: 현재 리전과 다른 리전 선택&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * 배포 옵션 선택: 단일 DB 인스턴스 이외 선택 시, 과금 발생 유의&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;3. 연결 및 보안 - 복제(역할, 리즌 및 AZ) 확인&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;RDB 보안 그룹 규칙 변경&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;1. 연결 및 보안 - &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;VPC 보안 그룹 링크 클릭&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;2. 인바운드 규칙 편집&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;3. 소스 변경&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 규칙 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/1000</guid>
      <comments>https://hj0216.tistory.com/1000#entry1000comment</comments>
      <pubDate>Thu, 20 Mar 2025 19:26:02 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] Auto Scaling</title>
      <link>https://hj0216.tistory.com/999</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Auto Scaling&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트래픽에 따라 자동으로 서버의 개수를 조절해주는 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(EC2 관점에서는 트래픽에 따라 자동으로 EC2 인스턴스 개수를 조절해주는 기능)&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;AWS Auto Scaling&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; * 클라이언트 요청이 ELB로 들어오면, 로드 밸런서에서 ASG(Auto Scaling Group)으로 부하를 분산&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * ASG: Auto Scaling되는 EC2 인스턴스들의 집합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * ASG(Auto Scaling Group)는 AMI(Amazon Machine Image)를 필요로 하며, Cloud Watch라는 클라우드 모니터링 서비스를 사용해서 서버 용량을 늘릴지 줄일지 결정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 시작 구성(Launch Configuration): Auto Scaling을 할 때 사용할 EC2 인스턴스의 사전 설정 정보&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;blockquote data-ke-style=&quot;style2&quot;&gt;AMI 생성하기&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 작업 - 이미지 및 템플릿 - 이미지 생성&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;&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;style2&quot;&gt;ASG 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Auto Scaling - Auto Scaling 그룹&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Auto Scaling 그룹 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. (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; &amp;nbsp; * 만들어둔 AMI 선택, 이 외 EC2 생성 과정과 동일 &amp;rarr; 시작 템플릿 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 시작 템플릿 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. (2단계) 인스턴스 시작 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 가용 영역 및 서브넷 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; * ELB 만들 때 생성했떤 대상 그룹의 가용 영역과 동일하게 설정 필수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; Auto Scaling으로 생성된 EC2 인스턴스가 대상 그룹에 속하게 되어 트래픽을 전달받아야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. (3단계) 다른 서비스와 통합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 로드 밸런서와 Auto Scaling Group을 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 기존 로드 밸런서에 연결 &amp;rarr; 대상 그룹 선택: Auto Scaling으로 생성된 EC2 인스턴스가 대상 그룹에 속하게 되어 트래픽을 전달받을 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 상태 확인 - &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Elastic Load Balancer 상태 확인 켜기(ELB 상태 체크)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. (4단계) &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;그룹 크기 및 크기 조정 구성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 크기 조정: EC2 인스턴스의 개수 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;대상 추적 정책 사용 여부 선택: Auto Scaling 그룹의 크기를 조정하기 위한 조건&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. (5단계) 알림 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. (6단계) 태그 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. (7단계) 검토&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Auto Scaling 그룹 생성 클릭&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;Auto Scaling 작동 테스트&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * ASG를 생성하면 ASG의 최소 크기를 1로 설정했기 때문 EC2 인스턴스가 하나 자동으로 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 부하 프로그램 사용하여 &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Auto Scaling 그룹의 크기를 조정하기 위한 조건이 발동하도록 설정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;2. Auto Scaling으로 생성된 EC2의 모니터링을 통해 부하 확인&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;3. &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Auto Scaling 발동 조건 확인 시, EC2 생성 확인&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;4. &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Auto Scaling&lt;span&gt; - &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Auto Scaling&lt;span&gt; 그룹 - 활동, 인스턴스 관리 확인&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span&gt;5. 로드 밸런싱 - 대상 그룹 - 대상 확인&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp; * ASG와 대상 그룹의 인스턴스 개수가 다른 이유&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; * 대상 그룹: 로드 밸런서가 부하를 분산하기 위한 그룹(&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;수동으로 생성한 인스턴스가 포함됨&lt;/span&gt;)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; * ASG: Auto Scaling된 인스턴스만 관리하기 위한 그룹&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TjDSA/btsMLWXZFtO/bGMlaWS1wcqPm4mYLo59W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TjDSA/btsMLWXZFtO/bGMlaWS1wcqPm4mYLo59W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TjDSA/btsMLWXZFtO/bGMlaWS1wcqPm4mYLo59W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTjDSA%2FbtsMLWXZFtO%2FbGMlaWS1wcqPm4mYLo59W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;197&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 EC2 인스턴스가 각각 DB에 연결되어있어 다른 EC2에 연결될 경우, 특정 EC2에 저장된 데이터가 보이지 않는 문제&lt;/p&gt;
&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;DB가 EC2 인스턴스에 들어가는 것이 아니라 별도의 DB 인스턴스를 구성해서 모든 EC2 인스턴스들이 해당 DB를 바라보도록 해야 함&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/999</guid>
      <comments>https://hj0216.tistory.com/999#entry999comment</comments>
      <pubDate>Tue, 18 Mar 2025 18:34:54 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] ELB</title>
      <link>https://hj0216.tistory.com/998</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; 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;Load Balancing(부하 분산)&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; * Load Balancer: 각 서버로 부하를 분산시켜주는 역할을 하는 것&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; * 안정성 향상: 여러 개의 EC2 인스턴스에 장애가 생겨도 남은 EC2 인스턴스들이 클라이언트의 요청을 처리할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 서버 장애 예방: 클라이언트의 요청이 대량으로 들어오거나, 여러 개의 EC2 인스턴스가 중간에 장애가 생겨도 미리 계획해둔 백업 계획에 따라 EC2 인스턴스를 더 추가하여 서버 장애를 예방할 수 있음&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; * Health Check: 서버가 살아있는지 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Connection Draining: 등록 취소 지연, 사용자의 요청을 처리 중인 서버를 곧바로 삭제하지 못하도록 방지하는 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Latency: Load Balancer와 서버 사이의 지연 시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ELB&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2141&quot; data-origin-height=&quot;1193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/28MCl/btsMLyQthPD/pdlNB8ZYsJJ27J2n3ciFTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/28MCl/btsMLyQthPD/pdlNB8ZYsJJ27J2n3ciFTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/28MCl/btsMLyQthPD/pdlNB8ZYsJJ27J2n3ciFTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F28MCl%2FbtsMLyQthPD%2FpdlNB8ZYsJJ27J2n3ciFTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2141&quot; height=&quot;1193&quot; data-origin-width=&quot;2141&quot; data-origin-height=&quot;1193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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; * ELB는 리전별로 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * ELB는 여러 개의 가용 영역에 존재하는 EC2 인스턴스들에 부하를 분산&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 모든 EC2 인스턴스에 부하를 분산시키는 것이 아니라, 대상 그룹에 속한 EC2 인스턴스들에게만 부하를 분산&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ELB Load Balancing 알고리즘: Round Robin Scheduling&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Round Robin Scheduling:&amp;nbsp; &lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;프로세스가 도착한 순서대로 프로세스를 디스패치하지만 정해진 시간 할당량(또는 시간 간격)에 의해 실행을 제한&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * ALB: HTTP 헤더를 기준으로 트래픽 분배(OSI 5-7계층)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * NLB: IP 주소와 포트 번호를 기준으로 트래픽 분배(OSI 4계층)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * CLB: HTTP 헤더 및 IP 주소와 포트 번호를 기준으로 트래픽을 분배&lt;b&gt;(이전 버전으로 ALB, NLB 사용 권장)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ELB Load Balancer 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 로드 밸런싱 - 로드밸런서&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. Application Load Balancer&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;&lt;b&gt;&amp;nbsp; &amp;nbsp; * 로드 밸런싱을 적용할 인스턴스가 있는 가용 영역을 무조건 포함해야 함&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 보안 그룹: 로드 밸런서로 들어오고 나가는 트래픽에 대한 가상 방화벽&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 리스너 및 라우팅: 부하를 어디로 분산시킬지 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 대상 그룹: ELB가 부하를 분산시킬 대상 인스턴스들의 집합&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; * 대상 유형, 이름 &amp;rarr; 다음 &amp;rarr; 인스턴스 선택 &amp;rarr; 아래에 보류 중인 것으로 포함(대상 그룹에 인스턴스를 대기 상태로 넣음) 클릭&amp;nbsp; &amp;rarr; 대상 그룹 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 새로고침 후, 대상 그룹 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 로드 밸런서 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 프로비저닝(IT 자원을 사용할 수 있는 상태로 준비하는 것) 중 &amp;rarr; 활성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 로드 밸런싱 - 대상 그룹 - 대상 탭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 상태 확인이 healthy인지 확인(= 부하를 분산받을 수 있는 상태)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. DNS 이름 주소로 접속 가능한지 확인&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;2429&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcdYbm/btsMLYgYqEg/d4oMmda0JcUBupGyGHD920/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcdYbm/btsMLYgYqEg/d4oMmda0JcUBupGyGHD920/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcdYbm/btsMLYgYqEg/d4oMmda0JcUBupGyGHD920/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcdYbm%2FbtsMLYgYqEg%2Fd4oMmda0JcUBupGyGHD920%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2429&quot; height=&quot;725&quot; data-origin-width=&quot;2429&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&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;다른 가용 영역에 EC2 인스턴스 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 네트워크 설정 &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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ELB 대상 그룹에 새로운 EC2 인스턴스 등록&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 로드 밸런싱 - 대상 그룹&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 대상 그룹 - 대상 탭 &amp;rarr; 대상 등록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 인스턴스 선택 &amp;rarr; 아래에 보류 중인 것으로 포함 &amp;rarr; 보류 중인 대상 등록&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2356&quot; data-origin-height=&quot;743&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOHEnn/btsMLJ5wELk/sUDylHkGwR7Ejk4k34Gj8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOHEnn/btsMLJ5wELk/sUDylHkGwR7Ejk4k34Gj8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOHEnn/btsMLJ5wELk/sUDylHkGwR7Ejk4k34Gj8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOHEnn%2FbtsMLJ5wELk%2FsUDylHkGwR7Ejk4k34Gj8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2356&quot; height=&quot;743&quot; data-origin-width=&quot;2356&quot; data-origin-height=&quot;743&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-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>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/998</guid>
      <comments>https://hj0216.tistory.com/998#entry998comment</comments>
      <pubDate>Mon, 17 Mar 2025 18:42:06 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] EBS</title>
      <link>https://hj0216.tistory.com/997</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;EBS(Elastic Block Store)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2에 연결해서 쓸 수 있는 Block Storage(* 외장 하드 같은 느낌)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; * 한 개의 EBS를 여러 개의 EC2 인스턴스에 연결하는 것은 불가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 한 개의 EC2 인스턴스에 여러개의 EBS를 연결하는 것은 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Volumn: EBS의 가장 기본적인 형태로 EC2에 바로 연결 가능한 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snapshot: 볼륨의 특정 시점을 그대로 복사하여 저장한 파일(* 백업 파일)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AMI(Amazon Machine Image): OS가 설치된 형태의 이미지 파일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * AMI를 이용하여 EC2 인스턴스를 생성할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IOPS(Input/Output Operaions Per Second): 저장 장치의 성능 측정 단위&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;blockquote data-ke-style=&quot;style2&quot;&gt;EBS 볼륨 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Elastic Block Store - 볼륨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2. 볼륨 생성&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;* 볼륨을 생성할 때 동일한 가용 영역에 생성해야만 EC2 인스턴스와 연결할 수 있음&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. 볼륨 상태: 생성 중 &amp;rarr; 사용 가능&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;6. 연결할 인스턴스 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 볼륨 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 첨부된 리소스에서 인스턴스 상태 attached 확인&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;EBS 스냅샷 생성 및 삭제&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Elastic Block Store - 볼륨&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. Elastic Block Store - 스냅샷&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;blockquote data-ke-style=&quot;style2&quot;&gt;EBS 볼륨 삭제&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; * 볼륨이 EC2 인스턴스에 연결되어 있지 않은 상태에서 삭제 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Elastic Block Store - 볼륨&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;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/997</guid>
      <comments>https://hj0216.tistory.com/997#entry997comment</comments>
      <pubDate>Sun, 16 Mar 2025 16:21:06 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] EC2</title>
      <link>https://hj0216.tistory.com/996</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&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;EC2&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elastic Compute Cloud, 컴퓨팅 자원을 클라우드로 제공하는 서비스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상 서버 서비스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Elastic IP&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 컴퓨팅을 위해 고안된 정적 IPv4 주소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; * Elastic&lt;/b&gt;: 다른 서비스와 유연하게 연동해서 사용할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elastic IP를 EC2 인스턴스에 연결하면 해당 IP 주소로 EC2 인스턴스에 접속할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * EC2 인스턴스에 장애 발생 시, 기존 Elastic IP를 새로운 EC2 인스턴스에 연결할 경우, 클라이언트에서 접속하는 IP 주소의 변경 없이 서버 장애를 극복할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;b&gt;Elastic IP는 인스턴스에 연결해둘 경우에만 무료&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;style2&quot;&gt;Security Group&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; * 방화벽(Firewall): 컴퓨터의 보안을 위해 외부에서 내부 또는 내부에서 외부의 정보통신망에 접근하는 것을 허용하거나 차단하기 위한 시스템&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 인바운드 트래픽: 서버 입장에서 안으로 들어오는 트래픽&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 아웃바운드 트래픽: 서버 입장에서 밖에서 나가는 트래픽&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 기본적으로 보안 그룹은 모든 아웃바운드 트래픽을 허용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 액세스를 거부하는 규칙을 생성할 수 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 특정 IP 주소에서 서버의 3000번 포트로 접속하는 것을 허용하는 것은 가능하나, 허용하지 않는 규칙은 불가능&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;blockquote data-ke-style=&quot;style2&quot;&gt;EC2 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인스턴스 시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름: HariboServer &amp;rarr; 추가 태그 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 애플리케이션 및 OS 이미지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 서버에 설치할 운영체제나 애플리케이션 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 인스턴스 유형&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 인스턴스 패밀리, 세대, 용량에 맞춰 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 키 페어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; * 키를 모르거나 잃어버릴 경우, 인스턴스에 접속할 수 없으므로 유의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 새 키 페어 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * private key 파일 형식: Windows 환경: .ppk, Mac: .pem&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;rarr; 다음에서 SSH 트래픽 허용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 위치 무관: 어느 IP 주소에서든지 SSH로 접속하는 것을 허용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 스토리지 구성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 사용할 저장 장치를 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 인스턴스 시작&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;SSH로 EC2 인스턴스 접속(Windows) with PuTTY&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * PuTTY: &lt;span style=&quot;background-color: #ffffff; color: #474747; text-align: left;&quot;&gt;윈도우 환경에서 리눅스 서버나 다른 원격 시스템에 SSH (Secure Shell), Telnet, Rlogin 등을 사용하여 접속할 수 있는 클라이언트&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Connection - SSH - Auth - Credentials&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private key file for authentification: .ppk 파일 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Session&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host Name(or IP address): EC2 인스턴스 퍼블릭 IPv4 주소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Open&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;Elastic IP 주소 할당 및 인스턴스 연결&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Elastic IP 주소 할당 &amp;rarr; 할당&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Elastic IP 주소&amp;nbsp; 선택 &amp;rarr; 작업 - Elastic IP 주소 연결 &amp;rarr; 인스턴스 선택 &amp;rarr; 연결&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;Elastic IP 주소 연결 해제 및 Elastic IP 주소&amp;nbsp;반납&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;1. Elastic IP 주소가 할당된 인스턴스 요약 &amp;rarr; 작업 - 네트워킹 &lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&amp;rarr; Elastic IP 주소 연결 해제&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Elastic IP &lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&amp;rarr; Elastic IP 주소&amp;nbsp; 선택 &lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&amp;rarr; 작업 - Elastic IP 주소 릴리즈&lt;/span&gt;&lt;/span&gt;&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; *&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Elastic IP는 인스턴스에 연결해둘 경우에만 무료&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;style2&quot;&gt;보안 그룹 편집&lt;/blockquote&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;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;&amp;nbsp; * 포트 범위: 서버에서 사용하는 포트 번호 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 소스: 서버로 들어오는 트래픽&lt;/p&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;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;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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;EC2 인스턴스 종료&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; * EC2 인스턴스는 삭제가 따로 없으며, 인스턴스 종료 시, 일정 시간 지난 후 자동으로 삭제&lt;/b&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;2. 인스턴스 상태: 실행 중 &amp;rarr; 종료 중 &amp;rarr; 종료됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⭐ 클라우드는 사용한 만큼 요금을 지불하므로 사용하지 않는 자원은 곧바로 종료/삭제 ⭐&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/996</guid>
      <comments>https://hj0216.tistory.com/996#entry996comment</comments>
      <pubDate>Sat, 15 Mar 2025 15:36:57 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] 멀티 팩터 인증(MFA)</title>
      <link>https://hj0216.tistory.com/995</link>
      <description>&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;a href=&quot;https://product.kyobobook.co.kr/detail/S000214700630&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;소플의&amp;nbsp;처음&amp;nbsp;만난&amp;nbsp;AWS&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;계정이 해킹되지 않도록 이중 보안 인증 추가&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;멀티&amp;nbsp;팩터&amp;nbsp;인증(MFA)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오른쪽 상단 계정 클릭 &amp;rarr; 보안 자격 증명 클릭 &amp;rarr; 멀티 팩터 인증(MFA) 구역에 ㅡMFA 디바이스 할당 버튼 클릭&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;1단계&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;MFA device name: 이중 보안 디바이스 이름&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;MFA device: 이중 보안 디바이스 종류&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;&amp;nbsp; * Google Authentificator 사용 시, 인증 관리자 앱 선택&lt;/span&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;2단계&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QR코드 표시 후, &lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Google Authentificator에서 스캔&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;6자리 코드 입력 &amp;rarr; 30초 후 다시 생성되는 6자리 코드 입력&lt;/span&gt;&lt;/p&gt;</description>
      <category>PlayGround/AWS 연습</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/995</guid>
      <comments>https://hj0216.tistory.com/995#entry995comment</comments>
      <pubDate>Fri, 14 Mar 2025 20:11:11 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] Builder Pattern</title>
      <link>https://hj0216.tistory.com/994</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;빌더 패턴&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: justify;&quot;&gt;은 복잡한 객체들을 단계별로 생성할 수 있도록 하는 생성 디자인 패턴입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어봅니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity 역할을 하는 Person의 DTO를 Builder로 변환해 봅시다!&lt;/p&gt;
&lt;pre id=&quot;code_1741423592410&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@AllArgsConstructor
public class Person {
  private String name;
  private LocalDate birthday;
  private Address address;
}&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;Person을 Famil, Friend, Coworker 등으로 확장하면서 구현해보기 위해 Interface를 만들어봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1741423824871&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface PersonDTO {
  String getName();
  LocalDate getBirthday();
  Address getAddress();
}&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;PersonDTO를 구현한 FriendDTO는 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nickname 필드를 추가했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1741423869499&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@AllArgsConstructor
@ToString
public class FriendDTO implements PersonDTO {
  private String name;
  private String nickname;
  private LocalDate birthday;
  private Address address;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 패턴을 사용하면, 매개변수가 많을 경우, 매개변수의 조합이 많을 경우 간편하게 불변 객체를 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 클래스도 DTO 클래스와 동일하게 interface를 만들고 구현 객체를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &amp;zwj;♂️ 아래 코드는 나중에 수정이 될 것입니다^^,,,&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741424217368&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface PersonDTOBuilder {
  PersonDTOBuilder name(String name);
  PersonDTOBuilder birthday(LocalDate birthday);
  PersonDTOBuilder address(Address address);

  PersonDTO build();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741424317866&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class FriendDTOBuilder implements PersonDTOBuilder {
  private String name;
  private String nickname;
  private LocalDate birthday;
  private Address address;

  @Override
  public FriendDTOBuilder name(String name) {
    this.name = name;
    return this;
  }

  public FriendDTOBuilder nickname(String nickname) {
    this.nickname = nickname;
    return this;
  }

  @Override
  public FriendDTOBuilder birthday(LocalDate birthday) {
    this.birthday = birthday;
    return this;
  }

  @Override
  public FriendDTOBuilder address(Address address) {
    this.address = address;
    return this;
  }

  @Override
  public PersonDTO build() {
    return new FriendDTO(name, nickname, birthday, address);
  }
}&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;이렇게 작성하면 Builder 패턴으로 객체를 만들 수 있으나..&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;pre id=&quot;code_1741425821954&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface PersonDTOBuilder&amp;lt;T extends PersonDTOBuilder&amp;lt;T&amp;gt;&amp;gt; {
  T name(String name);
  T birthday(LocalDate birthday);
  T address(Address address);

  PersonDTO build();
  PersonDTO getPersonDTO();
}&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;여기서 그치지 않고, Builder를 품은 DTO도 만들 수 있습니다,, &lt;/p&gt;
&lt;pre id=&quot;code_1741427779639&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@Setter
@ToString
@AllArgsConstructor
public class CafeDTO {
  private String name;
  private String address;
  private String signature;
  private String rating;

  public static Builder builder() {
    return new Builder();
  }

  public static class Builder {
    private String name;
    private String address;
    private String signature;
    private String rating;

    private CafeDTO cafeDTO;

    public Builder name(String name) {
      this.name = name;
      return this;
    }

    public Builder address(Address address) {
      this.address = &quot;(&quot; + address.getZipCode() + &quot;) &quot; + address.getCity() + &quot; &quot; + address.getState();
      return this;
    }

    public Builder signature(String signature) {
      this.signature = signature;
      return this;
    }

    public Builder rating(int rating) {
      this.rating = &quot;★&quot;.repeat(rating);
      return this;
    }

    public CafeDTO build() {
      this.cafeDTO = new CafeDTO(name, address, signature, rating);
      return this.cafeDTO;
    }

    public CafeDTO getCafeDTO() {
      return this.cafeDTO;
    }
  }
}&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;이제야 자주보던 @Builder를 선언했을 때와 같은 모양으로 쓸 수 있습니다,,&lt;/p&gt;
&lt;pre id=&quot;code_1741427827587&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CafeDTO cafe = CafeDTO.builder()
                      .name(&quot;starbucks&quot;)
                      .address(address)
                      .signature(&quot;americano&quot;)
                      .rating(5)
                      .build();&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;여기에 마지막으로 Entity를 DTO로 바꿔주는 static 메서드도 추가가 가능하죠,,&lt;/p&gt;
&lt;pre id=&quot;code_1741481216070&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static CafeDTO toDTO(Cafe cafe) {
    return CafeDTO.builder()
                  .name(cafe.getName())
                  .address(cafe.getAddress())
                  .signature(cafe.getSignature())
                  .rating(cafe.getRating())
                  .build();
}&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;Entity를 DTO로,,&lt;/p&gt;
&lt;pre id=&quot;code_1741481276026&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Cafe cafe = new Cafe(&quot;two-some&quot;, new Address(&quot;12345&quot;, &quot;seoul&quot;, &quot;bangbae&quot;), &quot;milktea&quot;, 5);
CafeDTO cafeDTOFromEntity = CafeDTO.toDTO(cafe);&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 style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1741481316653&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;  빌더(Builder) 패턴 - 완벽 마스터하기&quot; data-og-description=&quot;Builder Pattern 빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/badPO1/hyYqSg5UlT/vxNQXk9uappKWlORl1qaIK/img.jpg?width=800&amp;amp;height=426&amp;amp;face=0_0_800_426,https://scrap.kakaocdn.net/dn/cIpX8Q/hyYqbBa8RI/Aeuow6kewnG3Yw4C2Fg3LK/img.jpg?width=800&amp;amp;height=426&amp;amp;face=0_0_800_426,https://scrap.kakaocdn.net/dn/lkKr4/hyYq0zkX2I/WBn9VMnKD6OOzYZYyWKILK/img.png?width=1600&amp;amp;height=563&amp;amp;face=0_0_1600_563&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/badPO1/hyYqSg5UlT/vxNQXk9uappKWlORl1qaIK/img.jpg?width=800&amp;amp;height=426&amp;amp;face=0_0_800_426,https://scrap.kakaocdn.net/dn/cIpX8Q/hyYqbBa8RI/Aeuow6kewnG3Yw4C2Fg3LK/img.jpg?width=800&amp;amp;height=426&amp;amp;face=0_0_800_426,https://scrap.kakaocdn.net/dn/lkKr4/hyYq0zkX2I/WBn9VMnKD6OOzYZYyWKILK/img.png?width=1600&amp;amp;height=563&amp;amp;face=0_0_1600_563');&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;  빌더(Builder) 패턴 - 완벽 마스터하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Builder Pattern 빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://refactoring.guru/ko/design-patterns/builder&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://refactoring.guru/ko/design-patterns/builder&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1741481321143&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;빌더 패턴&quot; data-og-description=&quot;/ 디자인 패턴들 / 생성 패턴 빌더 패턴 다음 이름으로도 불립니다: Builder 의도 빌더는 복잡한 객체들을 단계별로 생성할 수 있도록 하는 생성 디자인 패턴입니다. 이 패턴을 사용하면 같은 제작 &quot; data-og-host=&quot;refactoring.guru&quot; data-og-source-url=&quot;https://refactoring.guru/ko/design-patterns/builder&quot; data-og-url=&quot;https://refactoring.guru/ko/design-patterns/builder&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/djmaFc/hyYq0sEUTK/wg0fzXiIoeV9bvzI7ggpH1/img.png?width=470&amp;amp;height=580&amp;amp;face=0_0_470_580,https://scrap.kakaocdn.net/dn/Ejocw/hyYmV7BZBj/eGNDiMxBeXa3dzKpMQYibK/img.png?width=640&amp;amp;height=400&amp;amp;face=0_0_640_400&quot;&gt;&lt;a href=&quot;https://refactoring.guru/ko/design-patterns/builder&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://refactoring.guru/ko/design-patterns/builder&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/djmaFc/hyYq0sEUTK/wg0fzXiIoeV9bvzI7ggpH1/img.png?width=470&amp;amp;height=580&amp;amp;face=0_0_470_580,https://scrap.kakaocdn.net/dn/Ejocw/hyYmV7BZBj/eGNDiMxBeXa3dzKpMQYibK/img.png?width=640&amp;amp;height=400&amp;amp;face=0_0_640_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;빌더 패턴&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;/ 디자인 패턴들 / 생성 패턴 빌더 패턴 다음 이름으로도 불립니다: Builder 의도 빌더는 복잡한 객체들을 단계별로 생성할 수 있도록 하는 생성 디자인 패턴입니다. 이 패턴을 사용하면 같은 제작&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;refactoring.guru&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.udemy.com/course/design-patterns-in-java-concepts-hands-on-projects/?couponCode=PLOYALTY0923&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.udemy.com/course/design-patterns-in-java-concepts-hands-on-projects/?couponCode=PLOYALTY0923&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer/Design Pattern</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/994</guid>
      <comments>https://hj0216.tistory.com/994#entry994comment</comments>
      <pubDate>Sun, 9 Mar 2025 09:54:05 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Webflux와 Checked/Unchecked Exception</title>
      <link>https://hj0216.tistory.com/993</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/zanmang-loopy-ver-4&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/debXTp/btsMsKDZoIx/o067qIogc0vYModkXT7QL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdebXTp%2FbtsMsKDZoIx%2Fo067qIogc0vYModkXT7QL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요새 날이 넘 춥네요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안그래도 추운 날씨에 밖에 나가기가 무서워진 요즘..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handle이 고장한 8톤 트럭,,도 아닌데, throws를 선언해도 처리되지 않았다며 우기는 Controller와 화해한 이야기 풉니다,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 문제의 코드&lt;/p&gt;
&lt;pre id=&quot;code_1740293954755&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/touch&quot;)
Mono&amp;lt;?&amp;gt; touch(@RequestParam(name = &quot;queue&quot;, defaultValue = &quot;default&quot;) String queue,
      @RequestParam(name = &quot;user_id&quot;) Long userId,
      ServerWebExchange exchange) throws NoSuchAlgorithmException {
    return Mono.defer(() -&amp;gt; userQueueService.generateToken(queue, userId))
        .map(token -&amp;gt; {
          exchange.getResponse().addCookie(
              ResponseCookie.from(&quot;user-queue-%s-token&quot;.formatted(queue), token)
                  .maxAge(Duration.ofSeconds(300))
                  .path(&quot;/&quot;)
                  .build()
          );

          return token;
        });
  }
  
public Mono&amp;lt;String&amp;gt; generateToken(final String queue, final Long userId) throws NoSuchAlgorithmException{
    MessageDigest digest = MessageDigest.getInstance(&quot;SHA-256&quot;);
    String input = &quot;user-queue-%s-%d&quot;.formatted(queue, userId);
    byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));

    StringBuilder hexString = new StringBuilder();
    for (byte b : encodedHash) {
      hexString.append(String.format(&quot;%02x&quot;, b));
    }
    
    return Mono.just(hexString.toString());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 문제의 상황&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;generateToken 메서드는 SHA-256 암호화 알고리즘을 이용해서 토큰값을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MessageDigest 클래스의 getInstance를 사용하려면 Checked Exception인 NoSuchAlgorithmException에 대한 처리를 해줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;generateToken()를 호출하는 &quot;/touch&quot; API 또한 NoSuchAlgorithmException이 발생한다고 throws를 적으면 NoSuchAlgorithmException이 Unhandled exception이라며 오류가 발생합니다^^!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 문제의 원인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring WebFlux에서 Mono 또는 Flux 내부에서 발생하는 Checked 예외는 반드시 &lt;b&gt;Mono.error()&lt;/b&gt; 또는 &lt;b&gt;onErrorResume()&lt;/b&gt; 등을 통해 처리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⭐ 즉, 비동기 코드(WebFlux)에서는 throws가 먹히지 않고 Mono.error(e)를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;blockquote data-ke-style=&quot;style2&quot;&gt;Mono.defer() 내부에서 예외를 처리&lt;/blockquote&gt;
&lt;pre id=&quot;code_1740294902723&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/touch&quot;)
Mono&amp;lt;?&amp;gt; touch(@RequestParam(name = &quot;queue&quot;, defaultValue = &quot;default&quot;) String queue,
              @RequestParam(name = &quot;user_id&quot;) Long userId,
              ServerWebExchange exchange) {
    return Mono.defer(() -&amp;gt; {
        try {
            return userQueueService.generateToken(queue, userId);
        } catch (NoSuchAlgorithmException e) {
            return Mono.error(e);
        }
    }).map(token -&amp;gt; {
        exchange.getResponse().addCookie(
            ResponseCookie.from(&quot;user-queue-%s-token&quot;.formatted(queue), token)
                .maxAge(Duration.ofSeconds(300))
                .path(&quot;/&quot;)
                .build()
        );
        
        return token;
    });
}&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;style2&quot;&gt;Checked 예외를 Unchecked 예외로 변경&lt;/blockquote&gt;
&lt;pre id=&quot;code_1740295043187&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Mono&amp;lt;String&amp;gt; generateToken(final String queue, final Long userId) {
    MessageDigest digest = null;
    try {
        digest = MessageDigest.getInstance(&quot;SHA-256&quot;);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }

    String input = &quot;user-queue-%s-%d&quot;.formatted(queue, userId);
    byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));

    StringBuilder hexString = new StringBuilder();
    for (byte b : encodedHash) {
        hexString.append(String.format(&quot;%02x&quot;, b));
    }
    return Mono.just(hexString.toString());
}&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;table style=&quot;border-collapse: collapse; width: 100%; height: 60px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt; 예외 유형 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Mono에서 처리 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;1938&quot; data-start=&quot;1846&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;Checked Exception (NoSuchAlgorithmException)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Mono.error(e)를 써야 Mono 체인에서 처리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;Unchecked Exception (RuntimeException)&lt;/b&gt; &lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;따로 감싸지 않아도 Mono 체인에서 자동으로 예외 감지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1701&quot; data-start=&quot;1689&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  결론&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1871&quot; data-start=&quot;1702&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예외를 클라이언트에게 전달할 필요가 있을 경우 &lt;/b&gt;&amp;rarr; Mono.error(e)로 감싸서 처리하는 것이 좋음&lt;br /&gt;&lt;b&gt;500 에러로 내보내면 충분할 경우 &lt;/b&gt;&amp;rarr; try-catch 후 RuntimeException 던지는 것이 더 간결하고 좋음&lt;/p&gt;
&lt;p data-end=&quot;1943&quot; data-start=&quot;1873&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;https://fastcampus.co.kr/dev_online_traffic_data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1740295229506&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&quot; data-og-description=&quot;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사 &quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cN8EDE/hyYjypQK7B/167iyHedFLEok1qFqleFGK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/cFgEQ6/hyYfNvsdVa/A42cUlXcJPwZYQW6yvOv50/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/xw88a/hyYjyXHWqY/phtgcDr413sYs4ZkiTZkVk/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cN8EDE/hyYjypQK7B/167iyHedFLEok1qFqleFGK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/cFgEQ6/hyYfNvsdVa/A42cUlXcJPwZYQW6yvOv50/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/xw88a/hyYjyXHWqY/phtgcDr413sYs4ZkiTZkVk/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520');&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;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/993</guid>
      <comments>https://hj0216.tistory.com/993#entry993comment</comments>
      <pubDate>Sun, 23 Feb 2025 16:20:45 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] 테스트를 위한 Embedded Redis</title>
      <link>https://hj0216.tistory.com/992</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;168&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eNCvRL/btsMjBNGBvh/kmH4wGR9Fu1cTpIizdc0z0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eNCvRL/btsMjBNGBvh/kmH4wGR9Fu1cTpIizdc0z0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eNCvRL/btsMjBNGBvh/kmH4wGR9Fu1cTpIizdc0z0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeNCvRL%2FbtsMjBNGBvh%2FkmH4wGR9Fu1cTpIizdc0z0%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;168&quot; height=&quot;300&quot; data-origin-width=&quot;168&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chill한 루피를 데려왔습니다.&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;평소보다 이미지가 길어서 깔끔하지 않은 모양새로 글을 시작하게 됬지만, Chill한 루피와 함께라면 이 정도는 Chill하게 넘어갈 수 있지 않을까,, 생각합니다,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 간단하게 테스트 코드 작성 시, Embedded Redis를 사용하는 방법을 정리해 보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Spring&amp;nbsp;Boot&amp;nbsp;+&amp;nbsp;Redis&amp;nbsp;환경에서&amp;nbsp;테스트&amp;nbsp;코드&amp;nbsp;작성&amp;nbsp;시&amp;nbsp;Embedded&amp;nbsp;Redis를&amp;nbsp;사용하는&amp;nbsp;이유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제&amp;nbsp;Redis&amp;nbsp;서버에&amp;nbsp;의존하지&amp;nbsp;않고&amp;nbsp;테스트를&amp;nbsp;실행할&amp;nbsp;수&amp;nbsp;있기&amp;nbsp;때문입니다.&amp;nbsp;즉,&amp;nbsp;Redis를&amp;nbsp;설치하지&amp;nbsp;않아도&amp;nbsp;테스트&amp;nbsp;가능합니다. &lt;br /&gt;Embedded&amp;nbsp;Redis는&amp;nbsp;테스트가&amp;nbsp;끝나면&amp;nbsp;자동으로&amp;nbsp;종료되므로,&amp;nbsp;테스트&amp;nbsp;데이터&amp;nbsp;정리를&amp;nbsp;신경&amp;nbsp;쓸&amp;nbsp;필요&amp;nbsp;없습니다. &lt;br /&gt;클라이언트-서버&amp;nbsp;통신이&amp;nbsp;필요없어서&amp;nbsp;네트워크&amp;nbsp;I/O&amp;nbsp;오버헤드가&amp;nbsp;발생하지&amp;nbsp;않으므로&amp;nbsp;실제&amp;nbsp;Redis&amp;nbsp;서버보다&amp;nbsp;좀&amp;nbsp;더&amp;nbsp;빠릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 네트워크 I/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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Dependancy 추가&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;build.gradle&lt;/blockquote&gt;
&lt;pre id=&quot;code_1739584970045&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
	testImplementation 'com.github.codemonstur:embedded-redis:1.4.3' // test 전용 embedded redis 추가
}&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;3. Configuration 파일 작성&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;EmbeddedRedis.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1739585098152&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@TestConfiguration
public class EmbeddedRedis {
  private final RedisServer redisServer;

  public EmbeddedRedis() throws IOException {
    this.redisServer = new RedisServer(63790);
  }

  // 1. Spring이 EmbeddedRedis 객체를 생성
  @PostConstruct
  public void start() throws IOException {
    // 2️. EmbeddedRedis 생성 후 &amp;rarr; start() 호출 (Redis 서버 시작)
    this.redisServer.start();
  }

  // 3. 테스트 실행

  @PreDestroy
  public void stop() throws IOException {
    // 4. EmbeddedRedis 소멸 전 &amp;rarr; stop() 호출 (Redis 서버 종료)  
    this.redisServer.stop();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  Local에서 Redis를 기본 포트(6379)로 실행하고 있어서 Embedded Redis는 다른 포트를 지정해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 환경 설정 파일 추가&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;application.yml&lt;/blockquote&gt;
&lt;pre id=&quot;code_1739585583018&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  data:
    redis:
      host: 127.0.0.1
      port: 6379

---
spring:
  config:
    activate:
      on-profile: test
  data:
    redis:
      host: 127.0.0.1
      port: 63790&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test 환경에서는 redis의 port가 다르므로 환경 설정 파일에 해당 내용을 추가해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 알 수 있는 건 환경마다 환경 설정을 다르게 할 수 있다는 사실입니다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 테스트 코드 작성&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Test.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1739585810575&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootTest
@Import(EmbeddedRedis.class)
@ActiveProfiles(&quot;test&quot;)
class UserQueueServiceTest {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 Redis Configuration을 Import 해주고, Profile을 적용하기 위해 ActiveProfiles도 추가해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;https://fastcampus.co.kr/dev_online_traffic_data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739585886843&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&quot; data-og-description=&quot;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사 &quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xqg4I/hyYfWkcR9X/HDdckvttVzWqiLC9wptycK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/ckOYOx/hyYf4JhenH/Inq90AiaT5mBR3aCM36Eo0/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/nC84q/hyYf1y15HW/WHd1Kdp7PvKaHFJODhUCFK/img.png?width=1904&amp;amp;height=400&amp;amp;face=0_0_1904_400&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xqg4I/hyYfWkcR9X/HDdckvttVzWqiLC9wptycK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/ckOYOx/hyYf4JhenH/Inq90AiaT5mBR3aCM36Eo0/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/nC84q/hyYf1y15HW/WHd1Kdp7PvKaHFJODhUCFK/img.png?width=1904&amp;amp;height=400&amp;amp;face=0_0_1904_400');&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;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/992</guid>
      <comments>https://hj0216.tistory.com/992#entry992comment</comments>
      <pubDate>Mon, 17 Feb 2025 20:36:38 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Task Execution and Scheduling</title>
      <link>https://hj0216.tistory.com/991</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b25sew/btsMk0ZAPU7/38nMKhtSptLTVeP9ExrCLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b25sew/btsMk0ZAPU7/38nMKhtSptLTVeP9ExrCLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b25sew/btsMk0ZAPU7/38nMKhtSptLTVeP9ExrCLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb25sew%2FbtsMk0ZAPU7%2F38nMKhtSptLTVeP9ExrCLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤헷,, 제가 한 건 아니고,, Spring Boot Scheduler가 한 것입니다,,&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;사용 방법이 간단해서 짧게 정리하면서 git bash에서 하염없이 백그라운드 요청을 했던 조그마한 실수도 공유합니다^^..&lt;/p&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. Spring boot Scheduler란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 백그라운드에서 반복적으로 수행해야 하는 작업을 위해 사용되며, 정해진 시간이나 주기에 따라 작업을 실행할 수 있게 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대기열에 계신 고객님들을 손수 대기열에서 제거해드릴 수 있지만, 저보단 Spring Boot Scheduler가 더 잘 할 것이라 믿습니다^^!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;blockquote data-ke-style=&quot;style2&quot;&gt;FlowApplication.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1739594119417&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableScheduling
@SpringBootApplication
public class FlowApplication {

	public static void main(String[] args) {
		SpringApplication.run(FlowApplication.class, args);
	}
}&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;@EnableScheduling을 통해 Scheduler 사용을 활성화힙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. Scheduler를 실행할 메서드 지정&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;UserQueueService.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1739594218579&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class UserQueueService {
  @Scheduled(initialDelay = 5000, fixedDelay = 10000) // 서버 시작 후 5초 뒤, 이후 10초마다 메서드 실행
  public void scheduleAllowUser(){
    log.info(&quot;called Scheduling...&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;4. local/test 환경에서 Scheduler 동작 설정&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;application.yml&lt;/blockquote&gt;
&lt;pre id=&quot;code_1739594342282&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 9010

spring:
  data:
    redis:
      host: 127.0.0.1
      port: 6379

scheduler:
  enabled: true

---
spring:
  config:
    activate:
      on-profile: test
  data:
    redis:
      host: 127.0.0.1
      port: 63790

scheduler:
  enabled: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 Scheduler를 실행하면 예상과는 다르게 동작할 수 있으므로 잠시 실행을 꺼둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739594456105&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class UserQueueService {
  @Value(&quot;${scheduler.enabled}&quot;)
  private Boolean scheduling = false;
  
  @Scheduled(initialDelay = 5000, fixedDelay = 10000)
  public void scheduleAllowUser(){
    if(!scheduling){
      log.info(&quot;passed scheduling...&quot;);
      return;
    }

    log.info(&quot;called Scheduling...&quot;);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 설정 value를 받아서 scheduling값에 할당해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test 환경에서 enabled를 false로 설정해두었으니, passed scheduling을 호출하고 메서드는 종료될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; CMD를 이용해서 API 요청을 보낼 때, 주의할 점&lt;/p&gt;
&lt;pre id=&quot;code_1739594691354&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X POST localhost:9010/api/v1/queue?user_id=1001&amp;amp;queue=test1
[1]+  Done                    curl -X POST localhost:9010/api/v1/queue?user_id=1001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 처럼 요청하면, 의문의 [1]+ 함께 제 queue 파라미터를 무시합니다.&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;Shell에서는 &amp;amp;가 명령어를 백그라운드에서 실행하도록 만들기 때문에, curl 명령어에서 &amp;amp;를 사용하면 URL의 쿼리 파라미터가 아니라 셸 명령어로 해석될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 뒤에 붙은 queue=test1은 무시되거나 처리되지 않아 user_id 파라미터만 전달됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739595421073&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X POST &quot;localhost:9010/api/v1/queue?user_id=1001&amp;amp;queue=test1&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우에는 간단히, URL을 큰따옴표로 감싸서 셸이 URL을 제대로 인식하도록 하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;https://fastcampus.co.kr/dev_online_traffic_data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739594605451&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&quot; data-og-description=&quot;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사 &quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xqg4I/hyYfWkcR9X/HDdckvttVzWqiLC9wptycK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/ckOYOx/hyYf4JhenH/Inq90AiaT5mBR3aCM36Eo0/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/nC84q/hyYf1y15HW/WHd1Kdp7PvKaHFJODhUCFK/img.png?width=1904&amp;amp;height=400&amp;amp;face=0_0_1904_400&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xqg4I/hyYfWkcR9X/HDdckvttVzWqiLC9wptycK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/ckOYOx/hyYf4JhenH/Inq90AiaT5mBR3aCM36Eo0/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/nC84q/hyYf1y15HW/WHd1Kdp7PvKaHFJODhUCFK/img.png?width=1904&amp;amp;height=400&amp;amp;face=0_0_1904_400');&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;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@ktf1686/Spring-Spring-Scheduler-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@ktf1686/Spring-Spring-Scheduler-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739593920312&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;[Spring] Spring Scheduler 적용해보기&quot; data-og-description=&quot;스프링 스케줄러는 Java에서 제공하는 스프링 프레임워크의 일부로, 애플리케이션 내에서 정기적으로 실행되어야 하는 작업을 스케줄링하는데 사용됩니다.스프링 스케줄러는 주로 백그라운드&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@ktf1686/Spring-Spring-Scheduler-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@ktf1686/Spring-Spring-Scheduler-적용해보기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bZCCV1/hyYfKwYyTJ/7iPRyeBq14g3Foyla3ZsV1/img.png?width=592&amp;amp;height=288&amp;amp;face=51_95_131_182,https://scrap.kakaocdn.net/dn/b2ZOg6/hyYfK49DUm/8OanOMJADg21zKuDv784VK/img.png?width=592&amp;amp;height=288&amp;amp;face=51_95_131_182,https://scrap.kakaocdn.net/dn/cE2Uj2/hyYfWYPw1q/HJKeThlqkIkl32H6tJyEs0/img.png?width=892&amp;amp;height=419&amp;amp;face=0_0_892_419&quot;&gt;&lt;a href=&quot;https://velog.io/@ktf1686/Spring-Spring-Scheduler-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@ktf1686/Spring-Spring-Scheduler-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bZCCV1/hyYfKwYyTJ/7iPRyeBq14g3Foyla3ZsV1/img.png?width=592&amp;amp;height=288&amp;amp;face=51_95_131_182,https://scrap.kakaocdn.net/dn/b2ZOg6/hyYfK49DUm/8OanOMJADg21zKuDv784VK/img.png?width=592&amp;amp;height=288&amp;amp;face=51_95_131_182,https://scrap.kakaocdn.net/dn/cE2Uj2/hyYfWYPw1q/HJKeThlqkIkl32H6tJyEs0/img.png?width=892&amp;amp;height=419&amp;amp;face=0_0_892_419');&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;[Spring] Spring Scheduler 적용해보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스프링 스케줄러는 Java에서 제공하는 스프링 프레임워크의 일부로, 애플리케이션 내에서 정기적으로 실행되어야 하는 작업을 스케줄링하는데 사용됩니다.스프링 스케줄러는 주로 백그라운드&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://spring.io/guides/gs/scheduling-tasks&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://spring.io/guides/gs/scheduling-tasks&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739594010121&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Getting Started | Scheduling Tasks&quot; data-og-description=&quot;Although scheduled tasks can be embedded in web applications, the simpler approach (shown in this guide) creates a standalone application. To do so, package everything in a single, executable JAR file, driven by a Java main() method. The following snippet &quot; data-og-host=&quot;spring.io&quot; data-og-source-url=&quot;https://spring.io/guides/gs/scheduling-tasks&quot; data-og-url=&quot;https://spring.io/guides/gs/scheduling-tasks&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxO33b/hyYfBG62XJ/yqBKw0NX0I6wzgLtoJBQlk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/eafRsq/hyYfSozuy2/bs7GCCuiKOmkzh1Vu5G8Kk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://spring.io/guides/gs/scheduling-tasks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://spring.io/guides/gs/scheduling-tasks&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxO33b/hyYfBG62XJ/yqBKw0NX0I6wzgLtoJBQlk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/eafRsq/hyYfSozuy2/bs7GCCuiKOmkzh1Vu5G8Kk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Getting Started | Scheduling Tasks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Although scheduled tasks can be embedded in web applications, the simpler approach (shown in this guide) creates a standalone application. To do so, package everything in a single, executable JAR file, driven by a Java main() method. The following snippet&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/991</guid>
      <comments>https://hj0216.tistory.com/991#entry991comment</comments>
      <pubDate>Sun, 16 Feb 2025 22:44:13 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Custom Exception</title>
      <link>https://hj0216.tistory.com/990</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/zanmang-loopy-ver-4&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pFOSS/btsMbIFXEBg/V4l933J8YyJtDL0VN1kvQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpFOSS%2FbtsMbIFXEBg%2FV4l933J8YyJtDL0VN1kvQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&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;기존에 마실가실 리팩토링을 진행하면서 Custom Exception을 정리했던 적이 있는데, 이번에 새로 알게된 Record 객체와 더불어 다시 한 번 Custom Exception을 정리해 보고자합니다.&lt;/p&gt;
&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://hj0216.tistory.com/948&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[1년 후 마실가실] Custom Error 처리&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. ApplicationException.java&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RuntimeException을 상속하는 Custom Exception을 선언합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exception이 발생했을 때, 고갱님께 의도하는 ErrorCode와 메시지를 보여드리기 위함입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739091672992&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@AllArgsConstructor
@Getter
public class ApplicationException extends RuntimeException {
  private HttpStatus status;
  private String code;
  private String reason;

}&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_1739091819257&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;status&quot;:500,&quot;error&quot;:&quot;Internal Server Error&quot;,
&quot;requestId&quot;:&quot;679d92c0-1&quot;,&quot;message&quot;:&quot;already registered...&quot;,
&quot;trace&quot;:&quot;java.lang.Exception: already registered...\r\n
\tat com.traffic.flow.service.UserQueueService.registerWaitQueue(UserQueueService.java:22)\r\n\t
Suppressed: The stacktrace has been enhanced by Reactor, 
refer to additional information below: \r\n
Error has been observed at the following site(s):\r\n
\t*__checkpoint ⇢ Handler com.traffic.flow.controller.UserQueueController#registerUser(Long) [DispatcherHandler]
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. ErrorCode.java&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 에러 코드를 Enum 형식으로 정리해놓으면 기분이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;농담이고, enum을 사용하면 한 곳(ErrorCode)에서 예외를 관리할 수 있어서 유지보수가 용이해지기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, build() 메서드를 활용해서 status, code, reason을 계속해서 직접 입력했을 때, 실수할 가능성도 낮출 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739092048039&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@AllArgsConstructor
public enum ErrorCode {
  QUEUE_ALREADY_REGISTERED_USER(HttpStatus.CONFLICT, &quot;UQ-0001&quot;, &quot;Already Registered in Queue&quot;),

  private final HttpStatus status;
  private final String code;
  private final String reason;

  public ApplicationException build(){
    return new ApplicationException(status, code, reason);
    // ErrorCode.QUEUE_ALREADY_REGISTERED_USER.build()를 호출하면 ApplicationException이 생성
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; ⭐ build() 메서드 동작 과정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 enum은 내부적으로 객체처럼 동작하며, 생성된 인스턴스를 통해 필드 값을 참조할 수 있습니다.&lt;br /&gt;ErrorCode.QUEUE_ALREADY_REGISTERED_USER.build()를 호출하면 QUEUE_ALREADY_REGISTERED_USER의 필드 값이 자동으로 ApplicationException 생성자에 전달됩니다.&lt;br /&gt;따라서, 각 예외 코드마다 status, code, reason을 직접 넘겨줄 필요 없이 enum을 활용해 깔끔하게 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  동적&amp;nbsp;메시지&amp;nbsp;포맷팅&amp;nbsp;지원&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739094526663&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@AllArgsConstructor
public enum ErrorCode {
  QUEUE_ALREADY_REGISTERED_USER_IN_QUEUE(HttpStatus.CONFLICT, &quot;UQ-0002&quot;, &quot;Already Registered in Queue-%s&quot;);
  // ErrorCode.QUEUE_ALREADY_REGISTERED_USER_IN_QUEUE.build(&quot;A&quot;); -&amp;gt; Already Registered in Queue-A
  // 인자를 초과해서 넘길 경우 -&amp;gt; 무시, 인자보다 부족하게 넘김 경우 -&amp;gt; MissingFormatArgumentException 발생

  private final HttpStatus status;
  private final String code;
  private final String reason;

  public ApplicationException build(Object ...args){ // 0개 이상의 인자를 받을 수 있음
    return new ApplicationException(status, code, reason.formatted(args));
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  formatted 메서드는 Java15 이상부터 사용 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. applicationExceptionHandler.java&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 전역에서 발생하는 예외를 처리하는 @RestControllerAdvice를 사용하여 applicationExceptionHandler를 만듭니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739092719693&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestControllerAdvice
// Spring에서 전역 예외 처리를 담당하는 클래스
// 컨트롤러에서 발생하는 예외를 가로채고, 적절한 응답을 반환
public class ApplicationAdvice {

  @ExceptionHandler(ApplicationException.class)
  // ApplicationException이 발생했을 때 실행되는 핸들러
  Mono&amp;lt;ResponseEntity&amp;lt;ServerExceptionResponse&amp;gt;&amp;gt; applicationExceptionHandler(ApplicationException e) {
    return Mono.just(ResponseEntity // ResponseEntity: HTTP 응답을 생성하는 객체
               .status(e.getStatus())
               .body(new ServerExceptionResponse(e.getCode(), e.getReason()))); // JSON 응답으로 변환할 데이터 구조
  }

  public record ServerExceptionResponse(String code, String reason) {

  }&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;⭐ record ServerExceptionResponse를 사용하는 이유&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; 1. JSON이 아니기 때문에 &lt;b&gt;프론트엔드에서 파싱할 때 문제가 발생할 수 있음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. 새로운 정보를 추가하기 어려워 확장성이 떨어짐&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 57px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;JSON 응답&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt; 확장성 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;유지보수&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;e.getCode() + e.getReason()&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;❌ 단순 문자열&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;❌ 새로운 필드 추가 어려움&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;❌ 가독성 떨어짐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;new ServerExceptionResponse&lt;br /&gt;(e.getCode(), e.getReason())&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;✅ JSON 자동 변환&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;✅ 필드 추가 쉬움&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;✅ 직관적&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Jackson 라이브러리가 있으면 record는 기본적으로 자동으로 JSON 직렬화됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * webflux, mvc 모두 jackson 라이브러리를 포함하고 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; ⭐ &amp;nbsp;Java record&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불변(immutable) 데이터 객체를 간단하게 정의하기 위한 새로운 클래스 유형으로, &lt;b&gt;Java 16&lt;/b&gt;부터 정식 기능으로 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * getter, toString(), equals(), hashCode() 등을 자동으로 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;b&gt;데이터 전달용 객체(DTO)나 불변 객체를 만들 때 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739093237347&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public record RegisterUserResponse(Long rank) {}

// record -&amp;gt; class
public final class RegisterUserResponse {
// final class로 다른 클래스 extends 불가, 인터페이스 implements만 가능
    private final Long rank;
    // 필드 값을 변경할 수 없음

    public RegisterUserResponse(Long rank) {
        this.rank = rank;
    }

    public Long rank() { // getter 역할, *필드명과 동일한 이름을 가짐
        return rank;
    }

    @Override
    public String toString() {
        return &quot;RegisterUserResponse[rank=&quot; + rank + &quot;]&quot;;
    }

    @Override
    public boolean equals(Object o) { /* equals() 자동 생성 */ }

    @Override
    public int hashCode() { /* hashCode() 자동 생성 */ }
}&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;4. 결과&lt;/p&gt;
&lt;pre id=&quot;code_1739093109565&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;* Connected to localhost (127.0.0.1) port 9010 (#0)
&amp;gt; POST /api/v1/queue?user_id=85 HTTP/1.1
&amp;gt; Host: localhost:9010
&amp;gt; User-Agent: curl/7.87.0 #요청을 보낸 애플리케이션
&amp;gt; Accept: */*
&amp;gt;
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 409 Conflict
&amp;lt; Content-Type: application/json
&amp;lt; Content-Length: 57
&amp;lt;
{ [57 bytes data] 100    57  100    57    0     0   3879      0 --:--:-- --:--:-- --:--:--  4071
# 100: 총 데이터의 100%를 받았다는 의미
# 57: 총 데이터 크기 (57 바이트)
# 100: 총 데이터의 100%를 보냈다는 의미
# 57: 전송된 데이터 크기 (57 바이트)
# 0: 업로드 크기 (현재 요청이 POST지만, 업로드할 데이터가 없으면 0)
# 0: 현재 업로드 속도(body에 데이터가 있는 경우)
# 3879: 다운로드 속도 (3.879 KB/s)
# 0: 예상 남은 시간 (이미 완료되었으면 0)
{&quot;code&quot;:&quot;UQ-0001&quot;,&quot;reason&quot;:&quot;Already Registered in Queue&quot;}
* Connection #0 to host localhost left intact #서버가 연결을 유지한 상태&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 style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://fastcampus.co.kr/dev_online_traffic_data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739092504700&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&quot; data-og-description=&quot;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사 &quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bKCy1R/hyYcdUuSzF/aOM3lQl3l3Rl8ETLj7jpPk/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/qAVcy/hyYb8yTzII/K28KDBJeNCPBwh89eLARpK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/blXZ5I/hyYcjmRWPc/2ukLRvjWaagVfinOdeDUFK/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bKCy1R/hyYcdUuSzF/aOM3lQl3l3Rl8ETLj7jpPk/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/qAVcy/hyYb8yTzII/K28KDBJeNCPBwh89eLARpK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/blXZ5I/hyYcjmRWPc/2ukLRvjWaagVfinOdeDUFK/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520');&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;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/990</guid>
      <comments>https://hj0216.tistory.com/990#entry990comment</comments>
      <pubDate>Thu, 13 Feb 2025 18:48:56 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Spring WebFlux와 BlockHound</title>
      <link>https://hj0216.tistory.com/989</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/zanmang-loopy-ver-3&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ufpWy/btsMcQXaUdp/xvrPVloiKa1iwvnyErmm11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FufpWy%2FbtsMcQXaUdp%2FxvrPVloiKa1iwvnyErmm11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BlockHound 정리해야지,, 생각만 하다가 어느덧 일주일이 흘러버렸습니다,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 BlockHound,, 기억이 나지 않는다는 것^^,,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런걸 진정한 복습이라 생각하며, 기억과 코드를 찾아보며 정리해 보고자합니다,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할 수 있다,, 기억해 낼 수 있다,, 아자아자,, &amp;zwj; &lt;/p&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. BlockHound 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;개발자가 직접 작성한 코드를 포함하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;JDK&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;, Thrid-party 라이브러리에 사용된 블로킹 메소드 호출을 모두 찾아내서 알려주는 도구&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;  &lt;span style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;Spring Webflux 기반의 애플리케이션은 모든 코드가 &lt;i&gt;&lt;u&gt;&lt;b&gt;Reactive 방식&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;으로 작동해야 최상의 성능이 나옵니다. 즉, blocking 코드가 존재한다면 충분한 성능이 나오지 않을 수도 있다는 것입니다. 이를 위해 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;BlockHound를 사용합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;i&gt;&lt;u&gt;&lt;b&gt;❓Reactive 방식&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&lt;br /&gt;비동기(Asynchronous) &lt;br /&gt;논블로킹(Non-blocking) &lt;br /&gt;데이터 스트림(Streams): 데이터를 필요한 만큼 조금씩 전달하며 처리하는 방식 &lt;br /&gt;Backpressure: 데이터 소비자가 처리할 수 있는 속도를 조절하는 기능 &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;a href=&quot;https://github.com/reactor/BlockHound&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/reactor/BlockHound&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739068698749&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - reactor/BlockHound: Java agent to detect blocking calls from non-blocking threads.&quot; data-og-description=&quot;Java agent to detect blocking calls from non-blocking threads. - reactor/BlockHound&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/reactor/BlockHound&quot; data-og-url=&quot;https://github.com/reactor/BlockHound&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/grhPN/hyYb8MliTC/N89fdlGvNqvzVomQSgAHn0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/SisjT/hyYcd7Wvc7/UH2U2KKsiieakYks5RtJX0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/reactor/BlockHound&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/reactor/BlockHound&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/grhPN/hyYb8MliTC/N89fdlGvNqvzVomQSgAHn0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/SisjT/hyYcd7Wvc7/UH2U2KKsiieakYks5RtJX0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - reactor/BlockHound: Java agent to detect blocking calls from non-blocking threads.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Java agent to detect blocking calls from non-blocking threads. - reactor/BlockHound&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. BlockHound 설정&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;build.gradle&lt;br /&gt;테스트 시, 사용 예정이므로 testImplementation을 선언&lt;br /&gt;버전은 Github를 참고하여 최신 버전을 입력(25.02.09기준 최신 버전 - 1.0.10)&lt;/blockquote&gt;
&lt;pre id=&quot;code_1739069734615&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
	testImplementation 'io.projectreactor.tools:blockhound:1.0.10.RELEASE'
}&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;3. 테스트 코드 작성&lt;/p&gt;
&lt;pre id=&quot;code_1739070246996&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void setUp() {
    BlockHound.install();
}

@Test
void shouldDetectBlockingCall() {
  Mono&amp;lt;Long&amp;gt; blockingMono = Mono.delay(Duration.ofSeconds(1))
                                .doOnNext(t -&amp;gt; {
                                  try {
                                    Thread.sleep(100);  // 블로킹 호출
                                  } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                  }
                                });

  StepVerifier.create(blockingMono)
              .expectError(BlockingOperationError.class)
              .verify();
}&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;하지만, 살다보면 Webflux에서도 Blocking을 허용해야하는 순간이 올 것입니다.. &lt;/p&gt;
&lt;pre id=&quot;code_1739070703053&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static {
    BlockHound.install(builder -&amp;gt;
        builder.allowBlockingCallsInside(UserControllerTest.class.getName(), &quot;shouldAllowBlockingCallInsideAllowedMethod&quot;)
    );
}

@Test
void shouldAllowBlockingCallInsideAllowedMethod() {
  Mono&amp;lt;Long&amp;gt; allowedMono = Mono.delay(Duration.ofSeconds(1))
                               .doOnNext(t -&amp;gt; {
                                 try {
                                   Thread.sleep(100);
                                 } catch (InterruptedException e) {
                                   throw new RuntimeException(e);
                                 }
                               });

  StepVerifier.create(allowedMono)
              .expectNextCount(0)
              .verifyComplete();
}&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;  분명 Blocking을 허용하겠다고 했는데, 아래와 같은 오류가 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739070786604&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;expectation &quot;expectComplete&quot; failed (expected: onComplete(); 
actual: onError(reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep))&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;allowBlockingCallsInside는 &lt;b&gt;특정 클래스의 특정 메서드 내부에서 발생하는 블로킹 호출&lt;/b&gt;만 허용하는데, &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;shouldAllowBlockingCallInsideAllowedMethod안에 있는 Thread.sleep(100)은 doOnNext의 람다 내부에서 호출되고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, BlockHound는 람다 표현식을 &lt;b&gt;익명 클래스&lt;/b&gt;로 취급하기 때문에, allowBlockingCallsInside가 해당 호출을 허용하지 않게 되어 오류를 발생시킵니다.&lt;/p&gt;
&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_1739071120108&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static {
    BlockHound.install(builder -&amp;gt;
        builder.allowBlockingCallsInside(UserControllerTest.class.getName(), &quot;allowedBlockingMethod&quot;)
);

// 블로킹 호출을 별도 메서드로 분리
void allowedBlockingMethod() {
  try {
    Thread.sleep(100);
  } catch (InterruptedException e) {
    throw new RuntimeException(e);
  }
}

@Test
void shouldAllowBlockingCallInsideAllowedMethod() {
  Mono&amp;lt;Long&amp;gt; allowedMono = Mono.delay(Duration.ofSeconds(1)) // 1초 후에 0L이라는 값을 방출
                                     .doOnNext(__ -&amp;gt; allowedBlockingMethod());

  StepVerifier.create(allowedMono)
              .expectNextCount(1)
              .verifyComplete();
}&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 style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739068734523&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;[Spring Webflux] BlockHound 적용하기&quot; data-og-description=&quot;비동기 프로그램을 구현하기 위해 블로킹 메소드를 검출하는 BlockHound를 적용해보자&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-적용하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/caVS4Y/hyYceeI2QK/0y9a91UY1NJqFle47Y8zN1/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207,https://scrap.kakaocdn.net/dn/ioI5I/hyYccgSE8j/7QjyFjakEqlocaYKE2ZDq0/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207,https://scrap.kakaocdn.net/dn/bnM1s8/hyYb8euTIC/R6mr1FTxNezQ9NwZuCQdZ1/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207&quot;&gt;&lt;a href=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/caVS4Y/hyYceeI2QK/0y9a91UY1NJqFle47Y8zN1/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207,https://scrap.kakaocdn.net/dn/ioI5I/hyYccgSE8j/7QjyFjakEqlocaYKE2ZDq0/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207,https://scrap.kakaocdn.net/dn/bnM1s8/hyYb8euTIC/R6mr1FTxNezQ9NwZuCQdZ1/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207');&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;[Spring Webflux] BlockHound 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;비동기 프로그램을 구현하기 위해 블로킹 메소드를 검출하는 BlockHound를 적용해보자&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739068740064&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;[Spring Webflux] BlockHound 적용하기&quot; data-og-description=&quot;비동기 프로그램을 구현하기 위해 블로킹 메소드를 검출하는 BlockHound를 적용해보자&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-적용하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/caVS4Y/hyYceeI2QK/0y9a91UY1NJqFle47Y8zN1/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207,https://scrap.kakaocdn.net/dn/ioI5I/hyYccgSE8j/7QjyFjakEqlocaYKE2ZDq0/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207,https://scrap.kakaocdn.net/dn/bnM1s8/hyYb8euTIC/R6mr1FTxNezQ9NwZuCQdZ1/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207&quot;&gt;&lt;a href=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/caVS4Y/hyYceeI2QK/0y9a91UY1NJqFle47Y8zN1/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207,https://scrap.kakaocdn.net/dn/ioI5I/hyYccgSE8j/7QjyFjakEqlocaYKE2ZDq0/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207,https://scrap.kakaocdn.net/dn/bnM1s8/hyYb8euTIC/R6mr1FTxNezQ9NwZuCQdZ1/img.png?width=2948&amp;amp;height=1207&amp;amp;face=0_0_2948_1207');&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;[Spring Webflux] BlockHound 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;비동기 프로그램을 구현하기 위해 블로킹 메소드를 검출하는 BlockHound를 적용해보자&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/989</guid>
      <comments>https://hj0216.tistory.com/989#entry989comment</comments>
      <pubDate>Tue, 11 Feb 2025 23:25:52 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] NullPointerException: Mocking</title>
      <link>https://hj0216.tistory.com/988</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/alone-zanmang-loopy&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7WKyM/btsMbXW0Pdd/690cBQSvx8JdKd3RXNmoek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7WKyM%2FbtsMbXW0Pdd%2F690cBQSvx8JdKd3RXNmoek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&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;아Nl요.. 없어요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔진 모르겠지만, 어쨌든 뭐가 없어요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Test Code를 작성하다 필요한 조건들을 모두 Mocking 했는데도 충분하지 않다며 NullPointerException을 반환하는 문제를 해결하는 과정을 정리해 봅니다..❓&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;ID 기준 Delete 메서드의 테스트 코드 작성&lt;/p&gt;
&lt;pre id=&quot;code_1739004493493&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
void deleteUserById() {
  when(userService.deleteById(1L)).thenReturn(
      Mono.empty()
  );

  client.delete().uri(&quot;/users/1&quot;)
        .exchange()
        .expectStatus()
        .is2xxSuccessful();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 문제&lt;/p&gt;
&lt;pre id=&quot;code_1739004628357&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.NullPointerException: Cannot invoke &quot;reactor.core.publisher.Mono.flatMap(java.util.function.Function)&quot; because the return value of &quot;com.example.webflux1.service.UserService.findById(java.lang.Long)&quot; is null
	at com.example.webflux1.controller.UserController.deleteUserById(UserController.java:98) ~[main/:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ HTTP DELETE &quot;/users/1&quot; [ExceptionHandlingWebHandler]
Original Stack Trace:
		at com.example.webflux1.controller.UserController.deleteUserById(UserController.java:98) ~[main/:na]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;findById에서 null을 리턴하여 flatMap 메서드를 호출할 수 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; Id 기준으로 회원 조회 시, 없을 경우, Mono.empty()가 아닌 null을 반환하는 것이 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❓ReactiveCrudRepository&amp;lt;User, Long&amp;gt;를 구현하는 UserR2dbcRepository는 findById에 해당하는 회원이 없을 경우 Mono.empty()를 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;pre id=&quot;code_1739004770119&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@DeleteMapping(&quot;/{id}&quot;)
public Mono&amp;lt;ResponseEntity&amp;lt;Object&amp;gt;&amp;gt; deleteUserById(@PathVariable Long id) {
  // user x: 404, Not Found
  // user o: 204, No Content
  return userService.findById(id)
                    .flatMap(user -&amp;gt; userService.deleteById(id)
                                                .then(Mono.just(ResponseEntity.noContent().build())))
                    .switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 컨트롤러에서 findById 호출 후, deleteById를 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; findById에 대한 모킹이 없어서 null 반환&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;@MockBean으로 만든 mock은 &lt;b&gt;모킹하지 않은 메소드&lt;/b&gt;에 대해: &lt;br /&gt;&amp;nbsp; * primitive 타입은 기본값 (0, false 등)&lt;br /&gt;&amp;nbsp; * 객체 타입은 null&lt;br /&gt;&amp;nbsp; * Mono/Flux 타입도 null을 반환 ( Mono.empty()가 아님)&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;⭐ ReactiveCrudRepository 동작 방식과 무관하게 MockBean에서 모킹하지 않은 메서드를 어떤 방식으로 처리하는가에 대한 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;findById도 모킹&lt;/p&gt;
&lt;pre id=&quot;code_1739006313688&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
void deleteUserById() {
  when(userService.findById(1L)).thenReturn(Mono.just(new User()));
  when(userService.deleteById(1L)).thenReturn(
      Mono.empty()
  );

  client.delete().uri(&quot;/users/1&quot;)
        .exchange()
        .expectStatus().is2xxSuccessful();
}&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 style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Claude&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;ChatGPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/988</guid>
      <comments>https://hj0216.tistory.com/988#entry988comment</comments>
      <pubDate>Mon, 10 Feb 2025 21:32:03 +0900</pubDate>
    </item>
    <item>
      <title>Grid와 StackPanel.. 그리고 ScrollViewer</title>
      <link>https://hj0216.tistory.com/987</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/zanmang-loopy-ver-5?t_ch=share_link_web&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buSSRr/btsMckxAoyZ/aD5iCuAXM9waXRSK4zCki1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuSSRr%2FbtsMckxAoyZ%2FaD5iCuAXM9waXRSK4zCki1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF를 사용하면 전체적인 틀을 잡기 위해 제일 많이 쓰는 태그가 Grid, StackPanel 아닐까요..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS로 따지면 Container.. div.. 같은 느낌..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소 느낌으로,, 아아,, Grid겠지,, 아아,, StackPanel일거야,,하며 사용하고는 했는데, 이번에 ScrollViewer를 사용하면서깨달은 점을 기록할 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 특징을 간단히 정리해보고, 특징에 따른 주의사항에 대해 간단히 정리해봅니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Grid&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 행(Row)과 열(Column)로 UI를 구성(Table과 유사)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* RowDefinition과 ColumnDefinition을 활용하여 정확한 크기 조정 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Grid.RowSpan, Grid.ColumnSpan을 사용해 특정 요소를 여러 행 또는 열에 걸쳐 배치 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;b&gt;정렬이 필요한 UI&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739009738709&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Grid&amp;gt;
    &amp;lt;Grid.RowDefinitions&amp;gt;
        &amp;lt;RowDefinition Height=&quot;Auto&quot;/&amp;gt;
        &amp;lt;RowDefinition Height=&quot;*&quot;/&amp;gt;
    &amp;lt;/Grid.RowDefinitions&amp;gt;

    &amp;lt;TextBlock Grid.Row=&quot;0&quot; Text=&quot;Title&quot; FontSize=&quot;20&quot;/&amp;gt;
&amp;lt;/Grid&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;2. StackPanel&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 수직 또는 수평으로 자식 요소들을 한 줄로 배치(Flex와 유사)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;b&gt;내부 요소의 크기를 모두 합산&lt;/b&gt;하여 &lt;b&gt;자신의 크기를 결정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 레이아웃이 단순해서 &lt;b&gt;버튼 목록, 메뉴 등 간단한 UI에 적합&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739010097669&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;StackPanel Orientation=&quot;Vertical&quot;&amp;gt;
    &amp;lt;Button Content=&quot;Button 1&quot;/&amp;gt;
    &amp;lt;Button Content=&quot;Button 2&quot;/&amp;gt;
    &amp;lt;Button Content=&quot;Button 3&quot;/&amp;gt;
&amp;lt;/StackPanel&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;&lt;b&gt; 3. StackPanel이 Grid보다 사용하기 좋은 경우&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.34884%;&quot;&gt;&lt;b&gt;No.&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 32.5582%;&quot;&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 62.093%;&quot;&gt;&lt;b&gt;이유&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.34884%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 32.5582%;&quot;&gt;단순한 리스트형 UI&lt;/td&gt;
&lt;td style=&quot;width: 62.093%;&quot;&gt;한 방향으로 요소를 추가하는 UI에서 Grid보다 더 간결하고 코드가 짧음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.34884%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 32.5582%;&quot;&gt;자식 요소 개수가 동적으로 변할 때&lt;/td&gt;
&lt;td style=&quot;width: 62.093%;&quot;&gt;내부 요소가 많아져도 자동으로 크기를 조정하므로 레이아웃 관리가 쉬움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.34884%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 32.5582%;&quot;&gt;자동 크기 확장이 필요한 경우&lt;/td&gt;
&lt;td style=&quot;width: 62.093%;&quot;&gt;요소 개수에 따라 부드럽게 늘어나야 하는 레이아웃에 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  4. StackPanel 사용 시, 유의 사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StackPanel은 &lt;b&gt;자식 요소들의 크기를 모두 합쳐서 자신의 크기를 결정&lt;/b&gt;하는 방식이므로,&amp;nbsp; StackPanel 내부에 ScrollViewer가 있을 경우 &lt;b&gt;자식의 전체 크기를 그대로 반영하려하기 때문에, 스크롤 기능을 무시하게 됨&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; ScrollViewer 기능이 필요할 경우, StackPanel이 아닌 Grid를 사용해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>C#/WPF</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/987</guid>
      <comments>https://hj0216.tistory.com/987#entry987comment</comments>
      <pubDate>Sun, 9 Feb 2025 09:47:31 +0900</pubDate>
    </item>
    <item>
      <title>EventHandler: From UserControl To Window..✉️</title>
      <link>https://hj0216.tistory.com/986</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0BChH/btsMcTlVN8V/igOMyrXhuNqjhD3b8aQNc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0BChH/btsMcTlVN8V/igOMyrXhuNqjhD3b8aQNc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0BChH/btsMcTlVN8V/igOMyrXhuNqjhD3b8aQNc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0BChH%2FbtsMcTlVN8V%2FigOMyrXhuNqjhD3b8aQNc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예? 저에게 데이터가 전달되었다고요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 표현한 잔망루피입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 WPF 개발을 MVVM 구조로 진행해서 ViewModel과 Model을 사용해서 데이터를 전달하곤 했는데, 이번에는 개발 디자인 패턴이 바뀌어서 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;데이터 전달을 EventHandler라는 것을 사용하게 되는데 간단히 정리해보도록 하겠습니다.&lt;/p&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. EventHander 선언&lt;/p&gt;
&lt;pre id=&quot;code_1739000551658&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public event EventHandler&amp;lt;string&amp;gt; TitleSelected;
// string 타입의 데이터를 이벤트 핸들러에 전달할 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. EventHanlder가 사용될 이벤트 등록 및 구현&lt;/p&gt;
&lt;pre id=&quot;code_1739002021498&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lv_Presents.SelectionChanged += Lv_Presents_SelectionChanged;
// ListView에서 항목이 변경될 때 실행되는 이벤트(Lv_Presents_SelectionChanged)를 등록
// 선택 항목이 바뀔 때마다 메서드가 호출

private void lv_Presents_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (lv_Presents.SelectedItem is PresentModel model)
    {
        TitleSelected?.Invoke(this, model.title);
        // this: 현재 UserControl을 이벤트 발신자로 지정
        // model.title: 전달할 데이터
        // ?.Invoke: 이벤트 구독자에게 데이터 전달(구독자가 없을 때 예외가 발생하지 않음)
    }
}&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;3. 이벤트 구독 및 처리로직 구현&lt;/p&gt;
&lt;pre id=&quot;code_1739002605548&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uc_Presents.TitleSelected += uc_Presents_TitleSelected;
// uc_Presents TitleSelected 이벤트가 발생하면, uc_Presents_TitleSelected 메서드 실행
// Window가 생성될 때 이벤트 핸들러를 등록하여 UserControl에서 발생한 이벤트를 받을 준비를 함

private void uc_Presents_TitleSelected(object sender, string title)
{
    tblock_SelectedTitle.Text = 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  이벤트 해제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 객체가 더 이상 사용되지 않거나 이벤트를 더 이상 받을 필요가 없을 때는 -=로 해제하는 것이 좋음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 이벤트 핸들러가 &lt;b&gt;해제되지 않으면&lt;/b&gt; 구독자가 GC에 의해 수거되지 않기 때문에, &lt;b&gt;메모리 누수&lt;/b&gt;가 발생할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;b&gt;여러 번 구독하는 것을 방지&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C#/WPF</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/986</guid>
      <comments>https://hj0216.tistory.com/986#entry986comment</comments>
      <pubDate>Sat, 8 Feb 2025 17:20:55 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] MVC vs Webflux - JMeter를 이용한 부하테스트</title>
      <link>https://hj0216.tistory.com/985</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/alone-zanmang-loopy&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dI8HGk/btsL3qsoX6U/oEBJlKYVSOzAEjNtmTOosk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdI8HGk%2FbtsL3qsoX6U%2FoEBJlKYVSOzAEjNtmTOosk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JMeter와 함께하는 부하테스트,, 노트북이 버티지 못할까 조마조마한 마음으로 진행을 해보았습니다,,&lt;/p&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 내부의 mysql이 CPU를 100% 넘기면서까지 노력해줘서 잘 마무리를 했습니다,, &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게, JMeter 설치와 부하테스트 결과를 정리해 보겠습니다,,&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;&lt;b&gt; 기본 환경&lt;/b&gt;&lt;br /&gt;- OS: &lt;b&gt;Windows 11 Home&lt;/b&gt;&lt;br /&gt;- Language:&amp;nbsp;Java&lt;br /&gt;- DB:&amp;nbsp;MySQL, Redis&lt;br /&gt;-&amp;nbsp;IDE:&amp;nbsp;IntelliJ&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;&lt;b&gt;0. 사전 지식 &lt;b&gt; &lt;/b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;기준&lt;br /&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Spring MVC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; Spring WebFlux &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;처리 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;동기/블로킹&lt;/td&gt;
&lt;td&gt;비동기/논블로킹&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;서버 지원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Tomcat (Servlet 기반)&lt;/td&gt;
&lt;td&gt;Netty, Undertow, Tomcat 등 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;성능 (대규모 트래픽)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스레드 수에 따라 성능 제한&lt;/td&gt;
&lt;td&gt;높은 동시성 처리 및 확장성 우수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;디버깅&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;용이&lt;/td&gt;
&lt;td&gt;어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;주요 사용 사례&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;전통적인 웹 앱, 관리 시스템&lt;/td&gt;
&lt;td&gt;실시간 데이터, 채팅, IoT, 스트리밍 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. JMeter 설치&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;The Apache JMeter&amp;trade; application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance. &lt;br /&gt;It was originally designed for testing Web Applications but has since expanded to other test functions. &lt;br /&gt;Apache JMeter&amp;trade; 애플리케이션은 기능 동작을 로드하고 &lt;b&gt;성능을 측정하도록 설계된 100% 순수 Java 애플리케이션&lt;/b&gt;인 오픈 소스 소프트웨어입니다. &lt;br /&gt;원래 웹 애플리케이션을 테스트하기 위해 설계되었지만 이후 다른 테스트 기능으로 확장되었습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⭐ JMeter는 순수 자바 애플리케이션으로, Java 8 문법을 내부적으로 사용하기 때문에 자바 8 버전 이상이 설치되어 있어야합니다.&lt;/p&gt;
&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;a href=&quot;https://developer-jinnie.tistory.com/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer-jinnie.tistory.com/105&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738469897705&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;[프로젝트] Windows 환경에서 JMeter 설치 및 부하 테스트 하기&quot; data-og-description=&quot;부하 테스트를 위해 JMeter 설치가 필요했던 나 ,, 윈도우에서 JMeter 설치를 비롯해 테스트를 해야 하는 분들이 이 포스팅 하나만 읽어도 쉽게 가능했으면 하는 마음으로 포스팅한다.&amp;nbsp;설치1. JMeter&quot; data-og-host=&quot;developer-jinnie.tistory.com&quot; data-og-source-url=&quot;https://developer-jinnie.tistory.com/105&quot; data-og-url=&quot;https://developer-jinnie.tistory.com/105&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TtYMI/hyYclwLHfY/8SZq5p3j2k8ktYm0BKuEp0/img.png?width=800&amp;amp;height=553&amp;amp;face=0_0_800_553,https://scrap.kakaocdn.net/dn/bfhNxU/hyYb7FhUm4/44NcPKkkc2wmHMFrFSBkdk/img.png?width=800&amp;amp;height=553&amp;amp;face=0_0_800_553,https://scrap.kakaocdn.net/dn/bCyoTY/hyX74co4JZ/cP0RUtl1ZKB646XRLNPKB0/img.png?width=2032&amp;amp;height=1271&amp;amp;face=0_0_2032_1271&quot;&gt;&lt;a href=&quot;https://developer-jinnie.tistory.com/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer-jinnie.tistory.com/105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TtYMI/hyYclwLHfY/8SZq5p3j2k8ktYm0BKuEp0/img.png?width=800&amp;amp;height=553&amp;amp;face=0_0_800_553,https://scrap.kakaocdn.net/dn/bfhNxU/hyYb7FhUm4/44NcPKkkc2wmHMFrFSBkdk/img.png?width=800&amp;amp;height=553&amp;amp;face=0_0_800_553,https://scrap.kakaocdn.net/dn/bCyoTY/hyX74co4JZ/cP0RUtl1ZKB646XRLNPKB0/img.png?width=2032&amp;amp;height=1271&amp;amp;face=0_0_2032_1271');&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;[프로젝트] Windows 환경에서 JMeter 설치 및 부하 테스트 하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;부하 테스트를 위해 JMeter 설치가 필요했던 나 ,, 윈도우에서 JMeter 설치를 비롯해 테스트를 해야 하는 분들이 이 포스팅 하나만 읽어도 쉽게 가능했으면 하는 마음으로 포스팅한다.&amp;nbsp;설치1. JMeter&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer-jinnie.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. JMeter Plugin 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction per Seconds와 Response Times Over Time을 함께 측정하기 위해 플러그인도 함께 설치합니다.&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://blog.naver.com/ka28/222492575671&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/ka28/222492575671&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738470262539&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;[JMETER] 부하 테스트로 TPS 측정하기(JMeter 플러그인 사용)&quot; data-og-description=&quot;JMeter 플러그인 다운 및 적용하기 아래 하단의 사이트를 들어간다. https://jmeter-plugins.org/wiki/T...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://blog.naver.com/ka28/222492575671&quot; data-og-url=&quot;https://blog.naver.com/ka28/222492575671&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/srIlJ/hyX73ExFP5/qOFndYP8nqZjq0Q2OzDpW0/img.png?width=743&amp;amp;height=627&amp;amp;face=0_0_743_627&quot;&gt;&lt;a href=&quot;https://blog.naver.com/ka28/222492575671&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.naver.com/ka28/222492575671&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/srIlJ/hyX73ExFP5/qOFndYP8nqZjq0Q2OzDpW0/img.png?width=743&amp;amp;height=627&amp;amp;face=0_0_743_627');&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;[JMETER] 부하 테스트로 TPS 측정하기(JMeter 플러그인 사용)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JMeter 플러그인 다운 및 적용하기 아래 하단의 사이트를 들어간다. https://jmeter-plugins.org/wiki/T...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 테스트할 API 준비&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 MVC와 WebFlux에 대해 크게 3가지씩 테스트 할 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 단순 String을 반환하는 API&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * RDB를 조회하는 API&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Cache를 활용한 API&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Case&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;MVC&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Webflux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;단순 String 반환&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;RDB 데이터 조회&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Caching 활용&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략 결과는 Webflux가 MVC보다 빠르되, String &amp;rarr; Caching &amp;rarr; RDB 순으로 빠르지 않을까,, 라고 예상해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. Jmeter 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbihEl/btsL4qkRnsc/QXJ3DXTc6ZZ4fdp8loAAXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbihEl/btsL4qkRnsc/QXJ3DXTc6ZZ4fdp8loAAXk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;856&quot; data-filename=&quot;mvc-settings-thread.png&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbihEl/btsL4qkRnsc/QXJ3DXTc6ZZ4fdp8loAAXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbihEl%2FbtsL4qkRnsc%2FQXJ3DXTc6ZZ4fdp8loAAXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1519&quot; height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mLyY7/btsL5OLABCD/scGQzotrVU4fKridI3f9yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mLyY7/btsL5OLABCD/scGQzotrVU4fKridI3f9yk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;856&quot; data-filename=&quot;mvc-settings-http.png&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mLyY7/btsL5OLABCD/scGQzotrVU4fKridI3f9yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmLyY7%2FbtsL5OLABCD%2FscGQzotrVU4fKridI3f9yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1519&quot; height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;(좌) Thread Group 설정 / (우) Http Reqeust 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Thread Groups&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Test Plan 우클릭 &amp;rarr; Add &amp;rarr; Threads (Users) &amp;rarr; Thread Group&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Numbers of Threads (users): 동시에 테스트를 수행할 가상의 사용자 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Ramp-up period (seconds): 모든 스레드(사용자)가 가동될 때까지 걸리는 시간(초), 부하를 점진적으로 증가시키고 싶을 때 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Ramp-up Period &amp;divide; Number of Threads = 각 스레드의 시작 간격&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Loop Count: 각 가상 사용자가 요청을 몇 번 반복할지 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;Http Reqeust&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread Group 우클릭 &amp;rarr; Add &amp;rarr; Sampler &amp;rarr; Http Request&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Protocol, Server name or IP, Port Number, Http Request 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Summary Report&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Http Request 우클릭 &amp;rarr; Add &amp;rarr; Listener &amp;rarr; Summary Report&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* #Samples: 테스트 동안 발생한 HTTP 요청의 총 개수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Average: 평균 응답 시간(ms)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Min / Max: 최소/최대 응답 시간(ms)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Std. Dev. (표준 편차): 응답 시간의 변동성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Error %: 에러율&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Throughput: 초당 처리된 요청 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Received KB/sec / Sent KB/sec: 초당 수신/송신된 데이터 양, 서버와의 데이터 전송 효율성을 나타냄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Avg. Bytes: 요청당 평균 전송된 데이터 크기&lt;/p&gt;
&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;Transaction Per Second&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; * TPS가 높으면 서버가 빠르게 요청을 처리한다는 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 테스트 중 TPS가 급격히 하락하면 서버 과부하 가능성이 존재&lt;/p&gt;
&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;Response Times Over Time&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;&amp;nbsp; * 스파이크(급격한 증가) 구간을 보면 병목 현상이나 과부하 시점을 파악할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;부하 테스트 전략&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Baseline 테스트&lt;/b&gt;: 적은 사용자 수로 정상 동작 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;부하 증가 테스트&lt;/b&gt;: Ramp-up을 활용해 점진적 부하 증가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스트레스 테스트&lt;/b&gt;: Ramp-up 없이 대량의 사용자로 한계 테스트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안정성 테스트&lt;/b&gt;: 오랜 시간 동안 고정된 부하로 서비스 안정성 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt; ✅단순 String 반환&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b55hVb/btsL5Br5gDM/okold9kJkFssvMXGvgdgK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b55hVb/btsL5Br5gDM/okold9kJkFssvMXGvgdgK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b55hVb/btsL5Br5gDM/okold9kJkFssvMXGvgdgK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb55hVb%2FbtsL5Br5gDM%2Fokold9kJkFssvMXGvgdgK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;902&quot; height=&quot;842&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 단순 String 반환 시, Summary Report 기준 Webflux가 Avg는 낮고, Throughput은 더 높음&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;style2&quot;&gt;✅&lt;b&gt;RDB 조회&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;899&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6rPiy/btsL4PqSSDA/L8EFXhZo6kO279pKE8xTd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6rPiy/btsL4PqSSDA/L8EFXhZo6kO279pKE8xTd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6rPiy/btsL4PqSSDA/L8EFXhZo6kO279pKE8xTd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6rPiy%2FbtsL4PqSSDA%2FL8EFXhZo6kO279pKE8xTd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;899&quot; height=&quot;780&quot; data-origin-width=&quot;899&quot; data-origin-height=&quot;780&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; RDB 조회 시, Summary Report 기준 Webflux가 Avg는 낮고, Throughput은 더 높음&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;style2&quot;&gt;✅&lt;b&gt;RDB 조회 및 Caching 활용 &lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;779&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D0Nbq/btsL3j1eLho/oPhj9PKFcRau8ZgyXPkmL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D0Nbq/btsL3j1eLho/oPhj9PKFcRau8ZgyXPkmL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D0Nbq/btsL3j1eLho/oPhj9PKFcRau8ZgyXPkmL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD0Nbq%2FbtsL3j1eLho%2FoPhj9PKFcRau8ZgyXPkmL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;898&quot; height=&quot;779&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;779&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Caching 활용 시, Summary Report 기준 Avg, Throughput 모두 비슷&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⭐캐시 처리 시 WebFlux의 이점이 미미한 이유:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Redis 자체가 매우 빠름&lt;/b&gt; &amp;rarr; 논블로킹 I/O의 효과가 뚜렷하게 나타나기 위해선 &lt;b&gt;대기 시간이 길어야 함&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 단순 RDB 조회 시, MySQL은 두뇌 풀 가동 모습,, &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WiOgq/btsL4XWFmdQ/kKMj7Ruqs8HFLOUH7hoCH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WiOgq/btsL4XWFmdQ/kKMj7Ruqs8HFLOUH7hoCH1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;262&quot; data-filename=&quot;mvc-with-rdb.png&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WiOgq/btsL4XWFmdQ/kKMj7Ruqs8HFLOUH7hoCH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWiOgq%2FbtsL4XWFmdQ%2FkKMj7Ruqs8HFLOUH7hoCH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mL47T/btsL4v7pTLx/2J9GY5NEMv1SjNLvMK9NqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mL47T/btsL4v7pTLx/2J9GY5NEMv1SjNLvMK9NqK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;262&quot; data-filename=&quot;webflux-with-rdb.png&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mL47T/btsL4v7pTLx/2J9GY5NEMv1SjNLvMK9NqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmL47T%2FbtsL4v7pTLx%2F2J9GY5NEMv1SjNLvMK9NqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer-jinnie.tistory.com/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer-jinnie.tistory.com/105&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738469929126&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;[프로젝트] Windows 환경에서 JMeter 설치 및 부하 테스트 하기&quot; data-og-description=&quot;부하 테스트를 위해 JMeter 설치가 필요했던 나 ,, 윈도우에서 JMeter 설치를 비롯해 테스트를 해야 하는 분들이 이 포스팅 하나만 읽어도 쉽게 가능했으면 하는 마음으로 포스팅한다.&amp;nbsp;설치1. JMeter&quot; data-og-host=&quot;developer-jinnie.tistory.com&quot; data-og-source-url=&quot;https://developer-jinnie.tistory.com/105&quot; data-og-url=&quot;https://developer-jinnie.tistory.com/105&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TtYMI/hyYclwLHfY/8SZq5p3j2k8ktYm0BKuEp0/img.png?width=800&amp;amp;height=553&amp;amp;face=0_0_800_553,https://scrap.kakaocdn.net/dn/bfhNxU/hyYb7FhUm4/44NcPKkkc2wmHMFrFSBkdk/img.png?width=800&amp;amp;height=553&amp;amp;face=0_0_800_553,https://scrap.kakaocdn.net/dn/bCyoTY/hyX74co4JZ/cP0RUtl1ZKB646XRLNPKB0/img.png?width=2032&amp;amp;height=1271&amp;amp;face=0_0_2032_1271&quot;&gt;&lt;a href=&quot;https://developer-jinnie.tistory.com/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer-jinnie.tistory.com/105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TtYMI/hyYclwLHfY/8SZq5p3j2k8ktYm0BKuEp0/img.png?width=800&amp;amp;height=553&amp;amp;face=0_0_800_553,https://scrap.kakaocdn.net/dn/bfhNxU/hyYb7FhUm4/44NcPKkkc2wmHMFrFSBkdk/img.png?width=800&amp;amp;height=553&amp;amp;face=0_0_800_553,https://scrap.kakaocdn.net/dn/bCyoTY/hyX74co4JZ/cP0RUtl1ZKB646XRLNPKB0/img.png?width=2032&amp;amp;height=1271&amp;amp;face=0_0_2032_1271');&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;[프로젝트] Windows 환경에서 JMeter 설치 및 부하 테스트 하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;부하 테스트를 위해 JMeter 설치가 필요했던 나 ,, 윈도우에서 JMeter 설치를 비롯해 테스트를 해야 하는 분들이 이 포스팅 하나만 읽어도 쉽게 가능했으면 하는 마음으로 포스팅한다.&amp;nbsp;설치1. JMeter&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer-jinnie.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jmeter.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jmeter.apache.org/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738469954341&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Apache JMeter
          -
          Apache JMeter&amp;trade;&quot; data-og-description=&quot;Apache JMeter&amp;trade; The Apache JMeter&amp;trade; application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance. It was originally designed for testing Web Applications but has since expanded to oth&quot; data-og-host=&quot;jmeter.apache.org&quot; data-og-source-url=&quot;https://jmeter.apache.org/&quot; data-og-url=&quot;https://jmeter.apache.org/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://jmeter.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jmeter.apache.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Apache JMeter - Apache JMeter&amp;trade;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Apache JMeter&amp;trade; The Apache JMeter&amp;trade; application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance. It was originally designed for testing Web Applications but has since expanded to oth&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jmeter.apache.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jaehoney.tistory.com/224&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jaehoney.tistory.com/224&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738470174611&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;Apache JMeter란 무엇인가? (+ 사용 방법 with 성능 및 부하 테스트)&quot; data-og-description=&quot;Apache JMeter Apache JMeter는 서버가 제공하는 성능 및 부하를 측정할 수 있는 테스트 도구이다. JMeter는 순수 Java 애플리케이션 오픈소스이며 서버나 네트워크 또는 개체에 대해 과부하를 시뮬레이션&quot; data-og-host=&quot;jaehoney.tistory.com&quot; data-og-source-url=&quot;https://jaehoney.tistory.com/224&quot; data-og-url=&quot;https://jaehoney.tistory.com/224&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WkpIa/hyX7RxhQVs/dKVY25MMdoyocnxivCfIE1/img.png?width=318&amp;amp;height=159&amp;amp;face=0_0_318_159,https://scrap.kakaocdn.net/dn/WyBiG/hyX7ZhQ9tf/fhYi7dVWqxNekgU7twgKyK/img.png?width=318&amp;amp;height=159&amp;amp;face=0_0_318_159,https://scrap.kakaocdn.net/dn/cfzc7z/hyYckkkew7/aXBQd3MKJOPJTK2SkSUB5k/img.png?width=2054&amp;amp;height=974&amp;amp;face=0_0_2054_974&quot;&gt;&lt;a href=&quot;https://jaehoney.tistory.com/224&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jaehoney.tistory.com/224&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WkpIa/hyX7RxhQVs/dKVY25MMdoyocnxivCfIE1/img.png?width=318&amp;amp;height=159&amp;amp;face=0_0_318_159,https://scrap.kakaocdn.net/dn/WyBiG/hyX7ZhQ9tf/fhYi7dVWqxNekgU7twgKyK/img.png?width=318&amp;amp;height=159&amp;amp;face=0_0_318_159,https://scrap.kakaocdn.net/dn/cfzc7z/hyYckkkew7/aXBQd3MKJOPJTK2SkSUB5k/img.png?width=2054&amp;amp;height=974&amp;amp;face=0_0_2054_974');&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;Apache JMeter란 무엇인가? (+ 사용 방법 with 성능 및 부하 테스트)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Apache JMeter Apache JMeter는 서버가 제공하는 성능 및 부하를 측정할 수 있는 테스트 도구이다. JMeter는 순수 Java 애플리케이션 오픈소스이며 서버나 네트워크 또는 개체에 대해 과부하를 시뮬레이션&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jaehoney.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.naver.com/ka28/222492575671&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/ka28/222492575671&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738470262597&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;[JMETER] 부하 테스트로 TPS 측정하기(JMeter 플러그인 사용)&quot; data-og-description=&quot;JMeter 플러그인 다운 및 적용하기 아래 하단의 사이트를 들어간다. https://jmeter-plugins.org/wiki/T...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://blog.naver.com/ka28/222492575671&quot; data-og-url=&quot;https://blog.naver.com/ka28/222492575671&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/srIlJ/hyX73ExFP5/qOFndYP8nqZjq0Q2OzDpW0/img.png?width=743&amp;amp;height=627&amp;amp;face=0_0_743_627&quot;&gt;&lt;a href=&quot;https://blog.naver.com/ka28/222492575671&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.naver.com/ka28/222492575671&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/srIlJ/hyX73ExFP5/qOFndYP8nqZjq0Q2OzDpW0/img.png?width=743&amp;amp;height=627&amp;amp;face=0_0_743_627');&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;[JMETER] 부하 테스트로 TPS 측정하기(JMeter 플러그인 사용)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JMeter 플러그인 다운 및 적용하기 아래 하단의 사이트를 들어간다. https://jmeter-plugins.org/wiki/T...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/985</guid>
      <comments>https://hj0216.tistory.com/985#entry985comment</comments>
      <pubDate>Sun, 2 Feb 2025 14:46:23 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] R2DBC와 Custom Query</title>
      <link>https://hj0216.tistory.com/984</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/alone-zanmang-loopy&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zSU7u/btsL2PyJXLn/YauQce3N40BE16z2U6wagk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzSU7u%2FbtsL2PyJXLn%2FYauQce3N40BE16z2U6wagk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;200&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글을 쓰기 전에 항상 고민합니다,, 오늘은 어떤 잔망루피를 데려올까,, &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 R2DBC에서 쿼리를 작성하는 2가지 방법을 작성할 예정이기에 잔망루피 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;잔망 루피 2명,, (+ 뒷모습의 잔망루피는 덤,,)과 함께하는 Custom Query를 정리해보고자 합니다,,&lt;/p&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. gradle.build&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 라이브러리를 쓰기 위해 종속성을 추가해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1738316055938&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies{
  	// r2dbc 설정
	implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc' // JDBC 대신 비동기 방식으로 DB와 통신할 수 있도록 지원
	implementation 'io.asyncer:r2dbc-mysql:1.0.2' // R2DBC를 이용해 MySQL과 연결하기 위한 드라이버
	// 비동기/리액티브 방식으로 MySQL과 데이터를 주고받을 수 있도록 하는 설정
	// 리액티브(Reactive) 방식: 비동기(Asynchronous) + 논블로킹(Non-blocking) 프로그래밍 모델
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. application.yml&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: MySQL &lt;/p&gt;
&lt;pre id=&quot;code_1738316238514&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  r2dbc:
    url: r2dbc:mysql://localhost:3307/database_name
    username: username
    password: passworad&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;3. R2dbcConfig.java&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2DBC 설정 파일을 만들어줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1738316613771&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@RequiredArgsConstructor
@Slf4j
@EnableR2dbcRepositories // 리액티브 리포지토리(ReactiveCrudRepository 등)를 자동으로 스캔하고, R2DBC를 통해 사용할 수 있도록 설정
@EnableR2dbcAuditing // R2DBC 엔터티의 자동 감사(Auditing) 기능을 활성화, createdAt, updatedAt 같은 값을 자동으로 입력
public class R2dbcConfig {
  private final DatabaseClient databaseClient;
  // MySQL 연결 시, 객체 생성은 하지만 Connection까지는 보장하지 않음
  // application.properties 파일에서 비밀번호 틀려도 Spring 연결 O
 }&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;4-1. Custom Repository 만들기 - @Query 버전&lt;/p&gt;
&lt;pre id=&quot;code_1738316967516&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface UserR2dbcRepository extends ReactiveCrudRepository&amp;lt;User, Long&amp;gt; {

  @Modifying
  // @Query: 읽기 전용(SELECT 쿼리)으로 동작
  // CREATE, UPDATE, DELETE 같은 데이터를 변경하는 쿼리를 실행하려면 @Modifying을 추가
  @Query(&quot;DELETE FROM users WHERE name = :name&quot;)
  Mono&amp;lt;Integer&amp;gt; deleteByName(String name);
}&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;4-2. Custom Repository 만들기 - DatabaseClient&lt;/p&gt;
&lt;pre id=&quot;code_1738317066078&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
@RequiredArgsConstructor
public class PostR2dbcCustomRepositoryImpl implements PostR2dbcCustomRepository {
  private final DatabaseClient databaseClient;

  @Override
  public Flux&amp;lt;Post&amp;gt; findAllByUserId(Long userId) {
    String sql = &quot;&quot;&quot;
                SELECT p.id as pid, p.user_id as userId, p.title, p.content, p.created_at as pcreatedAt, p.updated_at as pupdatedAt,
                       u.id as uid, u.name, u.email, u.created_at as ucreatedAt, u.updated_at as uupdatedAt
                FROM posts p
                LEFT JOIN users u ON p.user_id = u.id
                WHERE p.user_id = :userId
        &quot;&quot;&quot;;

    return databaseClient.sql(sql)
                         .bind(&quot;userId&quot;, userId)
                         // :userId에 매핑 -&amp;gt; 오류나는 부분 IntelliJ 문제
                         .fetch()
                         // fetch().all()을 쓰면 Flux, fetch().one()을 쓰면 Mono를 반환
                         .all()
                         .map(row -&amp;gt; Post.builder()
                                         .id((Long) row.get(&quot;pid&quot;))
                                         .userId((Long) row.get(&quot;userId&quot;))
                                         .title((String) row.get(&quot;title&quot;))
                                         .content((String) row.get(&quot;content&quot;))
                                         .user(User.builder()
                                                   .id((Long) row.get(&quot;uid&quot;))
                                                   .name((String) row.get(&quot;name&quot;))
                                                   .email((String) row.get(&quot;email&quot;))
                                                   .createdAt(((ZonedDateTime) row.get(&quot;ucreatedAt&quot;)).toLocalDateTime())
                                                   .updatedAt(((ZonedDateTime) row.get(&quot;uupdatedAt&quot;)).toLocalDateTime())
                                                   .build())
                                         .createdAt(((ZonedDateTime) row.get(&quot;pcreatedAt&quot;)).toLocalDateTime())
                                         .updatedAt(((ZonedDateTime) row.get(&quot;pupdatedAt&quot;)).toLocalDateTime())
                                         .build());
  }
}&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. Custom Repository별 장단점 정리&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.124%;&quot;&gt;비교&amp;nbsp;항목&lt;/td&gt;
&lt;td style=&quot;width: 44.031%;&quot;&gt;ReactiveCrudRepository&lt;/td&gt;
&lt;td style=&quot;width: 44.8449%;&quot;&gt;DatabaseClient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.124%;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 44.031%;&quot;&gt;ReactiveCrudRepository 기본 메서드&lt;br /&gt;(save, findById, deleteById 등) 사용 가능&lt;/td&gt;
&lt;td style=&quot;width: 44.8449%;&quot;&gt;SQL 직접 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.124%;&quot;&gt;유연성&lt;/td&gt;
&lt;td style=&quot;width: 44.031%;&quot;&gt;@Query로 커스텀 쿼리 가능&lt;br /&gt;복잡한 JOIN 처리 어려움&lt;/td&gt;
&lt;td style=&quot;width: 44.8449%;&quot;&gt;복잡한&amp;nbsp;JOIN&amp;nbsp;및&amp;nbsp;다양한&amp;nbsp;SQL&amp;nbsp;작성&amp;nbsp;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.124%;&quot;&gt;활용&lt;/td&gt;
&lt;td style=&quot;width: 44.031%;&quot;&gt;기본적인&amp;nbsp;CRUD&amp;nbsp;및&amp;nbsp;간단한&amp;nbsp;조건&amp;nbsp;검색에&amp;nbsp;적합&lt;/td&gt;
&lt;td style=&quot;width: 44.8449%;&quot;&gt;직접 최적화가 가능하므로&lt;br /&gt;복잡한 비즈니스 로직이 필요한 경우 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.124%;&quot;&gt;동적 쿼리&lt;/td&gt;
&lt;td style=&quot;width: 44.031%;&quot;&gt;불가능(동적 쿼리 작성 시, Querydsl 필요)&lt;/td&gt;
&lt;td style=&quot;width: 44.8449%;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://fastcampus.co.kr/dev_online_traffic_data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738316633356&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&quot; data-og-description=&quot;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사 &quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eqMx59/hyX7TVN6bR/rluCYXiFDwRNqJUMezxTm0/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/cGhPyy/hyX7XcPwdy/m1eOnK5LG5s3vdc0X3OpfK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/MwiNY/hyX7T9kKHL/Hext7XXkKVsCME6mY3rtak/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eqMx59/hyX7TVN6bR/rluCYXiFDwRNqJUMezxTm0/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/cGhPyy/hyX7XcPwdy/m1eOnK5LG5s3vdc0X3OpfK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/MwiNY/hyX7T9kKHL/Hext7XXkKVsCME6mY3rtak/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520');&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;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/984</guid>
      <comments>https://hj0216.tistory.com/984#entry984comment</comments>
      <pubDate>Fri, 31 Jan 2025 19:10:35 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Redis와 Replication</title>
      <link>https://hj0216.tistory.com/983</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpULHN/btsL18SbOCC/9tXOP94mbMXhgqOIszbS11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpULHN/btsL18SbOCC/9tXOP94mbMXhgqOIszbS11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpULHN/btsL18SbOCC/9tXOP94mbMXhgqOIszbS11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpULHN%2FbtsL18SbOCC%2F9tXOP94mbMXhgqOIszbS11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;살면서 도플갱어를 만난다면 죽는다는 무서운 말이 있지만,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis는 도플갱어를 만나도 죽지 않습니다,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나와 동일한 또 다른 나,,?&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;replication의 개념과 docker-compose를 이용한 replica 생성 방법까지 간단히 정리해 보고자 합니다.&lt;/p&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. Replication의 정의&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;At the base of Redis replication (excluding the high availability features provided as an additional layer by Redis Cluster or Redis Sentinel) there is a leader follower&amp;nbsp;(master-replica) replication that is simple to use and configure. &lt;br /&gt;It allows replica Redis instances to be exact copies of master instances. &lt;br /&gt;The replica will automatically reconnect to the master every time the link breaks, and will attempt to be an exact copy of it regardless&amp;nbsp;of what happens to the master.&lt;br /&gt;Redis 복제의 기본(Redis Cluster 또는 Redis Sentinel에서 추가 계층으로 제공하는 고가용성 기능 제외)에는 사용 및 구성이 간단한 리더 팔로워(마스터-복제본) 복제가 있습니다. &lt;br /&gt;이를 통해 복제 Redis 인스턴스는 마스터 인스턴스의 정확한 복사본이 될 수 있습니다. &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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 왜 Replication이 필요한가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;안정성&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Replication을 사용하면 마스터 서버에서 발생한 데이터 변경이 슬레이브 서버로 자동으로 복제됩니다. 이는 데이터 손실을 방지하고, 마스터 서버가 장애를 겪었을 때 슬레이브 서버를 통해 데이터가 유지될 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;가용성&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis의 Replication을 통해 여러 서버에 데이터를 복제함으로써, 마스터 서버가 다운되더라도 슬레이브 서버를 통해 읽기 작업을 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt; &lt;b&gt;읽기 부하 분산&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마스터 서버에 대한 읽기 요청을 슬레이브 서버로 분산시킬 수 있습니다. 여러 개의 슬레이브 서버를 운영하여 읽기 부하를 분산시킴으로써 성능을 개선할 수 있습니다. 이를 통해 대규모 트래픽을 처리할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. Dokcer-Compose 파일 준비!&lt;/p&gt;
&lt;pre id=&quot;code_1738042180044&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;networks: #네트워크 설정으로, 서비스 간 통신을 가능하게 함
  replica: #네트워크 이름
    driver: bridge #네트워크 드라이버로 bridge를 사용, 같은 호스트 내에서 여러 컨테이너가 서로 통신할 수 있도록 설정

services: #services 아래에 실행할 컨테이너를 정의
  redis: #service 이름
    container_name: redis #container 이름
    image: redis:6.2 #docker hub 내 이미지(repository:verson_tag)
    ports: #port 매핑(호스트 6379 포트:컨테이너 6379 포트)
      - 6379:6379
    networks: 
      - replica 
      #연결할 네트워크
      #docker-compose.yml에서 networks를 정의하고, 각 컨테이너가 이를 사용하도록 설정하면 같은 네트워크를 공유하는 컨테이너끼리 이름(container_name)을 통해 접근할 수 있음
      #예: redis://redis:6379
    restart: always #컨테이너 중단 시, 동작 설정

  imreplica:
    container_name: imreplica
    image: redis:6.2
    ports:
      - 6378:6379
      #동일한 호스트에서 여러 개의 Redis 인스턴스를 실행하려면 각 인스턴스가 사용하는 포트를 다르게 설정해야 함
    networks:
      - replica
    volumes:
    #호스트(내 컴퓨터)와 Docker 컨테이너의 특정 폴더를 연결하는 설정
    #컨테이너 내부에서 생성된 파일이 호스트에 저장되거나, 호스트의 파일이 컨테이너에서 사용될 수 있게 됨
    #데이터를 영구적으로 저장하거나 공유하기 위해 사용
    #volumes를 통해 데이터를 외부(호스트)에 저장하면 컨테이너를 삭제해도 데이터가 보존
      - ./conf:/usr/local/etc/redis/
    command: redis-server /usr/local/etc/redis/redis.conf
    #컨테이너가 실행될 때 수행할 명령어
    #redis-server: Redis Server 실행
    #Redis 서버는 기본적으로 설정 파일인 redis.conf를 통해 설정을 읽고 실행
    restart: always

  imduplica:
    container_name: imduplica
    image: redis:6.2
    ports:
      - 6377:6379
    networks:
      - replica
    volumes:
      - ./conf:/usr/local/etc/redis/
    command: redis-server /usr/local/etc/redis/redis.conf
    restart: always&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;imreplica,,  imduplica,, &lt;/p&gt;
&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;style2&quot;&gt;Docker 컨테이너에 호스트 포트를 연결하는 이유는 외부 시스템(클라이언트나 다른 서버 등)이 컨테이너 내부의 서비스에 접근할 수 있게 만들기 위함입니다. &lt;br /&gt;Docker는 컨테이너화된 애플리케이션을 실행하는 환경으로, 기본적으로 컨테이너는 호스트 시스템과 격리된 네트워크를 사용합니다. &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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Replication을 위한 conf 파일 준비!&lt;/p&gt;
&lt;pre id=&quot;code_1738042124751&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;replicaof redis 6379
#Redis의 복제(replication) 기능을 설정
#replicaof: 현재 Redis 인스턴스를 복제본(replica)으로 설정하는데 사용
#지정된 마스터 Redis 서버에서 데이터를 복제하도록 설정
#redis: 복제할 마스터 Redis 서버의 서비스 이름(호스트명 또는 IP 주소)
#6379: 마스터 Redis 서버가 사용하는 포트 번호&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://hj0216.tistory.com/982&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[대기열&amp;nbsp;시스템]&amp;nbsp;Docker&amp;nbsp;Compose로&amp;nbsp;Grafana와&amp;nbsp;Prometheus로&amp;nbsp;모니터링&amp;nbsp;시스템&amp;nbsp;구축하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때는 분명됐는데, 이번부터는 docker-compose.yml 파일에 version을 추가하면 오류는 아니고, 경고가 발생합니다,, &lt;/p&gt;
&lt;pre id=&quot;code_1738040821154&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;the attribute `version` is obsolete, 
it will be ignored, 
please remove it to avoid potential confusion&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;top-level에서 version을 지정하는 환경이 obsolete되었기에 이제는 쓰지 않길 원한다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;version을 제거하면 경고 문구 없이 잘 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;➕ Deprecated와 Obsolete&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deprecated &lt;br /&gt;- 여전히 작동하며 당분간 지원 대상이나 향후 지원이 중단될 예정&lt;br /&gt;Obsolete &lt;br /&gt;- 더 이상 지원되지 않거나 사용되지 않으며, 대체 기술이나 방법으로 완전히 전환된 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. Replication 테스트!&lt;/p&gt;
&lt;pre id=&quot;code_1738043262580&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 원본(Master) 인스턴스
docker-compose exec redis redis-cli

info replication
# Replication
# role:master, 현재 Redis 인스턴스의 역할
# connected_slaves:2, 현재 이 마스터 Redis 인스턴스와 연결된 슬레이브(복제본) 인스턴스의 수
# slave0:ip=172.18.0.3,port=6379,state=online,offset=941,lag=0
# offset: 마스터와 슬레이브 간 동기화 상태를 나타내는 데이터의 위치
# 마스터와 슬레이브의 오프셋이 같으면 동기화가 완료된 상태
# lag: 슬레이브가 마스터와 동기화할 때의 지연 시간
# slave1:ip=172.18.0.2,port=6379,state=online,offset=941,lag=0
# master_failover_state:no-failover
# 마스터가 장애가 발생했을 때 슬레이브 중 하나를 새로운 마스터로 승격시키는 과정

set key1 1
# 복제본에서도 실시간으로 데이터 확인 가능&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1738043281705&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 복제본(Slave, imreplica&amp;amp;imduplica) 인스턴스
docker-compose exec imreplica redis-cli
set key1 1
# READONLY You can't write against a read only replica.&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;  Master Redis 인스턴스 Stop 후, Start할 경우,&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;기존 마스터가 중단된 후, 슬레이브 중 하나가 failover로 인해 마스터로 승격&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;Master를 대리했던 인스턴스가 새로운 정보를 Write 했을 가능성이 존재&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_1738043786440&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Stop 전
# Replication
role:master
connected_slaves:2
slave0:ip=172.18.0.3,port=6379,state=online,offset=1000,lag=0
slave1:ip=172.18.0.2,port=6379,state=online,offset=1000,lag=0


# Stop 후, Start 시
# Replication
role:slave
master_host:172.18.0.3
master_port:6379
master_link_status:up
slave_repl_offset: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 style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://redis.io/docs/latest/operate/oss_and_stack/management/replication/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://redis.io/docs/latest/operate/oss_and_stack/management/replication/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738039999979&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;Redis replication&quot; data-og-description=&quot;How Redis supports high availability and failover with replication&quot; data-og-host=&quot;redis.io&quot; data-og-source-url=&quot;https://redis.io/docs/latest/operate/oss_and_stack/management/replication/&quot; data-og-url=&quot;https://redis.io/docs/latest/operate/oss_and_stack/management/replication/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://redis.io/docs/latest/operate/oss_and_stack/management/replication/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.io/docs/latest/operate/oss_and_stack/management/replication/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Redis replication&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How Redis supports high availability and failover with replication&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://teveloper.tistory.com/82&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://teveloper.tistory.com/82&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738040877322&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;docker-compose.yaml: &amp;#96;version&amp;#96; is obsolete 이슈&quot; data-og-description=&quot;이슈 발생사내 프로젝트에 docker-compose를 사용해 docker container환경을 구축하던 중 다음과 같은 경고 메세지가 발생했다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;docker-compose의 버전관련 문제인듯 하여 찾아보니 깃허브 이슈에서 다&quot; data-og-host=&quot;teveloper.tistory.com&quot; data-og-source-url=&quot;https://teveloper.tistory.com/82&quot; data-og-url=&quot;https://teveloper.tistory.com/82&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bPwUVx/hyX4zqvKuH/B3zHMsnZgtEUlkcBk6ljnK/img.jpg?width=800&amp;amp;height=383&amp;amp;face=0_0_800_383,https://scrap.kakaocdn.net/dn/Ec7p5/hyX7O0CaDB/2ya7QCRBbicZ8gVeGTY540/img.jpg?width=800&amp;amp;height=383&amp;amp;face=0_0_800_383,https://scrap.kakaocdn.net/dn/tkEfa/hyX4tX9yFm/EclrJDdix6WABUUybamQYk/img.jpg?width=750&amp;amp;height=359&amp;amp;face=0_0_750_359&quot;&gt;&lt;a href=&quot;https://teveloper.tistory.com/82&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://teveloper.tistory.com/82&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bPwUVx/hyX4zqvKuH/B3zHMsnZgtEUlkcBk6ljnK/img.jpg?width=800&amp;amp;height=383&amp;amp;face=0_0_800_383,https://scrap.kakaocdn.net/dn/Ec7p5/hyX7O0CaDB/2ya7QCRBbicZ8gVeGTY540/img.jpg?width=800&amp;amp;height=383&amp;amp;face=0_0_800_383,https://scrap.kakaocdn.net/dn/tkEfa/hyX4tX9yFm/EclrJDdix6WABUUybamQYk/img.jpg?width=750&amp;amp;height=359&amp;amp;face=0_0_750_359');&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;docker-compose.yaml: `version` is obsolete 이슈&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이슈 발생사내 프로젝트에 docker-compose를 사용해 docker container환경을 구축하던 중 다음과 같은 경고 메세지가 발생했다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;docker-compose의 버전관련 문제인듯 하여 찾아보니 깃허브 이슈에서 다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;teveloper.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://fastcampus.co.kr/dev_online_traffic_data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738042261637&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&quot; data-og-description=&quot;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사 &quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/chXVR9/hyX70Nx8Kd/VK2fdE1XpEuekYkt3b17F0/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/j163c/hyX74oQ8R6/zlKEJn1pAXp28skXyFTtDK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/bjl1SH/hyX4uvZc01/88XsSGLXKUylo23efEaAE0/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/chXVR9/hyX70Nx8Kd/VK2fdE1XpEuekYkt3b17F0/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/j163c/hyX74oQ8R6/zlKEJn1pAXp28skXyFTtDK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/bjl1SH/hyX4uvZc01/88XsSGLXKUylo23efEaAE0/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520');&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;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/983</guid>
      <comments>https://hj0216.tistory.com/983#entry983comment</comments>
      <pubDate>Tue, 28 Jan 2025 14:39:23 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Docker Compose로 Grafana와 Prometheus로 모니터링 시스템 구축하기</title>
      <link>https://hj0216.tistory.com/982</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA7YhI/btsL0XX4iwc/snxk1hz0JOV5QyjBIX3xiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA7YhI/btsL0XX4iwc/snxk1hz0JOV5QyjBIX3xiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA7YhI/btsL0XX4iwc/snxk1hz0JOV5QyjBIX3xiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA7YhI%2FbtsL0XX4iwc%2Fsnxk1hz0JOV5QyjBIX3xiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;315&quot; height=&quot;232&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 강의를 들으면서 모르는 단어의 연속이었지만.. 암 오케..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옆에 맛있는 빵을 두고 먹으면서 이겨내 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이겨낸 김에 적어보는 Grafana와 Prometheus로 Redis-exporter 모니터링 해보는 방법..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. docker 대신 docker-compose 사용 준비&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Docker&lt;br /&gt;단일 컨테이너를 관리하는 도구로, 애플리케이션과 그 애플리케이션이 실행되는 데 필요한 모든 것을 컨테이너라는 단위로 패키징하고 실행&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;Docker Compose&lt;br /&gt;여러 컨테이너를 한 번에 관리할 수 있는 도구로, 애플리케이션이 여러 컨테이너로 구성된 경우(예: 웹 서버, 데이터베이스, 캐시), 이를 하나의 설정 파일(docker-compose.yml)로 정의하고 실행&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;Redis, Redis Exporter, Prometheus, Grafana로 구성된 총 4개의 컨테이너를 관리할 예정이므로 Docker Compose를 사용할 예정입니다.&lt;/p&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 Desktop을 설치했을 경우, Docker 및 Docker Compose 모두 설치되므로 따로 설치 작업은 필요 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. docker-compose 설정 파일 준비&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;docker-compose.yaml&lt;br /&gt;여러 개의 Docker 컨테이너를 정의하고 관리하기 위해 사용되는 설정 파일&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⭐ 학습 목표: Redis 데이터를 기반으로 Prometheus가 모니터링 데이터를 수집하고, Grafana가 이를 시각화  ⭐&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전체 데이터 흐름&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Redis&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;Redis Exporter&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis의 상태 정보를 수집하여 Prometheus가 이해할 수 있는 형식으로 변환&lt;/li&gt;
&lt;li&gt;Redis와 연결(여기선 REDIS_ADDR=redis://redis:6379)하고 메트릭 데이터를 Prometheus로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Prometheus&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis Exporter가 제공한 메트릭 데이터를 주기적으로 수집하고 저장&lt;/li&gt;
&lt;li&gt;데이터베이스 역할을 하며, 요청 시 데이터를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Grafana&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Prometheus에 저장된 데이터를 가져와 사용자 정의 대시보드를 통해 시각화&lt;/li&gt;
&lt;li&gt;사용자는 웹 인터페이스로 그래프, 차트 등을 확인할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1737795573178&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3.8' #Docker Compose 파일 버전
networks: #네트워크 설정으로, 서비스 간 통신을 가능하게 함
  monitor:
    driver: bridge #네트워크 드라이버로 bridge를 사용

services: #services 아래에 실행할 컨테이너를 정의
  redis:
    container_name: redis #container 이름 지정
    image: redis:6.2 #Image 지정
    ports: #port 매핑
      - 6379:6379
    networks:
      - monitor
      #연결할 네트워크
      #Docker 네트워크는 컨테이너 간의 통신을 가능하게 하는 가상의 네트워크
      #docker-compose.yml에서 networks를 정의하고, 각 컨테이너가 이를 사용하도록 설정하면 같은 네트워크를 공유하는 컨테이너끼리 이름(container_name)을 통해 접근할 수 있음
      #예: redis://redis:6379
    restart: #컨테이너 중단 시, 동작 설정
      always

  prometheus: #모니터링 및 경보 시스템
    image: prom/prometheus:latest
    user: root
    volumes: 
    #호스트(내 컴퓨터)와 Docker 컨테이너의 특정 폴더를 연결하는 설정
    #컨테이너 내부에서 생성된 파일이 호스트에 저장되거나, 호스트의 파일이 컨테이너에서 사용될 수 있게 됨
    #데이터를 영구적으로 저장하거나 공유하기 위해 사용
    #volumes를 통해 데이터를 외부(호스트)에 저장하면 컨테이너를 삭제해도 데이터가 보존
      - ./prometheus/config:/etc/prometheus 
      #호스트의 ./prometheus/config에 있는 설정 파일이 컨테이너 내부에서 사용
      #컨테이너 내부에서 설정을 수정하면 그 내용이 호스트의 ./prometheus/config에 그대로 저장
      - ./prometheus/data:/prometheus
      #호스트의 폴더 ./prometheus/data &amp;rarr; 컨테이너 내부의 폴더 /prometheus와 연결
      #Prometheus가 수집한 데이터가 컨테이너 내부의 /prometheus에 저장되는데, 이 데이터가 호스트의 ./prometheus/data에 동기화
      #컨테이너를 삭제해도 데이터는 호스트의 ./prometheus/data 폴더에 남아 있음
    #요약: Prometheus의 설정 파일과 데이터를 호스트의 특정 폴더에 저장하고 컨테이너가 이를 읽고 쓰도록 연결하는 작업
    ports:
      - 9090:9090
    networks:
      - monitor
    restart: always


  grafana: #Prometheus의 데이터를 시각화해주는 툴
    container_name: grafana
    image: grafana/grafana:latest
    environment:
      - GF_SECURITY_ADMIN_USER=admin #관리자 ID를 admin으로 설정
      - GF_SECURITY_ADMIN_PASSWORD=password #비밀번호를 password로 지정
      - GF_USERS_ALLOW_SIGN_UP=false #사용자가 직접 가입하지 못하도록 설정
    volumes:
      - ./grafana/data:/var/lib/grafana #Grafana 데이터 저장소
      - ./grafana/provisioning:/etc/grafana/provisioning #Grafana의 초기 설정 파일들이 들어 있는 디렉터리
    ports:
      - 3000:3000
    depends_on: #Prometheus가 먼저 실행되어야 함
      - prometheus
    networks:
      - monitor
    restart: always

  redis-exporter: #Redis 데이터를 Prometheus에 전송해주는 툴
    container_name: redis-exporter
    image: oliver006/redis_exporter:latest
    environment:
      - REDIS_ADDR=redis://redis:6379 #Redis 컨테이너와 연결
      #redis:// Redis 프로토콜(통신 방식)
      #redis Redis 컨테이너의 이름 (DNS처럼 사용)
    ports:
      - 9121:9121
    depends_on:
      - prometheus #Prometheus가 먼저 실행되어야 함
    networks:
      - monitor
    restart: always&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;3. 의존성의 기반에 되는 prometheus 설정 파일 만들기&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;prometheus.yml&lt;br /&gt;Prometheus가 어디에서 데이터를 수집할지(스크래핑)&amp;nbsp;설정하는 파일&lt;/blockquote&gt;
&lt;pre id=&quot;code_1737796297398&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;global:
  scrape_interval: 1m 
  #모든 스크래핑 기본 간격 설정
  #기본적으로 1분마다(targets에서 지정한 서버에서) 데이터를 수집

scrape_configs: #스크래핑 대상 정의
  - job_name: 'prometheus'
    scrape_interval: 1m
    # scrape_configs.scrape_interval이 global.scrape_interval보다 우선 적용
    static_configs: #고정된 타겟 리스트
      - targets: ['localhost:9090'] #Prometheus 자체를 스크래핑

  - job_name: 'redis-exporter'
    scrape_interval: 5s
    static_configs:
      - targets: ['redis-exporter:9121'] #Redis Exporter에서 메트릭 수집&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;4. 가자, Docker-compose!&lt;/p&gt;
&lt;pre id=&quot;code_1737872081272&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker-compose -f -d docker-compose.yml up
# Docker Compose는 기본적으로 커맨드가 실행하는 디렉토리에 있는 docker-compose.yml 또는 docker-compose.yaml을 설정 파일로 사용
# -f: 다른 이름이나 경로의 파일을 Docker Compose 설정 파일로 사용
#   : 여러 개의 설정 파일 사용
# up: Docker Compose에 정의되어 있는 모든 서비스 컨테이너를 한 번에 생성하고 실행하기 위해서 사용
# -d: 백그라운드에서 컨테이너 실행

docker-compose start redis
# 내려가 있는 있는 특정 서비스 컨테이너를 올리기 위해서 사용
# 서비스에 대한 기존 컨테이너를 시작
# docker-compose.yml 파일에서 서비스 이름을 기준으로 사용

docker-compose ps
# Docker Compose에 정의되어 있는 모든 서비스 컨테이너 목록을 조회할 때 사용&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. 실행 화면&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Redis-exporter(127.0.0.1:9121)&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mSqlc/btsL2f30PaW/cUmIXD3u1WYJdv9optSaxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mSqlc/btsL2f30PaW/cUmIXD3u1WYJdv9optSaxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mSqlc/btsL2f30PaW/cUmIXD3u1WYJdv9optSaxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmSqlc%2FbtsL2f30PaW%2FcUmIXD3u1WYJdv9optSaxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1158&quot; height=&quot;126&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;126&quot;/&gt;&lt;/span&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;Prometheus(127.0.0.1:9090)&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKxORD/btsL1t3fDaS/MqtK14V4tTuM3pAMH3QCX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKxORD/btsL1t3fDaS/MqtK14V4tTuM3pAMH3QCX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKxORD/btsL1t3fDaS/MqtK14V4tTuM3pAMH3QCX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKxORD%2FbtsL1t3fDaS%2FMqtK14V4tTuM3pAMH3QCX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1919&quot; height=&quot;536&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&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;Grafana(127.0.0.1:3000)&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;(3000 포트... 이제 React만의 것이 아닙니다.. )&lt;/i&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1503&quot; data-origin-height=&quot;785&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXcKr6/btsL0Wx8KFy/a6jii0kKtGxO0JSMZDBk5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXcKr6/btsL0Wx8KFy/a6jii0kKtGxO0JSMZDBk5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXcKr6/btsL0Wx8KFy/a6jii0kKtGxO0JSMZDBk5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXcKr6%2FbtsL0Wx8KFy%2Fa6jii0kKtGxO0JSMZDBk5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1503&quot; height=&quot;785&quot; data-origin-width=&quot;1503&quot; data-origin-height=&quot;785&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ Grafana에 Prometheus 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚙️설정 &amp;rarr; Data Sources&amp;nbsp; &amp;rarr; Add new data source: Prometheus 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;  url 입력 시, localhost가 아닌 localhost가 아니라 &lt;b&gt;서비스 이름&lt;/b&gt;(prometheus)을 사용해야함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; * Prometheus 컨테이너는 기본적으로 localhost:9090에서 동작하지만, 이는 &lt;b&gt;Prometheus 컨테이너 내부에서만 접근 가능한 주소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; * Grafana 컨테이너는 네트워크 상에서 Prometheus 컨테이너에 접근해야 하며, 이 경우 localhost가 아니라 &lt;b&gt;서비스 이름&lt;/b&gt;(prometheus)을 사용해야 함(Docker Compose는 서비스 이름을 DNS 이름으로 자동 해석)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ Grafana Redis Dashboard 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 해당 Dashboard Template의 &lt;u&gt;&lt;b&gt;Copy ID to clipboard&lt;/b&gt;&lt;/u&gt; 또는 &lt;u&gt;&lt;b&gt;Download JSON&lt;/b&gt;&lt;/u&gt; 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Dashboards &amp;rarr; Import &amp;rarr; &lt;b&gt;Import via grafana.com&lt;/b&gt; 또는 &lt;b&gt;Upload dashboard JSON file &amp;rarr;&amp;nbsp;&lt;/b&gt;Import 클릭&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;Grafana Dashboard Template&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://grafana.com/grafana/dashboards/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://grafana.com/grafana/dashboards/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737873455779&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Grafana dashboards | Grafana Labs&quot; data-og-description=&quot;No results found. Please clear one or more filters.&quot; data-og-host=&quot;grafana.com&quot; data-og-source-url=&quot;https://grafana.com/grafana/dashboards/&quot; data-og-url=&quot;https://grafana.com/grafana/dashboards/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gClhF/hyX4wf254s/c1kvFtjQrbijSzo1vvckP0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bwpbEd/hyX7SuXNzp/JBow4pwBVmaY6oCF2Fkxx0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://grafana.com/grafana/dashboards/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://grafana.com/grafana/dashboards/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gClhF/hyX4wf254s/c1kvFtjQrbijSzo1vvckP0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bwpbEd/hyX7SuXNzp/JBow4pwBVmaY6oCF2Fkxx0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;Grafana dashboards | Grafana Labs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;No results found. Please clear one or more filters.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;grafana.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis Exporter Dashboard Template&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://grafana.com/oss/prometheus/exporters/redis-exporter/?tab=dashboards&quot;&gt;https://grafana.com/oss/prometheus/exporters/redis-exporter/?tab=dashboards&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737873557676&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Prometheus OSS | Redis exporter&quot; data-og-description=&quot;Overview Installation Recording rules Dashboards Alerting rules Grafana Cloud Integration&quot; data-og-host=&quot;grafana.com&quot; data-og-source-url=&quot;https://grafana.com/oss/prometheus/exporters/redis-exporter/?tab=dashboards&quot; data-og-url=&quot;https://grafana.com/oss/prometheus/exporters/redis-exporter/?tab=dashboards&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gjWO2/hyX7RCOYlG/FVsRaHxwgbGT9YzbmdooX0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/r6NzQ/hyX7PLLlzK/0CCKgcUyl33FiMNtfMa6ZK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://grafana.com/oss/prometheus/exporters/redis-exporter/?tab=dashboards&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://grafana.com/oss/prometheus/exporters/redis-exporter/?tab=dashboards&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gjWO2/hyX7RCOYlG/FVsRaHxwgbGT9YzbmdooX0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/r6NzQ/hyX7PLLlzK/0CCKgcUyl33FiMNtfMa6ZK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Prometheus OSS | Redis exporter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Overview Installation Recording rules Dashboards Alerting rules Grafana Cloud Integration&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;grafana.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 안녕, Dokcer-Compose!&lt;/p&gt;
&lt;pre id=&quot;code_1737872446457&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker-compose down
# Docker Compose에 정의되어 있는 모든 서비스 컨테이너를 한 번에 정지시키고 삭제

docker-compose stop redis
# 돌아기고 있는 특정 서비스 컨테이너를 정지시키기 위해서 사용
# 컨테이너를 제거하지 않고 실행중인 컨테이너를 중지
# docker-compose.yaml 파일에서 서비스 이름을 기준으로 사용&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 style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://solo5star.tistory.com/19&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://solo5star.tistory.com/19&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737796173171&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;grafana와 prometheus로 모니터링 시스템 구축하기&quot; data-og-description=&quot;리뉴얼 된 블로그로 보기: https://solo5star.dev/posts/19/ 작업관리자는 윈도우 PC를 사용하는 사람들에겐 너무나도 친숙한 프로그램입니다. 렉이 걸린다면 CPU를 너무 많이 잡아먹고 있는건지, 메모리&quot; data-og-host=&quot;solo5star.tistory.com&quot; data-og-source-url=&quot;https://solo5star.tistory.com/19&quot; data-og-url=&quot;https://solo5star.tistory.com/19&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bck9c2/hyX4sdAjmT/qScY1mANd0Zs3NAqXABA4K/img.png?width=753&amp;amp;height=545&amp;amp;face=0_0_753_545,https://scrap.kakaocdn.net/dn/KPFQw/hyX4o92JZI/hqKj5F2e2qad3sBIS8up9K/img.png?width=753&amp;amp;height=545&amp;amp;face=0_0_753_545,https://scrap.kakaocdn.net/dn/TPEJq/hyX4ohRtOw/aVlFrqiFFpVGf5fF2YekZK/img.png?width=1270&amp;amp;height=720&amp;amp;face=0_0_1270_720&quot;&gt;&lt;a href=&quot;https://solo5star.tistory.com/19&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://solo5star.tistory.com/19&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bck9c2/hyX4sdAjmT/qScY1mANd0Zs3NAqXABA4K/img.png?width=753&amp;amp;height=545&amp;amp;face=0_0_753_545,https://scrap.kakaocdn.net/dn/KPFQw/hyX4o92JZI/hqKj5F2e2qad3sBIS8up9K/img.png?width=753&amp;amp;height=545&amp;amp;face=0_0_753_545,https://scrap.kakaocdn.net/dn/TPEJq/hyX4ohRtOw/aVlFrqiFFpVGf5fF2YekZK/img.png?width=1270&amp;amp;height=720&amp;amp;face=0_0_1270_720');&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;grafana와 prometheus로 모니터링 시스템 구축하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리뉴얼 된 블로그로 보기: https://solo5star.dev/posts/19/ 작업관리자는 윈도우 PC를 사용하는 사람들에겐 너무나도 친숙한 프로그램입니다. 렉이 걸린다면 CPU를 너무 많이 잡아먹고 있는건지, 메모리&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;solo5star.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/docker-compose/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.daleseo.com/docker-compose/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737872508664&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Docker Compose 커맨드 사용법&quot; data-og-description=&quot;Engineering Blog by Dale Seo&quot; data-og-host=&quot;www.daleseo.com&quot; data-og-source-url=&quot;https://www.daleseo.com/docker-compose/&quot; data-og-url=&quot;https://www.daleseo.com/docker-compose/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dIwa1p/hyX7V6ii4l/hP37jwZbbs5nKM37pkh9lK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/docker-compose/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.daleseo.com/docker-compose/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dIwa1p/hyX7V6ii4l/hP37jwZbbs5nKM37pkh9lK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Docker Compose 커맨드 사용법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Engineering Blog by Dale Seo&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.daleseo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@coastby/Grafana-%EC%84%A4%EC%B9%98-prometheus%EC%99%80-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@coastby/Grafana-%EC%84%A4%EC%B9%98-prometheus%EC%99%80-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737873360851&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;[Grafana] 설치, prometheus와 연동하기&quot; data-og-description=&quot;Grfana는 Grafana Labs가 개발한 오픈소스 데이터 시각화 플랫폼이다. 차트와 그래프로 대시보드를 구성하여 간편하게 데이터를 확인할 수 있다. 또한 기존의 서버 환경, 쿠버네티스 클러스터 또는 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@coastby/Grafana-%EC%84%A4%EC%B9%98-prometheus%EC%99%80-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@coastby/Grafana-설치-prometheus와-연동하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MjTKb/hyX4n4rbGG/RcRjW1zIE6Jekmf28sAFa1/img.png?width=1498&amp;amp;height=732&amp;amp;face=0_0_1498_732,https://scrap.kakaocdn.net/dn/edIRvL/hyX72qO3bp/VckEjWDq4EYKyqlXhkdW41/img.png?width=1498&amp;amp;height=732&amp;amp;face=0_0_1498_732,https://scrap.kakaocdn.net/dn/iyxIh/hyX7XiKlxZ/qLGzBMcdXqqyRRtcjMmkd0/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200&quot;&gt;&lt;a href=&quot;https://velog.io/@coastby/Grafana-%EC%84%A4%EC%B9%98-prometheus%EC%99%80-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@coastby/Grafana-%EC%84%A4%EC%B9%98-prometheus%EC%99%80-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MjTKb/hyX4n4rbGG/RcRjW1zIE6Jekmf28sAFa1/img.png?width=1498&amp;amp;height=732&amp;amp;face=0_0_1498_732,https://scrap.kakaocdn.net/dn/edIRvL/hyX72qO3bp/VckEjWDq4EYKyqlXhkdW41/img.png?width=1498&amp;amp;height=732&amp;amp;face=0_0_1498_732,https://scrap.kakaocdn.net/dn/iyxIh/hyX7XiKlxZ/qLGzBMcdXqqyRRtcjMmkd0/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200');&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;[Grafana] 설치, prometheus와 연동하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Grfana는 Grafana Labs가 개발한 오픈소스 데이터 시각화 플랫폼이다. 차트와 그래프로 대시보드를 구성하여 간편하게 데이터를 확인할 수 있다. 또한 기존의 서버 환경, 쿠버네티스 클러스터 또는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/982</guid>
      <comments>https://hj0216.tistory.com/982#entry982comment</comments>
      <pubDate>Sun, 26 Jan 2025 15:46:29 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] RedisTemplate, RedisHash, @Cacheable</title>
      <link>https://hj0216.tistory.com/981</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/zanmang-loopy-ver-3&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AR88Z/btsLWNmNQyv/AQZ1n6dL1ld2olEHipyIaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAR88Z%2FbtsLWNmNQyv%2FAQZ1n6dL1ld2olEHipyIaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache의, cache를 위한, cache에 의한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache의 3단 변신..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vegeta로 하염없는 Request를 보내보니 알겠더군요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;a href=&quot;https://hj0216.tistory.com/980&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[대기열&amp;nbsp;시스템]&amp;nbsp;HTTP&amp;nbsp;load&amp;nbsp;testing&amp;nbsp;tool,&amp;nbsp;Vegeta&amp;nbsp;설치(Window)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cache가 중요하다는 것을요..&lt;/p&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..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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.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;&lt;i&gt;&lt;u&gt;&lt;b&gt;1단 변신 준비. RedisConfig - userRedisTemplate&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis에&amp;nbsp;데이터를&amp;nbsp;저장하고&amp;nbsp;가져올&amp;nbsp;때,&amp;nbsp;User&amp;nbsp;객체를&amp;nbsp;쉽게&amp;nbsp;직렬화/역직렬화하여&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;RedisTemplate을&amp;nbsp;설정&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;RedisConfig.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1737767403934&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class RedisConfig {

  @Bean
  RedisTemplate&amp;lt;String, User&amp;gt; userRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
    // RedisConnectionFactory: Redis 서버와의 연결을 관리하는 클래스
    ObjectMapper objectMapper = new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .registerModule(new JavaTimeModule())
        .disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
    // ObjectMapper: JSON 데이터를 Java 객체로 변환하거나, Java 객체를 JSON 데이터로 변환
    // DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false: JSON에 Java 클래스에 정의되지 않은 필드가 있어도 에러를 발생시키지 않도록 설정
    // JavaTimeModule: java.time 패키지의 날짜/시간 클래스(LocalDate, LocalDateTime 등)를 처리할 수 있는 모듈을 추가
    // SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS
    // : 날짜를 타임스탬프가 아닌 ISO-8601 포맷(예: 2023-01-18T10:15:30)으로 직렬화하도록 설정

    // Object Mapper 설정 이유
    // Java 객체의 구조에 맞지 않는 JSON 필드가 있을 경우 에러가 발생할 수 있음
    // Java의 날짜/시간 클래스(LocalDate, LocalDateTime 등)는 기본적으로 직렬화/역직렬화가 제대로 지원되지 않음
    // 기본적으로 날짜와 시간을 타임스탬프 형식(예: 1674042600000, 밀리초 단위)으로 직렬화
    // 날짜를 사람이 읽을 수 있는 ISO 형식(예: 2025-01-18T15:30:00)으로 변환

    RedisTemplate&amp;lt;String, User&amp;gt; template = new RedisTemplate&amp;lt;&amp;gt;();
    template.setConnectionFactory(redisConnectionFactory);
    template.setKeySerializer(new StringRedisSerializer()); // 키를 문자열(String)로 직렬화
    template.setValueSerializer(new Jackson2JsonRedisSerializer&amp;lt;&amp;gt;(objectMapper, User.class)); // JSON 포맷으로 데이터를 저장

      return template;
  	}
  }&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;i&gt;&lt;u&gt;&lt;b&gt;1단 변신. RedisConfig - objectRedisTemplate&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RedisTemplate를&amp;nbsp;광범위하게&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;Value를&amp;nbsp;Object로&amp;nbsp;확장&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;RedisConfig.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1737768008785&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class RedisConfig {

  @Bean
  RedisTemplate&amp;lt;String, Object&amp;gt; objectRedisTemplate(RedisConnectionFactory connectionFactory) {
    PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
        .builder()
        .allowIfSubType(Object.class)
        .build();
    // PolymorphicTypeValidator: allowIfSubType을 통해 역직렬화 허용 클래스 타입을 정의
    
    ObjectMapper objectMapper = new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .registerModule(new JavaTimeModule())
        .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
        .disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
    // activateDefaultTyping: Jackson에서 직렬화/역직렬화 시 객체 타입 정보를 포함하도록 설정하는 메서드
    // DefaultTyping: 비최종 클래스(Non-final class)에 대해서만 타입 정보를 포함

    RedisTemplate&amp;lt;String, Object&amp;gt; template = new RedisTemplate&amp;lt;&amp;gt;();
    template.setConnectionFactory(connectionFactory);
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));
    // RedisTemplate를 광범위하게 사용할 수 있도록 Value를 Object로 확장
    // GenericJackson2JsonRedisSerializer
    // : JSON 데이터를 역직렬화할 때, 대상 객체의 타입 정보를 명시적으로 제공하지 않으면 기본적으로 Map 타입(즉, LinkedHashMap)으로 역직렬화

    return template;
  }
}&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;i&gt;&lt;u&gt;&lt;b&gt;2단 변신. RedisHashUser&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Redishash를 사용해 객체를 Redis의 Hash 데이터 구조로 저장&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;RedisHashUser.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1737769590213&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RedisHash(value = &quot;redishash-user&quot;, timeToLive = 30L)
// @RedisHash를 사용해 Redis에 저장할 객체를 정의
public class RedisHashUser {

  @Id
  private Long id;
  // HSET redishash-user:3 id 3
  private String name;
  // HSET redishash-user:3 name &quot;hi3&quot;
  @Indexed
  private String email;
  // HSET redishash-user:3 email &quot;hi3@email.com&quot;
  // &quot;SADD&quot; &quot;redishash-user:email:hi3@email.com&quot; 3
  private LocalDateTime createdAt;
  private LocalDateTime updatedAt;
}&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;style2&quot;&gt;RedisHashUserRepository.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1737770225364&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface RedisHashUserRepository extends CrudRepository&amp;lt;RedisHashUser, Long&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;JpaRepository는 Redis가 아닌 &lt;b&gt;RDBMS&lt;/b&gt;(예: MySQL, PostgreSQL 등)와 상호작용하기 위한 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Repository&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CrudRepository / RedisRepository를 상속하는 Repository 이용 필수&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 88px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.3721%;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;JpaRepository&lt;/td&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;CrudRepository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.3721%; height: 20px;&quot;&gt;저장소 대상&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%; height: 20px;&quot;&gt;관계형 데이터베이스 (RDBMS)&lt;/td&gt;
&lt;td style=&quot;width: 32.907%; height: 20px;&quot;&gt;Redis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 28.3721%; height: 17px;&quot;&gt;데이터 모델&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%; height: 17px;&quot;&gt;테이블과 레코드&lt;/td&gt;
&lt;td style=&quot;width: 32.907%; height: 17px;&quot;&gt;Key-Value 또는 Hash 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 28.3721%; height: 17px;&quot;&gt;내부 구현&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%; height: 17px;&quot;&gt;JPA(EntityManage, Hibernate 등) 기반&lt;/td&gt;
&lt;td style=&quot;width: 32.907%; height: 17px;&quot;&gt;RedisTemplate 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 28.3721%; height: 17px;&quot;&gt;동작 방식&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%; height: 17px;&quot;&gt;SQL 쿼리 생성 및 실행&lt;/td&gt;
&lt;td style=&quot;width: 32.907%; height: 17px;&quot;&gt;Redis 명령어(SET, GET 등) 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 28.3721%; height: 17px;&quot;&gt;@RedisHash 지원 여부&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%; height: 17px;&quot;&gt;지원하지 않음&lt;/td&gt;
&lt;td style=&quot;width: 32.907%; height: 17px;&quot;&gt;지원함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;u&gt;&lt;b&gt;3단 변신(최종 변신). @Cacheable&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시에 데이터가 없을 경우, 캐시에 데이터를 추가하고, 있을 경우, 캐시의 데이터 반환&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;CacheConfig.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1737771238407&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableCaching
// Spring의 캐싱 기능을 활성화
// 캐싱 관련 애너테이션(@Cacheable, @CacheEvict, @CachePut)이 동작하도록 설정
// 애플리케이션 컨텍스트에 한 번만 추가되면 전체 프로젝트에서 동작
@Configuration
public class CacheConfig {

  public static final String CACHE1 = &quot;cache1&quot;;
  public static final String CACHE2 = &quot;cache2&quot;;

  @AllArgsConstructor
  @Getter
  public static class CacheProperty{
    private String name;
    private Integer ttl;
  }

  @Bean
  public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(){
    PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
        .builder()
        .allowIfSubType(Object.class)
        .build();

    ObjectMapper objectMapper = new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .registerModule(new JavaTimeModule())
        .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
        .disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);

    List&amp;lt;CacheProperty&amp;gt; properties = List.of(
        new CacheProperty(CACHE1, 300),
        new CacheProperty(CACHE2, 30)
    );

    return (builder -&amp;gt; {
      properties.forEach(i -&amp;gt;
          builder.withCacheConfiguration(i.getName(),
              RedisCacheConfiguration.defaultCacheConfig()
              .disableCachingNullValues()
              .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
              .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)))
              .entryTtl(Duration.ofSeconds(i.getTtl()))));
    });
    // RedisCacheConfiguration.defaultCacheConfig(): 기본 Redis 캐시 설정을 가져옴
    // disableCachingNullValues(): null 값을 캐싱하지 않도록 설정
    // serializeKeysWith: Redis의 키를 직렬화하는 방법을 설정, 키를 문자열로 직렬화
    // serializeValuesWith: Redis의 값을 직렬화하는 방법을 설정, GenericJackson2JsonRedisSerializer를 사용하여 값을 JSON으로 직렬화
    // entryTtl: 캐시의 만료 시간을 설정
  }
}&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;style2&quot;&gt;UserService.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1737772864823&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class UserService {
  @Cacheable(cacheNames = CACHE1, key = &quot;'users:' + #id&quot;)
  // cacheNames: 캐시를 구분하고 각각의 캐시에 대해 커스터마이징된 설정을 적용하기 위해 사용
  public User getUser3(final Long id) {
    return userRepository.findById(id).orElseThrow();
  }
}&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 style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;https://fastcampus.co.kr/dev_online_traffic_data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737770193310&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&quot; data-og-description=&quot;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사 &quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/blWS3b/hyX4ydIV07/R01bhzZx6j8ByvtCOidmqK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/qV5oi/hyX7RvUH5W/5UbnevXJ3VCuLxGSSUKU11/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/eAPuCO/hyX7XbPE0Y/aPilDfdaaTO3IRU2hiz23k/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/blWS3b/hyX4ydIV07/R01bhzZx6j8ByvtCOidmqK/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/qV5oi/hyX7RvUH5W/5UbnevXJ3VCuLxGSSUKU11/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/eAPuCO/hyX7XbPE0Y/aPilDfdaaTO3IRU2hiz23k/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520');&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;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/981</guid>
      <comments>https://hj0216.tistory.com/981#entry981comment</comments>
      <pubDate>Sat, 25 Jan 2025 11:43:09 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] HTTP load testing tool, Vegeta 설치(Window)</title>
      <link>https://hj0216.tistory.com/980</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/zanmang-loopy&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ryhZn/btsLTthUS6J/Xu3Q1YZv4mL6IpoeIiOCC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FryhZn%2FbtsLTthUS6J%2FXu3Q1YZv4mL6IpoeIiOCC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선생님은 Mac, 나는 Windows 시리즈입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 알아서 따라가야하는 시간이라는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 Redis를 활용한 caching을 배우고 있는데, 정말 효과가 있나를 테스트 해보기 위해서 발빠르게 Postman을 연속해서 클릭할 수도 있지만, 부하테스트 툴이 따로 존재한다고 하여 해당 툴에서 맡겨보고자 합니다,,&lt;/p&gt;
&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;Vegeta&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/tsenart/vegeta&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/tsenart/vegeta&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737380820812&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - tsenart/vegeta: HTTP load testing tool and library. It's over 9000!&quot; data-og-description=&quot;HTTP load testing tool and library. It's over 9000! - tsenart/vegeta&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/tsenart/vegeta&quot; data-og-url=&quot;https://github.com/tsenart/vegeta&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3B6Rw/hyX4vmL27G/J4uuUFiBhCqbKQv6kbkMpK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/AJeet/hyX4vAigaO/tszXsBGYg2S4zfNIvmfGKK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/tsenart/vegeta&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/tsenart/vegeta&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3B6Rw/hyX4vmL27G/J4uuUFiBhCqbKQv6kbkMpK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/AJeet/hyX4vAigaO/tszXsBGYg2S4zfNIvmfGKK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - tsenart/vegeta: HTTP load testing tool and library. It's over 9000!&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;HTTP load testing tool and library. It's over 9000! - tsenart/vegeta&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfKXo8/btsLUVK4Een/IHx1BLlklMpGtfpoWpAB41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfKXo8/btsLUVK4Een/IHx1BLlklMpGtfpoWpAB41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfKXo8/btsLUVK4Een/IHx1BLlklMpGtfpoWpAB41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfKXo8%2FbtsLUVK4Een%2FIHx1BLlklMpGtfpoWpAB41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;842&quot; height=&quot;842&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈 씻고 찾아보아도 Windows는 없습니다.&lt;/p&gt;
&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;우리에게는 윈분투,, WSL(&lt;span style=&quot;color: #3a4954; text-align: start;&quot;&gt;Windows Subsystem for Linux&lt;/span&gt;),,&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #161616; text-align: start;&quot;&gt;윈도우 환경에 우분투 설치해서 Vegeta를 쓸 수 있습니다 .&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;No brew.. &amp;zwj;♀️ &amp;zwj;♂️ Yes sudo.. &amp;zwj;♀️ &amp;zwj;♂️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #161616; text-align: start;&quot;&gt;1. 우분투 설치&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1737381222975&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wsl --install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공용 네트워크에서는 잘 안받아질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 도서관 와이파이로 install을 진행하려고 하니, &lt;i&gt;&lt;u&gt;&lt;b&gt;'목록&amp;nbsp;배포를&amp;nbsp;가져오지&amp;nbsp;못했습니다.&amp;nbsp;작업&amp;nbsp;시간을&amp;nbsp;초과했습니다.'&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&amp;nbsp;라는 오류가 났었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집에 와서 다시 설치하니 잘 됐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 끝나고 재부팅 후 자동으로 터미널 창에서 우분투가 실행되며, 이 때 UNIX 유저명과 비밀번호를 설정해주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. root 계정 로그인&lt;/p&gt;
&lt;pre id=&quot;code_1737456091538&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo su&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 비밀번호를 입력하면 우분투 세계의 근원이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. IPv4 주소 확인&lt;/p&gt;
&lt;pre id=&quot;code_1737456160989&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ipconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vegeta를 통해 부하 테스트를 진행하기 위해 어느 IP 주소로 요청을 보낼 것인지 적어줘야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 미리 확인해둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Vegeta 설치&lt;/p&gt;
&lt;pre id=&quot;code_1737456278950&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo install vegeta&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vegeta 설치를 완료하면, 재빠른 포스트맨 클릭 대신 Vegeta가 요청을 보내줄 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(번외) Vegeta 명령어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;vegeta attack : 부하 테스트(공격)를 실행&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;-timeout : 각 요청의 최대 대기 시간, 요청이 지정한 시간(예: 20초) 안에 응답되지 않으면 실패로 간주&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;-duration : 실행 시간&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;-rate : 초당 요청 수, 트래픽 양을 설정&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;-targets : 테스트할 대상 URL과 HTTP 메서드 정보가 담긴 파일&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;-workers : 동시에 동작하는 작업 단위(worker)의 수, 병렬 요청 처리에 사용&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;| : 파이프라인, 앞에 있는 vegeta attack에서 만든 결과를 뒤에 있는 vegeta report로 바로 넘기는 것&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;vegeta report : 결과를 요약 보고서 형식으로 출력, 평균 응답 시간, 성공률, 요청/초, 분산 등의 통계가 포함 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1737456504536&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vegeta attack -timeout=20s -duration=10s -rate=3000/1s -targets=request.txt -workers=100 | vegeta report

# request.txt
GET http://192.168.0.1:8080/users/1
GET http://192.168.0.1:8080/users/2
GET http://192.168.0.1:8080/users/3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3a4954; text-align: start;&quot;&gt;최대 20초의 타임아웃으로 100개의 워커가 10초동안 초당 3천번의 요청을 보냄&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devstriker.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devstriker.tistory.com/14&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737381519996&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;[Linux] Windows11에 WSL(Ubuntu) 설치&quot; data-og-description=&quot;개발환경 세팅을 위해 WSL (Windows Subsystem for Linux)로 내 Windows11에 Linux(Ubuntu)를 설치하고자 한다.&amp;nbsp; Windows 10 버전 2004 이상(빌드 19041 이상) 또는 Windows 11이라면 이하의 과정을 통해 별도의 번거로운 &quot; data-og-host=&quot;devstriker.tistory.com&quot; data-og-source-url=&quot;https://devstriker.tistory.com/14&quot; data-og-url=&quot;https://devstriker.tistory.com/14&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jJEVh/hyX4mDnYOJ/KBIkfKy2yVXDOesKqzxKn0/img.png?width=800&amp;amp;height=312&amp;amp;face=0_0_800_312,https://scrap.kakaocdn.net/dn/qExC0/hyX4lYLL3C/FO6QfAeZW6MiCPXZy3hqQ0/img.png?width=800&amp;amp;height=312&amp;amp;face=0_0_800_312,https://scrap.kakaocdn.net/dn/b0hF15/hyX4oOIMmU/lYFruRS4H9ms6JRPY19H11/img.png?width=1500&amp;amp;height=823&amp;amp;face=0_0_1500_823&quot;&gt;&lt;a href=&quot;https://devstriker.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devstriker.tistory.com/14&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jJEVh/hyX4mDnYOJ/KBIkfKy2yVXDOesKqzxKn0/img.png?width=800&amp;amp;height=312&amp;amp;face=0_0_800_312,https://scrap.kakaocdn.net/dn/qExC0/hyX4lYLL3C/FO6QfAeZW6MiCPXZy3hqQ0/img.png?width=800&amp;amp;height=312&amp;amp;face=0_0_800_312,https://scrap.kakaocdn.net/dn/b0hF15/hyX4oOIMmU/lYFruRS4H9ms6JRPY19H11/img.png?width=1500&amp;amp;height=823&amp;amp;face=0_0_1500_823');&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;[Linux] Windows11에 WSL(Ubuntu) 설치&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개발환경 세팅을 위해 WSL (Windows Subsystem for Linux)로 내 Windows11에 Linux(Ubuntu)를 설치하고자 한다.&amp;nbsp; Windows 10 버전 2004 이상(빌드 19041 이상) 또는 Windows 11이라면 이하의 과정을 통해 별도의 번거로운&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devstriker.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lsdiary.tistory.com/86&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://lsdiary.tistory.com/86&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737381566868&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;Spring cache abstraction, Vegeta 오픈소스 사용해보기&quot; data-og-description=&quot;2024.05.08 - [Spring/대용량 트래픽] - Spring Boot Cache&amp;nbsp;Spring Boot Cache2024.05.08 - [Spring/대용량 트래픽] - Redis Cache로 실습하기&amp;nbsp;Redis Cache로 실습하기2024.05.07 - [Spring/대용량 트래픽] - Redis Cache 이론&amp;nbsp;Redis Cache &quot; data-og-host=&quot;lsdiary.tistory.com&quot; data-og-source-url=&quot;https://lsdiary.tistory.com/86&quot; data-og-url=&quot;https://lsdiary.tistory.com/86&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/p1iLF/hyX4nbeE7Y/2Z01RLt635dFvyb9SLz7z1/img.png?width=776&amp;amp;height=52&amp;amp;face=0_0_776_52,https://scrap.kakaocdn.net/dn/r1SsC/hyX4tWNdNA/o33EpGEFVtqk05uuT9hOU1/img.png?width=776&amp;amp;height=52&amp;amp;face=0_0_776_52,https://scrap.kakaocdn.net/dn/jLwrP/hyX4sjij0d/JioOSskRqtw09mzMI9Lvb1/img.png?width=1452&amp;amp;height=427&amp;amp;face=0_0_1452_427&quot;&gt;&lt;a href=&quot;https://lsdiary.tistory.com/86&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lsdiary.tistory.com/86&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/p1iLF/hyX4nbeE7Y/2Z01RLt635dFvyb9SLz7z1/img.png?width=776&amp;amp;height=52&amp;amp;face=0_0_776_52,https://scrap.kakaocdn.net/dn/r1SsC/hyX4tWNdNA/o33EpGEFVtqk05uuT9hOU1/img.png?width=776&amp;amp;height=52&amp;amp;face=0_0_776_52,https://scrap.kakaocdn.net/dn/jLwrP/hyX4sjij0d/JioOSskRqtw09mzMI9Lvb1/img.png?width=1452&amp;amp;height=427&amp;amp;face=0_0_1452_427');&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;Spring cache abstraction, Vegeta 오픈소스 사용해보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2024.05.08 - [Spring/대용량 트래픽] - Spring Boot Cache&amp;nbsp;Spring Boot Cache2024.05.08 - [Spring/대용량 트래픽] - Redis Cache로 실습하기&amp;nbsp;Redis Cache로 실습하기2024.05.07 - [Spring/대용량 트래픽] - Redis Cache 이론&amp;nbsp;Redis Cache&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lsdiary.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/980</guid>
      <comments>https://hj0216.tistory.com/980#entry980comment</comments>
      <pubDate>Tue, 21 Jan 2025 20:01:22 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] objectRedisTemplate과 ClassCastException</title>
      <link>https://hj0216.tistory.com/979</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/zanmang-loopy-ver-5?t_ch=share_link_web&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0nlCq/btsLS8DUyAO/Zinq0DCbDl1Zp64wwRbLFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0nlCq%2FbtsLS8DUyAO%2FZinq0DCbDl1Zp64wwRbLFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 한 번 입력 안했다고 Exception에 멱살 잡힌 이야기를 정리해 봅니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;농담입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RedisTemplate을 사용할 때, Class Type을 명시할 경우, 재사용성이 떨어져서 Object로 Value값을 지정할 경우, 나타나는 오류에 대해 간단히 정리해 보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;RedisTemplate를&amp;nbsp;광범위하게&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;Value를&amp;nbsp;Object로&amp;nbsp;확장&lt;/p&gt;
&lt;pre id=&quot;code_1737198897953&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
RedisTemplate&amp;lt;String, Object&amp;gt; objectRedisTemplate(RedisConnectionFactory connectionFactory) {
	ObjectMapper objectMapper = new ObjectMapper()
		.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
		.registerModule(new JavaTimeModule())
		.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);

	RedisTemplate&amp;lt;String, Object&amp;gt; template = new RedisTemplate&amp;lt;&amp;gt;();
	template.setConnectionFactory(connectionFactory);
	template.setKeySerializer(new StringRedisSerializer());
	template.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));

	return template;
}&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;2. 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LinkedHashMap을 User 타입으로 Casting하지 못해 ClassCastException 발생&lt;/p&gt;
&lt;pre id=&quot;code_1737198946990&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.ClassCastException: 
class java.util.LinkedHashMap cannot be cast to class com.example.spring_boot_cache.domain.entity.User (java.util.LinkedHashMap is in module java.base of loader 'bootstrap';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GenericJackson2JsonRedisSerializer는 JSON 데이터를 역직렬화할 때, 대상 객체의 타입 정보를 명시적으로 제공하지 않으면 기본적으로 Map 타입(즉, LinkedHashMap)으로 역직렬화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; LinkedHashMap을 User 객체로 강제 캐스팅할 수 없어 오류 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;JSON 데이터를 저장할 때 타입 정보를 포함&lt;/p&gt;
&lt;pre id=&quot;code_1737200454536&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
RedisTemplate&amp;lt;String, Object&amp;gt; objectRedisTemplate(RedisConnectionFactory connectionFactory) {
	PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
		.builder()
		.allowIfSubType(Object.class)
		.build();

	ObjectMapper objectMapper = new ObjectMapper()
		.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
		.registerModule(new JavaTimeModule())
		.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
		.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);

	RedisTemplate&amp;lt;String, Object&amp;gt; template = new RedisTemplate&amp;lt;&amp;gt;();
	template.setConnectionFactory(connectionFactory);
	template.setKeySerializer(new StringRedisSerializer());
	template.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));

	return template;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis에&amp;nbsp;저장되는&amp;nbsp;데이터의&amp;nbsp;타입은&amp;nbsp;여러&amp;nbsp;종류(Object)로&amp;nbsp;다양 &lt;br /&gt;Jackson(직렬화&amp;nbsp;라이브러리)은&amp;nbsp;기본적으로&amp;nbsp;타입&amp;nbsp;정보를&amp;nbsp;추가하지&amp;nbsp;않음 &lt;br /&gt;데이터를&amp;nbsp;읽어올&amp;nbsp;때&amp;nbsp;원래&amp;nbsp;타입이&amp;nbsp;무엇인지&amp;nbsp;알&amp;nbsp;수&amp;nbsp;없는&amp;nbsp;문제가&amp;nbsp;발생 &lt;br /&gt;&lt;br /&gt;PolymorphicTypeValidator:&amp;nbsp;allowIfSubType을&amp;nbsp;통해&amp;nbsp;변환할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;데이터&amp;nbsp;타입을&amp;nbsp;정의 &lt;br /&gt;&lt;br /&gt;activateDefaultTyping:&amp;nbsp;Jackson에서&amp;nbsp;직렬화/역직렬화&amp;nbsp;시&amp;nbsp;객체&amp;nbsp;타입&amp;nbsp;정보를&amp;nbsp;포함하도록&amp;nbsp;설정하는&amp;nbsp;메서드 &lt;br /&gt;&amp;nbsp; 만일, allowIfSubType에서 허용하지 않은 class type이 redis에 저장되어있는 채로 역직렬화 시도 시, &lt;b&gt;InvalidTypeIdException&lt;/b&gt; 발생 &lt;br /&gt;PolymorphicTypeValidator:&amp;nbsp;역직렬화&amp;nbsp;시&amp;nbsp;허용된&amp;nbsp;클래스&amp;nbsp;타입인지&amp;nbsp;검증하는&amp;nbsp;데&amp;nbsp;사용 &lt;br /&gt;DefaultTyping:&amp;nbsp;비최종&amp;nbsp;클래스(Non-final&amp;nbsp;class)에&amp;nbsp;대해서만&amp;nbsp;타입&amp;nbsp;정보를&amp;nbsp;포함 &lt;br /&gt;예를&amp;nbsp;들어,&amp;nbsp;Object나&amp;nbsp;서브클래스에는&amp;nbsp;타입&amp;nbsp;정보가&amp;nbsp;포함되지만,&amp;nbsp;String,&amp;nbsp;Integer처럼&amp;nbsp;final로&amp;nbsp;선언된&amp;nbsp;클래스는&amp;nbsp;제외&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ TTL 설정은 안해서 Redis 값이 Class 정보를 포함하지 않은 채로 남아있게 되면 만날 수 있는 오류.. &lt;/p&gt;
&lt;pre id=&quot;code_1737200131536&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 오류
com.fasterxml.jackson.databind.exc.MismatchedInputException: 
Unexpected token (START_OBJECT), expected START_ARRAY: 
need Array value to contain As.WRAPPER_ARRAY type information for class java.lang.Object

# Redis 패키지가 저장되지 않았던 데이터
&quot;{\&quot;id\&quot;:3,\&quot;name\&quot;:\&quot;hi3\&quot;,\&quot;email\&quot;:\&quot;hi3@email.com\&quot;,\&quot;createdAt\&quot;:[2025,1,18,18,25,53,321769000],\&quot;updatedAt\&quot;:[2025,1,18,18,25,53,321769000]}&quot;

# Redis 패키지가 저장된 데이터
&quot;[\&quot;com.example.spring_boot_cache.domain.entity.User\&quot;,{\&quot;id\&quot;:3,\&quot;name\&quot;:\&quot;hi3\&quot;,\&quot;email\&quot;:\&quot;hi3@email.com\&quot;,\&quot;createdAt\&quot;:[2025,1,18,20,33,16,59381000],\&quot;updatedAt\&quot;:[2025,1,18,20,33,16,59381000]}]&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://fastcampus.co.kr/dev_online_traffic_data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737199235893&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&quot; data-og-description=&quot;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사 &quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bC2d7W/hyX4vmpnri/XrxJKKc0sJpNK5DdWSWGP1/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/bmtBPU/hyX4xLiuHA/K59Y1A6jdddF3tOY7DmBU1/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/bJNKan/hyX0m5KCDX/6wKwF8Ng3VIS0lcQz9HKxK/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520&quot;&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastcampus.co.kr/dev_online_traffic_data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bC2d7W/hyX4vmpnri/XrxJKKc0sJpNK5DdWSWGP1/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/bmtBPU/hyX4xLiuHA/K59Y1A6jdddF3tOY7DmBU1/img.png?width=1200&amp;amp;height=631&amp;amp;face=0_0_1200_631,https://scrap.kakaocdn.net/dn/bJNKan/hyX0m5KCDX/6wKwF8Ng3VIS0lcQz9HKxK/img.png?width=1440&amp;amp;height=520&amp;amp;face=0_0_1440_520');&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;9개 프로젝트로 경험하는 대용량 트래픽 &amp;amp; 데이터 처리 완벽 마스터하기 | 패스트캠퍼스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에서 자주 일어나는 대용량 트래픽 &amp;amp; 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 &amp;amp; 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/979</guid>
      <comments>https://hj0216.tistory.com/979#entry979comment</comments>
      <pubDate>Sun, 19 Jan 2025 10:33:11 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Windows Docker 환경에서 MySQL 설치 및 연결</title>
      <link>https://hj0216.tistory.com/978</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MdMRX/btsLJVeTVKe/BL2To2k2CzpzK3OZZky0H0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MdMRX/btsLJVeTVKe/BL2To2k2CzpzK3OZZky0H0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MdMRX/btsLJVeTVKe/BL2To2k2CzpzK3OZZky0H0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMdMRX%2FbtsLJVeTVKe%2FBL2To2k2CzpzK3OZZky0H0%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;230&quot; height=&quot;210&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여전히 저는 환경 설정에 머물러 있습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis와 달리 유난히 연결되지 않았던 MySQL, YourSQL이 될 뻔하다 다시 찾은 MySQL..&lt;/p&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에서 MySQL 설치 후, 포트가 사용 중이라 오류가 났던 부분과 SpringBoot에 연결하면서 이 비밀번호는 맞는데 자꾸 틀리다고 오류가 났던 부분까지의 과정을 남겨봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. MySQL 이미지 다운로드&lt;/p&gt;
&lt;pre id=&quot;code_1736610245808&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker pull mysql:8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. MySQL 컨테이너 생성&lt;/p&gt;
&lt;pre id=&quot;code_1736610407602&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -e MYSQL_ROOT_PASSWORD=password -d -p 3306:3306 mysql:8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은 실패&lt;/p&gt;
&lt;pre id=&quot;code_1736610482717&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Error response from daemon: 
Ports are not available: 
exposing port TCP 0.0.0.0:3306 -&amp;gt; 0.0.0.0:0: 
listen tcp 0.0.0.0:3306: 
bind: 
Only one usage of each socket address (protocol/network address/port) is normally permitted&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Window에 설치된 MySQL이 3306 포트를 사용하고 있기에 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;컨테이너에 3306 호스트 포트를 할당할 수 없다&lt;/span&gt;는 오류가 발생하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736610601271&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;netstat -ano | findstr 3306
#  TCP    0.0.0.0:3306           0.0.0.0:0              LISTENING       6628
# 0.0.0.0:3306: IPv4에서 모든 네트워크 인터페이스에 대해 이 포트를 열어 놓고 있음
# 6628: 모든 관련 포트를 점유하고 있는 프로세스의 PID
#  TCP    0.0.0.0:3306           0.0.0.0:0              LISTENING       6628
#  TCP    [::]:3306              [::]:0                 LISTENING       6628
# IPv6에서 모든 네트워크 인터페이스에 대해 이 포트를 열어 놓고 있음
#  TCP    [::]:33060             [::]:0                 LISTENING       6628
# 33060: MySQL에서 제공하는 X Protocol(엑스 프로토콜)을 사용하는 통신에 사용
# 기존의 SQL 기반 통신 방식과는 다른, JSON 기반 데이터 처리를 지원하기 위해 만들어짐
# JSON 데이터를 쉽게 저장하고 다룰 수 있는 NoSQL 기능을 추가한 것&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트 운영체제(윈도우)에서 3306 포트를 사용하는지 확인하면 3306 포트가 열심히 듣고있는 걸 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스 제거해서 도커가 3306 포트를 사용할 수 있게 해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1736656920420&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;askkill /pid 6628 /f&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736657031796&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;오류: 프로세스(PID 6628)를 종료할 수 없습니다.
원인: 액세스가 거부되었습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은 실패&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; cmd를 관리자 권한으로 실행해서 해당 프로세스를 kill해주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 저는 컨테이너에&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;다른 포트르 할당하여 생성해볼 예정입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736657787051&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -e MYSQL_ROOT_PASSWORD=password -d -p 3307:3306 mysql:8
# docker run Docker 컨테이너를 실행하는 기본 명령
# -e MYSQL_ROOT_PASSWORD=password: MySQL의 루트 계정(root user) 비밀번호를 지정하는 환경 변수
# -d: Detached mode로 컨테이너를 실행, 터미널에서 실행 로그를 출력하지 않고, 백그라운드에서 컨테이너를 실행
# -p 3307:3306: 포트 포워딩을 설정
# 호스트의 3307 포트를 컨테이너 내부의 3306 포트로 연결
# 로컬 머신에서 localhost:3307로 docker 컨테이너의 MySQL 서버에 접근할 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 명령어와 달리진 점은 포트 포워딩 부분이 달라졌다는 것인데, 호스트의 3307 포트를 컨테이너 내부의 3306 포트로 연결했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. SpringBoot 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음은 Springboot에서 properties 파일에 MySQL 설정을 해야합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1736658192655&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: &quot;jdbc:mysql://localhost:3306/fastsns&quot;
    username: root
    password: password
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect&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;위의 설정 파일로는 MySQL을 만날 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어 틀린 부분이 2개나 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 메시지는 단 하나 뿐 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 원인은 자그마치 2개 !&lt;/p&gt;
&lt;pre id=&quot;code_1736658475766&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)&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;  1. 비밀번호 설정 규칙&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;대문자, 소문자, 숫자, 특수문자를 포함한 암호 길이 8자 이상으로 설정해야 연결이 제대로 됨&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존에는 비밀번호가 password였는데, Springboot에 MySQL을 연결하고 싶다하면 password123! 정도는 설정해줘야 합니다.. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;  2. 포트번호 확인&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;로컬 컴퓨터가 쓰고 있던 포트를 빼앗고 싶지 않았던 전 로컬 3307포트를 사용했었습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, datasource의 포트번호는 3307이 되야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⭐ 최종&lt;/p&gt;
&lt;pre id=&quot;code_1736658821447&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: &quot;jdbc:mysql://localhost:3307/fastsns&quot;
    username: root
    password: password123!
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 사실은 하나 더 있던 오류 &lt;/p&gt;
&lt;pre id=&quot;code_1736658315911&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Caused by: org.hibernate.HibernateException: 
Unable to determine Dialect without JDBC metadata 
(please set 'jakarta.persistence.jdbc.url' for common cases 
or 'hibernate.dialect' when a custom Dialect implementation must be provided)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;원인: Spring Boot 컨테이너가 MySQL Dialect 설정을 못 찾음&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;해결: dialect 설정 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736658367777&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect&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 style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;a href=&quot;https://yonghwankim-dev.tistory.com/570&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yonghwankim-dev.tistory.com/570&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736656971819&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;[docker] mysql conatiner 생성시 포트포워딩 문제&quot; data-og-description=&quot;배경 docker 엔진을 사용하여 mysql container를 생성하기 위해 다음과 같은 명령어를 실행하였습니다. $ docker run -p 3306:3306 &amp;mdash;name mysql_boot -e MYSQL_ROOT_PASSWORD=1 -e MYSQL_DATABASE=springboot -e MYSQL_USER=yonghwan -e MY&quot; data-og-host=&quot;yonghwankim-dev.tistory.com&quot; data-og-source-url=&quot;https://yonghwankim-dev.tistory.com/570&quot; data-og-url=&quot;https://yonghwankim-dev.tistory.com/570&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biPNVt/hyX0wlIqO7/PK3iogDpVjVijxaEswBry0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/QAPjb/hyX0u9gKYD/QUuFtXR5kuNSsGILkmRgHK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://yonghwankim-dev.tistory.com/570&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yonghwankim-dev.tistory.com/570&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biPNVt/hyX0wlIqO7/PK3iogDpVjVijxaEswBry0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/QAPjb/hyX0u9gKYD/QUuFtXR5kuNSsGILkmRgHK/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;[docker] mysql conatiner 생성시 포트포워딩 문제&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;배경 docker 엔진을 사용하여 mysql container를 생성하기 위해 다음과 같은 명령어를 실행하였습니다. $ docker run -p 3306:3306 &amp;mdash;name mysql_boot -e MYSQL_ROOT_PASSWORD=1 -e MYSQL_DATABASE=springboot -e MYSQL_USER=yonghwan -e MY&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yonghwankim-dev.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/gingsero/222350441743&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://m.blog.naver.com/gingsero/222350441743&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736657048316&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;taskkill 액세스가 거부되었습니다.&quot; data-og-description=&quot;어라? 80포트로 뭔가 떠있는데 해당 프로세스가 taskkill로 죽여보니 액세스가 거부된다. 기억은 안나지만 ...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://m.blog.naver.com/gingsero/222350441743&quot; data-og-url=&quot;https://blog.naver.com/gingsero/222350441743&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/NwCO9/hyX0xLFzt1/XUKEuO9Hls0p051LKdKi80/img.png?width=349&amp;amp;height=110&amp;amp;face=0_0_349_110&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/gingsero/222350441743&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://m.blog.naver.com/gingsero/222350441743&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/NwCO9/hyX0xLFzt1/XUKEuO9Hls0p051LKdKi80/img.png?width=349&amp;amp;height=110&amp;amp;face=0_0_349_110');&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;taskkill 액세스가 거부되었습니다.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;어라? 80포트로 뭔가 떠있는데 해당 프로세스가 taskkill로 죽여보니 액세스가 거부된다. 기억은 안나지만 ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736657156656&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;[Error] 도커 컨테이너 생성시 포트 충돌&quot; data-og-description=&quot;docker Error invoking remote method 'docker-start-container': Error: (HTTP code 500) server error - Ports are not available: exposing port TCP 0.0.0.0&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&quot; data-og-url=&quot;https://velog.io/@saintho/Error-도커-컨테이너-생성시-포트-충돌&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buSRUT/hyX0kr262n/ZHVcf8527eKvA5DPdKNYNk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buSRUT/hyX0kr262n/ZHVcf8527eKvA5DPdKNYNk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&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;[Error] 도커 컨테이너 생성시 포트 충돌&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;docker Error invoking remote method 'docker-start-container': Error: (HTTP code 500) server error - Ports are not available: exposing port TCP 0.0.0.0&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736657741484&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;[Error] 도커 컨테이너 생성시 포트 충돌&quot; data-og-description=&quot;docker Error invoking remote method 'docker-start-container': Error: (HTTP code 500) server error - Ports are not available: exposing port TCP 0.0.0.0&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&quot; data-og-url=&quot;https://velog.io/@saintho/Error-도커-컨테이너-생성시-포트-충돌&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buSRUT/hyX0kr262n/ZHVcf8527eKvA5DPdKNYNk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@saintho/Error-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buSRUT/hyX0kr262n/ZHVcf8527eKvA5DPdKNYNk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&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;[Error] 도커 컨테이너 생성시 포트 충돌&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;docker Error invoking remote method 'docker-start-container': Error: (HTTP code 500) server error - Ports are not available: exposing port TCP 0.0.0.0&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@jiwonblue/Spring-Boot%EC%99%80-MySQL-%EC%97%B0%EB%8F%99-%EC%98%A4%EB%A5%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@jiwonblue/Spring-Boot%EC%99%80-MySQL-%EC%97%B0%EB%8F%99-%EC%98%A4%EB%A5%98&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736658377899&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;Spring Boot와 MySQL 연동 오류&quot; data-og-description=&quot;Spring Boot와 MySQL연동 시 발생한 오류Caused by: org.hibernate.HibernateException: Unable to determine Dialect without JDBC metadata (please set 'jakarta.persi&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@jiwonblue/Spring-Boot%EC%99%80-MySQL-%EC%97%B0%EB%8F%99-%EC%98%A4%EB%A5%98&quot; data-og-url=&quot;https://velog.io/@jiwonblue/Spring-Boot와-MySQL-연동-오류&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/etOvj5/hyX0vUDoj7/0U4JZzhhReK9g4rn66g890/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/BNb9G/hyX0qeHFYq/UeFnc7Or5fifofk5qCEuB1/img.png?width=210&amp;amp;height=210&amp;amp;face=0_0_210_210&quot;&gt;&lt;a href=&quot;https://velog.io/@jiwonblue/Spring-Boot%EC%99%80-MySQL-%EC%97%B0%EB%8F%99-%EC%98%A4%EB%A5%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@jiwonblue/Spring-Boot%EC%99%80-MySQL-%EC%97%B0%EB%8F%99-%EC%98%A4%EB%A5%98&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/etOvj5/hyX0vUDoj7/0U4JZzhhReK9g4rn66g890/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/BNb9G/hyX0qeHFYq/UeFnc7Or5fifofk5qCEuB1/img.png?width=210&amp;amp;height=210&amp;amp;face=0_0_210_210');&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;Spring Boot와 MySQL 연동 오류&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot와 MySQL연동 시 발생한 오류Caused by: org.hibernate.HibernateException: Unable to determine Dialect without JDBC metadata (please set 'jakarta.persi&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://luna-archive.tistory.com/17&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://luna-archive.tistory.com/17&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736658516451&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;Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES) 해결하기&quot; data-og-description=&quot;이번 글에서는 spring boot에서 RDB 세팅을 하다가 겪었던 에러 사항 및 해결 방법에 대해 남기려고 합니다. 최근 spring batch작업을 위해 RDB 연동을 하는데, 아래와 같은 에러를 마주쳤다. Caused by: java.&quot; data-og-host=&quot;luna-archive.tistory.com&quot; data-og-source-url=&quot;https://luna-archive.tistory.com/17&quot; data-og-url=&quot;https://luna-archive.tistory.com/17&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9R9xQ/hyX0qseJlY/hjvGcCBHLy6XKqHKn27Kv0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/nrNvw/hyX0qFMuI6/TykN1IXGnFD8pkIdvqKf40/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://luna-archive.tistory.com/17&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://luna-archive.tistory.com/17&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9R9xQ/hyX0qseJlY/hjvGcCBHLy6XKqHKn27Kv0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/nrNvw/hyX0qFMuI6/TykN1IXGnFD8pkIdvqKf40/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;Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES) 해결하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 spring boot에서 RDB 세팅을 하다가 겪었던 에러 사항 및 해결 방법에 대해 남기려고 합니다. 최근 spring batch작업을 위해 RDB 연동을 하는데, 아래와 같은 에러를 마주쳤다. Caused by: java.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;luna-archive.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kjy1ho.tistory.com/18&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kjy1ho.tistory.com/18&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736658676702&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;[mysql] java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)&quot; data-og-description=&quot;이클립스와 mySQL을 연동하는데 java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES) 이런 에러가 떴다. 에러 이유를 알아보니 root 계정에 접속할 비밀번호가 틀려서 나온 에러라고 &quot; data-og-host=&quot;kjy1ho.tistory.com&quot; data-og-source-url=&quot;https://kjy1ho.tistory.com/18&quot; data-og-url=&quot;https://kjy1ho.tistory.com/18&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bHdHkD/hyX0n3kEfR/6T8zYM63hZhDU4Ukf9UBHk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bX1VtH/hyX0ldpHfz/f2BI1DHjJXu6M7OUQe4JaK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://kjy1ho.tistory.com/18&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kjy1ho.tistory.com/18&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bHdHkD/hyX0n3kEfR/6T8zYM63hZhDU4Ukf9UBHk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bX1VtH/hyX0ldpHfz/f2BI1DHjJXu6M7OUQe4JaK/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;[mysql] java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이클립스와 mySQL을 연동하는데 java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES) 이런 에러가 떴다. 에러 이유를 알아보니 root 계정에 접속할 비밀번호가 틀려서 나온 에러라고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kjy1ho.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-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>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/978</guid>
      <comments>https://hj0216.tistory.com/978#entry978comment</comments>
      <pubDate>Sun, 12 Jan 2025 14:13:58 +0900</pubDate>
    </item>
    <item>
      <title>[커피 월드컵] 미디어 쿼리와 모바일 CSS</title>
      <link>https://hj0216.tistory.com/977</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;coffee-worldcup.jpg&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bekqh0/btsLEbHTTgW/RgLXxUnvNVT3n2OV6TqnEk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bekqh0/btsLEbHTTgW/RgLXxUnvNVT3n2OV6TqnEk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bekqh0/btsLEbHTTgW/RgLXxUnvNVT3n2OV6TqnEk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbekqh0%2FbtsLEbHTTgW%2FRgLXxUnvNVT3n2OV6TqnEk%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;257&quot; height=&quot;437&quot; data-filename=&quot;coffee-worldcup.jpg&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1탄:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #04beb8;&quot; href=&quot;https://hj0216.tistory.com/972&quot;&gt;[커피 월드컵] Netlify React 배포&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2탄:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #04beb8;&quot; href=&quot;https://hj0216.tistory.com/973&quot;&gt;[커피&amp;nbsp;월드컵]&amp;nbsp;React&amp;nbsp;카카오톡&amp;nbsp;공유하기&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3탄: &lt;a href=&quot;https://hj0216.tistory.com/974&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[커피 월드컵] React에서 사용한 기술 정리&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4탄: 현재 글!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 커피 월드컵이 돌아가는 것 자체는 오래 걸리지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 Netlify에서 배포한 사이트를 모바일로 확인했을 때부터 시작되었죠..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC에선 참 깔끔하게 나온 화면이 모바일에서는 중구난방..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 미디어 쿼리를 사용해 봤는데, 아직도 미지의 세계지만 짧게나마 알게 된 걸 정리해 봅니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 미디어 쿼리란.. (요새 바람이 많이 불어서 바람에 날리는 낙엽 이모지를 넣어봤습니다,,)&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;style2&quot;&gt;CSS Media Query&lt;br /&gt;예를 들어 &quot;뷰포트가 480 픽셀보다 넓다.&quot;라고 지정한 규칙에 브라우저 및 장치 환경이 일치하는 경우에만 CSS를 적용할 수 있는 방법을 제공합니다. 미디어 쿼리는 뷰포트의 크기에 따라 서로 다른 레이아웃을 생성할 수 있기 때문에 반응형 웹 디자인의 중요한 부분입니다.&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. 미디어 유형..  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* all&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* print: 페이지가 인쇄된 경우에 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* screen: 페이지가 화면에 보이는 경우에 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * screen은 주로 min-width, max-width와 함께 사용하여 반응형 디자인을 만들 때 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; * 뷰포트가 특정 너비 이상 또는 이하인 경우 CSS를 적용하는 방식으로 사용&lt;/p&gt;
&lt;pre id=&quot;code_1735914874690&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@media screen and (max-width: 768px){ ... }
/* 뷰포트의 width가 768 픽셀보다 좁은 경우 적용적용 */
/*@media (width &amp;lt;= 768px) { ... }과 동일*/&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;3. 미디어 쿼리와 연산..  &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;and&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1736539423791&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*landscape(가로 방향화면) 방향으로 제한시키고 최소폭을 30 ems로 지정*/
@media (min-width: 30em) and (orientation: landscape) {
  /* &amp;hellip; */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ &lt;b&gt;em&lt;/b&gt;, &lt;b&gt;rem&lt;/b&gt;: &lt;b&gt;font-size&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b2e46; text-align: start;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;속성값에 비례&lt;/b&gt;해서 결정되는 상대 단위&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b2e46; text-align: start;&quot;&gt;&amp;nbsp; ⭐ rem을 사용하는 것이 &lt;span style=&quot;background-color: #ffffff; color: #1b2e46; text-align: start;&quot;&gt;유지보수 측면에서&lt;span&gt; 더 좋음&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b2e46; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b2e46; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; (em은 해당 요소의 font-size가 없을 경우, 부모의 font-size를 찾을 때까지 계속 거슬러 올라가는데, em계산 시 어떤 요소의 font-size가 영향을 주는지 한 번에 파악하기 어려울 수 있으므로)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736539730845&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;div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1736539995819&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;html {
  font-size: 16px;
}
div {
  font-size: 20px;
  width: 10em; 
  /* 200px, 해당 단위가 사용되고 있는 요소의 font-size 속성값이 기준
  해당 요소에 font-size가 없을 경우, 상위 요소의 font-size 적용*/
}

html {
  font-size: 16px;
}
div {
  font-size: 20px;
  width: 10rem; /* 160px, 최상위 요소의 font-size 속성값이 기준 */
}&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;style2&quot;&gt;&lt;b&gt;or(,)&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1736540617041&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*뷰포트의 높이가 680px 이상이거나, screen 모드에서 세로 모드(뷰포트의 높이가 너비보다 큰 경우)일 때 적용*/
@media (min-height: 680px), screen and (orientation: portrait) {
  /* &amp;hellip; */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ &lt;b&gt;뷰포트&lt;/b&gt;: 웹 브라우저에서 웹 콘텐츠가 &lt;b&gt;화면에 표시되는 영역&lt;/b&gt;(스크롤을 하지 않고 한눈에 보이는 영역만 포함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 브라우저가&amp;nbsp;웹사이트를&amp;nbsp;렌더링(화면에&amp;nbsp;보여주는)하는&amp;nbsp;공간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 스마트폰이나&amp;nbsp;컴퓨터에서&amp;nbsp;웹사이트를&amp;nbsp;볼&amp;nbsp;때,&amp;nbsp;그&amp;nbsp;화면에서&amp;nbsp;실제&amp;nbsp;콘텐츠가&amp;nbsp;표시되는&amp;nbsp;부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;b&gt;해상도&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 디스플레이(모니터,&amp;nbsp;스마트폰&amp;nbsp;등)에서&amp;nbsp;&lt;b&gt;실제로&amp;nbsp;보이는&amp;nbsp;부분의&amp;nbsp;픽셀&amp;nbsp;수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 스마트폰 화면 해상도 1080x1920: 화면이 가로로 1080픽셀, 세로로 1920픽셀로 구성&lt;/p&gt;
&lt;pre id=&quot;code_1736540682031&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
&amp;lt;/meta&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * width=device-width: 기기의 화면 너비에 맞게 뷰포트를 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * height=device-height: 콘텐츠가 기기의 높이를 넘는 경우 스크롤이 생길 수 있고, 동적인 뷰포트 변화가 있는 브라우저(iOS Safari 등)에서는 동작이 예상과 다를 수 있음 &amp;rarr; 잘 사용되지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * initial-scale=1.0: 초기 확대/축소 비율을 1로 설정&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;&lt;b&gt;not&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1736541085048&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*미디어 쿼리 전체의 의미를 반전*/
@media not print {
  /* &amp;hellip; */
}

/*print에만 not 적용*/
@media (hover) and not print {
  /* &amp;hellip; */
}

/*@media (not (screen and (color))), print and (color)와 동일*/
@media not screen and (color), print and (color) {
  /* &amp;hellip; */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ (&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b; text-align: start;&quot;&gt;hover&lt;/span&gt;&lt;/b&gt;): &lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b; text-align: start;&quot;&gt;hover가 가능한 지&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b; text-align: start;&quot;&gt; 여부를 조건으로 걸 때 사용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ (&lt;b&gt;color&lt;/b&gt;): &lt;b&gt;색상을 표현할 수 있는 디스플레이 장치&lt;/b&gt;를 조건으로 걸 때 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;b&gt;모노크롬 디스플레이&lt;/b&gt;(흑백만 지원)에서는 (color) 조건이 거짓(false)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Chat GPT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/CSS_layout/Media_queries&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/CSS_layout/Media_queries&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735914390943&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;미디어 쿼리 초보자 안내서 - Web 개발 학습하기 | MDN&quot; data-og-description=&quot;CSS Media Query는 예를 들어 &amp;quot;뷰포트가 480 픽셀보다 넓다.&amp;quot;라고 여러분이 지정한 규칙에 브라우저 및 장치 환경이 일치하는 경우에만 CSS를 적용할 수 있는 방법을 제공합니다. 미디어 쿼리는 뷰포트&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/CSS_layout/Media_queries&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/CSS_layout/Media_queries&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cBbgHm/hyXWwlKUGm/Zli8uSpsRSnnrlEyi3kSw1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/gpj0r/hyXWABIG07/KEPRYZFwIhBCX6hlh1BSh0/img.png?width=1443&amp;amp;height=917&amp;amp;face=0_0_1443_917&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/CSS_layout/Media_queries&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/CSS_layout/Media_queries&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cBbgHm/hyXWwlKUGm/Zli8uSpsRSnnrlEyi3kSw1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/gpj0r/hyXWABIG07/KEPRYZFwIhBCX6hlh1BSh0/img.png?width=1443&amp;amp;height=917&amp;amp;face=0_0_1443_917');&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;미디어 쿼리 초보자 안내서 - Web 개발 학습하기 | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;CSS Media Query는 예를 들어 &quot;뷰포트가 480 픽셀보다 넓다.&quot;라고 여러분이 지정한 규칙에 브라우저 및 장치 환경이 일치하는 경우에만 CSS를 적용할 수 있는 방법을 제공합니다. 미디어 쿼리는 뷰포트&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/css-em-rem/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.daleseo.com/css-em-rem/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736539570828&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CSS 상대 단위 - em과 rem&quot; data-og-description=&quot;Engineering Blog by Dale Seo&quot; data-og-host=&quot;www.daleseo.com&quot; data-og-source-url=&quot;https://www.daleseo.com/css-em-rem/&quot; data-og-url=&quot;https://www.daleseo.com/css-em-rem/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zn6hA/hyXWre5FKn/JCtZuJBCQUK9Cw9JDQcKZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/css-em-rem/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.daleseo.com/css-em-rem/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zn6hA/hyXWre5FKn/JCtZuJBCQUK9Cw9JDQcKZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CSS 상대 단위 - em과 rem&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Engineering Blog by Dale Seo&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.daleseo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/커피 월드컵</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/977</guid>
      <comments>https://hj0216.tistory.com/977#entry977comment</comments>
      <pubDate>Sat, 11 Jan 2025 06:05:34 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Windows Docker 환경에서 Redis 설치</title>
      <link>https://hj0216.tistory.com/976</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD0VT1/btsLEpzu8dK/GK41CtZUnn66G8OsdJqa71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD0VT1/btsLEpzu8dK/GK41CtZUnn66G8OsdJqa71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD0VT1/btsLEpzu8dK/GK41CtZUnn66G8OsdJqa71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD0VT1%2FbtsLEpzu8dK%2FGK41CtZUnn66G8OsdJqa71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선생님은 Mac, 전 Window..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 생기면 스스로 해결해 나가야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 강의는 시작도 안하고, 환경만 만들었을 뿐..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dokcer Desktop 설치도 문제가 있었지만, 레디스 이미지를 가져오는데에도 문제가 생겼습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 전 다른 블로거분들의 도움으로 Redis 설치에 성공했습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서 Docker 환경에 Redis 이미지를 가져오는 것을 기록으로 남겨두어, 나중에는 잘 당겨오는 것을 목표로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0-1. 도커란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨테이너화 기술&lt;/b&gt;을 기반으로 한 플랫폼으로, 애플리케이션을 &lt;b&gt;컨테이너&lt;/b&gt;라는 격리된 환경에서 실행할 수 있도록 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 애플리케이션과 그 의존성, 라이브러리, 환경 설정 등을 포함한 &lt;b&gt;이미지&lt;/b&gt;를 만들고, 이를 실행할 수 있는 &lt;b&gt;컨테이너&lt;/b&gt;라는 단위로 구동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0-2. 컨테이너란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 이미지에서 만들어진 독립적인 실행 환경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, Redis 이미지를 기반으로 생성된 컨테이너는 Redis 서버만 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0-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; * 오늘 설치할 Redis 이미지는 Redis 서버가 실행될 수 있도록 필요한 모든 파일과 환경을 갖추고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 사용자는 Redis 서버를 직접 설치하거나 환경을 설정할 필요 없이, Docker를 이용하여 손쉽게 Redis를 실행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0-4. Docker 이미지 다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Hub에서 Redis 이미지를 로컬 시스템의 Docker 엔진으로 다운로드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Docker Hub: Docker 이미지를 저장하고 공유하는 &lt;b&gt;클라우드 저장소&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Docker Engine: Docker 엔진은 Docker를 실행하기 위한 백엔드 소프트웨어입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너를 실행하고 관리하는 역할을 하며, Docker 이미지를 다운로드하거나 새로운 이미지를 빌드할 수 있는 기능도 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736046135888&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker pull redis:version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;1. 이미지 다운로드: 중앙 저장소(Docker Hub)에서 Redis 이미지를 가져옵니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 로컬 저장소: 이미지는 로컬 Docker 엔진의 로컬 이미지 저장소(Docker가 설치된 현재 컴퓨터)에 저장됩니다.&amp;nbsp;&lt;br /&gt;3. 컨테이너 실행: Redis 이미지를 다운로드한 후, docker run 명령어를 사용하여 Redis 이미지를 기반으로 컨테이너를 실행합니다. 컨테이너는 이미지에서 실행되는 독립된 환경이며, 이를 통해 Redis 서버가 작동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Docker Hub 접속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hub.docker.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hub.docker.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736046352787&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Docker Hub Container Image Library | App Containerization&quot; data-og-description=&quot;Increase your reach and adoption on Docker Hub With a Docker Verified Publisher subscription, you'll increase trust, boost discoverability, get exclusive data insights, and much more.&quot; data-og-host=&quot;hub.docker.com&quot; data-og-source-url=&quot;https://hub.docker.com/&quot; data-og-url=&quot;https://hub.docker.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/koNWt/hyXWx6obhd/P4OwLFAykkTQKnSkbx6ST1/img.png?width=3372&amp;amp;height=1896&amp;amp;face=0_0_3372_1896,https://scrap.kakaocdn.net/dn/bqWrVZ/hyXWyEdd3D/7SpT1CarSViFk0N3OcfAn1/img.png?width=2560&amp;amp;height=1030&amp;amp;face=0_0_2560_1030,https://scrap.kakaocdn.net/dn/bMN25s/hyXWyqF1yf/imjSj7b11kypkaOirohJbK/img.png?width=980&amp;amp;height=515&amp;amp;face=0_0_980_515&quot;&gt;&lt;a href=&quot;https://hub.docker.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hub.docker.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/koNWt/hyXWx6obhd/P4OwLFAykkTQKnSkbx6ST1/img.png?width=3372&amp;amp;height=1896&amp;amp;face=0_0_3372_1896,https://scrap.kakaocdn.net/dn/bqWrVZ/hyXWyEdd3D/7SpT1CarSViFk0N3OcfAn1/img.png?width=2560&amp;amp;height=1030&amp;amp;face=0_0_2560_1030,https://scrap.kakaocdn.net/dn/bMN25s/hyXWyqF1yf/imjSj7b11kypkaOirohJbK/img.png?width=980&amp;amp;height=515&amp;amp;face=0_0_980_515');&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;Docker Hub Container Image Library | App Containerization&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Increase your reach and adoption on Docker Hub With a Docker Verified Publisher subscription, you'll increase trust, boost discoverability, get exclusive data insights, and much more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hub.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1379&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkwWgT/btsLDUzMGXX/sMC7fdsykf66cXQ0XtrnYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkwWgT/btsLDUzMGXX/sMC7fdsykf66cXQ0XtrnYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkwWgT/btsLDUzMGXX/sMC7fdsykf66cXQ0XtrnYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkwWgT%2FbtsLDUzMGXX%2FsMC7fdsykf66cXQ0XtrnYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1379&quot; height=&quot;228&quot; data-origin-width=&quot;1379&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인도 이미지를 공유할 수 있기때문에, Docker Official Image가 붙은 Redis를 찾습니다.&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;1864&quot; data-origin-height=&quot;795&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNbt1I/btsLDNOrZoS/TyiPQ3MuWJETcTx4urTTmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNbt1I/btsLDNOrZoS/TyiPQ3MuWJETcTx4urTTmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNbt1I/btsLDNOrZoS/TyiPQ3MuWJETcTx4urTTmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNbt1I%2FbtsLDNOrZoS%2FTyiPQ3MuWJETcTx4urTTmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1864&quot; height=&quot;795&quot; data-origin-width=&quot;1864&quot; data-origin-height=&quot;795&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원하는 버전을 확인하고, 오른쪽 상단의 docker full redis를 복사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 명령어는 &lt;b&gt;docker ull redis:버전&lt;/b&gt;이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Docker Desktop 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정⚙️ &amp;rarr; General &amp;rarr; Use the WSL2 based engine이 체크됐는지 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;설정⚙️ &amp;rarr; Resources&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr;&lt;span&gt; WSL Integration &amp;rarr;&amp;nbsp; Enable integration with my default WSL distro가 체크됐는지 확인&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Docker Desktop과 &lt;b&gt;Windows Subsystem for Linux (WSL)&lt;/b&gt; 간의 통합을 활성화하는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 이를 통해 Docker는 Windows에서 직접 실행되지 않고, WSL 2 기반의 Linux 커널 위에서 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; * WSL 환경에서 Docker 명령어(docker run, docker ps 등)를 실행할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; * WSL 내에서 Linux 명령어를 사용해 Docker 작업을 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. cmd&lt;/p&gt;
&lt;pre id=&quot;code_1736047201006&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Docker 버전 확인, Docker가 정상적으로 설치 되었는지 확인
docker -v

# 현재 실행중인 container가 존재하는지 확인
docker ps

# Redis 6.2 이미지 다운로드
docker pull redis:6.2

# 다운로드 받은 이미지 확인
docker images

# Redis 이미지를 실행하여 Redis 서버를 컨테이너 안에서 구동
docker run --rm -d -p 6379:6379 -it redis:6.2
# --rm: 컨테이너를 끈 뒤에 자동으로 삭제
# -d: 컨테이너를 실행하면서 해당 컨테이너는 수동으로 중단하지 않는 이상 계속 백그라운드에서 실행된 상태를 유지
# - p 6379:6379
# Docker Container는 외부 네트워크에 대해 기본적으로 격리된 상태
# 로컬 개발 환경에서 컨테이너로 연결하기 위해 host와 container port를 매필해야 함
# Redis 6.2 컨테이너가 로컬 환경 포트 6379에서 docker의 6379 포트로 매핑하여 실행
# -it: 터미널 창에서 Redis 명령을 직접 실행할 수 있게 함

# docker ps로 Container ID 를 확인 후, Redis client에 접속하여 작업 환경이 정상적으로 이루어졌는지 확인
docker exec -it {Container ID} redis-cli

# docker ps로 Container ID 를 확인 후, 컨테이너 종료
docker kill {Container ID}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@juno0713/Windows-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-Docker%EC%97%90-Redis-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@juno0713/Windows-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-Docker%EC%97%90-Redis-%EC%84%A4%EC%B9%98&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736047012614&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;Windows 환경에서 Docker에 Redis 설치&quot; data-og-description=&quot;윈도우즈 환경에 docker를 통해 redis를 설치해보자.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@juno0713/Windows-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-Docker%EC%97%90-Redis-%EC%84%A4%EC%B9%98&quot; data-og-url=&quot;https://velog.io/@juno0713/Windows-환경에서-Docker에-Redis-설치&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hiYjO/hyXWAaY1xf/WkChgYwuKAbI25cheawGF0/img.png?width=3480&amp;amp;height=1888&amp;amp;face=0_0_3480_1888,https://scrap.kakaocdn.net/dn/c0dftp/hyXWw0IGdm/ZeOGksVNtPyjCKszVR1kd0/img.png?width=3480&amp;amp;height=1888&amp;amp;face=0_0_3480_1888,https://scrap.kakaocdn.net/dn/TYwU3/hyXWuBOOif/eb2xMkqXXnolKefWzH7sC0/img.png?width=3480&amp;amp;height=1888&amp;amp;face=0_0_3480_1888&quot;&gt;&lt;a href=&quot;https://velog.io/@juno0713/Windows-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-Docker%EC%97%90-Redis-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@juno0713/Windows-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-Docker%EC%97%90-Redis-%EC%84%A4%EC%B9%98&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hiYjO/hyXWAaY1xf/WkChgYwuKAbI25cheawGF0/img.png?width=3480&amp;amp;height=1888&amp;amp;face=0_0_3480_1888,https://scrap.kakaocdn.net/dn/c0dftp/hyXWw0IGdm/ZeOGksVNtPyjCKszVR1kd0/img.png?width=3480&amp;amp;height=1888&amp;amp;face=0_0_3480_1888,https://scrap.kakaocdn.net/dn/TYwU3/hyXWuBOOif/eb2xMkqXXnolKefWzH7sC0/img.png?width=3480&amp;amp;height=1888&amp;amp;face=0_0_3480_1888');&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;Windows 환경에서 Docker에 Redis 설치&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;윈도우즈 환경에 docker를 통해 redis를 설치해보자.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jindevelopetravel0919.tistory.com/391&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jindevelopetravel0919.tistory.com/391&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736047016468&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;[Redis] Docker 환경에서 Redis 설치 - (로컬)&quot; data-og-description=&quot;Redis를 설치할 경우 크게 로컬 환경, 배포 서버 환경, Docker 환경 이 세 가지 환경에서 설치하는 것으로 구분할 수 있는데, 이전에는 AWS EC2 환경에서 설치하는 과정을 정리해보았다면, 이번에는 Doc&quot; data-og-host=&quot;jindevelopetravel0919.tistory.com&quot; data-og-source-url=&quot;https://jindevelopetravel0919.tistory.com/391&quot; data-og-url=&quot;https://jindevelopetravel0919.tistory.com/391&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cBP36k/hyXWyRLn6x/Eh9JUIuRgin66HspGDUnRK/img.png?width=800&amp;amp;height=313&amp;amp;face=0_0_800_313,https://scrap.kakaocdn.net/dn/sC7uK/hyXWx6ojPc/rw6aMJkAZ1G2WamFl0Uzj1/img.png?width=800&amp;amp;height=313&amp;amp;face=0_0_800_313,https://scrap.kakaocdn.net/dn/bmZXsC/hyXWmRj3kI/6JgmK5UquhYc5UsrKzfdn1/img.png?width=1537&amp;amp;height=914&amp;amp;face=0_0_1537_914&quot;&gt;&lt;a href=&quot;https://jindevelopetravel0919.tistory.com/391&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jindevelopetravel0919.tistory.com/391&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cBP36k/hyXWyRLn6x/Eh9JUIuRgin66HspGDUnRK/img.png?width=800&amp;amp;height=313&amp;amp;face=0_0_800_313,https://scrap.kakaocdn.net/dn/sC7uK/hyXWx6ojPc/rw6aMJkAZ1G2WamFl0Uzj1/img.png?width=800&amp;amp;height=313&amp;amp;face=0_0_800_313,https://scrap.kakaocdn.net/dn/bmZXsC/hyXWmRj3kI/6JgmK5UquhYc5UsrKzfdn1/img.png?width=1537&amp;amp;height=914&amp;amp;face=0_0_1537_914');&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;[Redis] Docker 환경에서 Redis 설치 - (로컬)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Redis를 설치할 경우 크게 로컬 환경, 배포 서버 환경, Docker 환경 이 세 가지 환경에서 설치하는 것으로 구분할 수 있는데, 이전에는 AWS EC2 환경에서 설치하는 과정을 정리해보았다면, 이번에는 Doc&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jindevelopetravel0919.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/976</guid>
      <comments>https://hj0216.tistory.com/976#entry976comment</comments>
      <pubDate>Sun, 5 Jan 2025 12:29:08 +0900</pubDate>
    </item>
    <item>
      <title>[대기열 시스템] Windows Docker Desktop 설치</title>
      <link>https://hj0216.tistory.com/975</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhpdHm/btsLDc2bIdu/m5gBBLarhKinZZyJjAOKAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhpdHm/btsLDc2bIdu/m5gBBLarhKinZZyJjAOKAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhpdHm/btsLDc2bIdu/m5gBBLarhKinZZyJjAOKAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhpdHm%2FbtsLDc2bIdu%2Fm5gBBLarhKinZZyJjAOKAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전, 사실 안녕하지 못했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마실가실 리팩토링을 로그인 부분을 마치고, 개인적으로 해보고 싶었던 공부가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째가 클라우드, 두번째가 테이블 설계, 세번째가 간단한 프로젝트 만들어서 완성해 보기..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 설계는 강의를 들으면서 개념을 좀 잡았고(조만간 정리해서 글을 올릴 예정입니다), 간단한 프로젝트는 커피 월드컵이 있었습니다. 남은 건 프리 티어가 끝난 제 계정과 클라우드였는데, 자주 접해보지 못해서 이참에 모델링처럼 강의를 들으며 공부하자라고 생각했었습니다.&lt;/p&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 Desktop 설치가 안되는데, 심지어 노트북이 잠시 먹통되서 눈물이 그렁그렁했었습니다. 노트북이 저에게 얼마나 소중했는지, 방금 깨달았습니다. 이 노트북을 못쓰게 될 수도 있다고 생각하니 이제야 소중함을.. 이래서 있을 때 잘해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행이 Windows에 Docker Desktop도 설치하고 놑북도 돌아왔기에 그 과정을 짧게 정리해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0. Docker Container란,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는&amp;nbsp;컨테이너라는&amp;nbsp;기술을&amp;nbsp;사용하여&amp;nbsp;애플리케이션을&amp;nbsp;독립적으로&amp;nbsp;실행할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;환경을&amp;nbsp;제공합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 원래 리눅스 환경에서 잘 동작하도록 설계되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Desktop은 Windows와 macOS 환경에서 이 리눅스 기반의 Docker를 실행할 수 있게 도와주는 프로그램입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;안될 수 있으니까, 1번은 긴장하기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 윈도우 버전 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 &amp;rarr; 시스템 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr;&lt;span&gt; 정보 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr;&lt;span&gt; Window 사양&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blq6ro/btsLCFDJYFC/DVjtJWRLK7mf5Ay1hUdod1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blq6ro/btsLCFDJYFC/DVjtJWRLK7mf5Ay1hUdod1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blq6ro/btsLCFDJYFC/DVjtJWRLK7mf5Ay1hUdod1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblq6ro%2FbtsLCFDJYFC%2FDVjtJWRLK7mf5Ay1hUdod1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;638&quot; height=&quot;120&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/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;전 Windows 11 Home을 쓰고 있습니다.&lt;/p&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를&amp;nbsp;사용할&amp;nbsp;때&amp;nbsp;Windows&amp;nbsp;Home&amp;nbsp;Edition과&amp;nbsp;Windows&amp;nbsp;Pro&amp;nbsp;Edition의&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;차이는&amp;nbsp;Hyper-V&amp;nbsp;기능의&amp;nbsp;지원여부입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker&amp;nbsp;Desktop은&amp;nbsp;기본적으로&amp;nbsp;Hyper-V&amp;nbsp;기능을&amp;nbsp;사용하기&amp;nbsp;때문에&amp;nbsp;Windows&amp;nbsp;Pro&amp;nbsp;에디션에서만&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있었습니다. &lt;br /&gt;2020년&amp;nbsp;5월에&amp;nbsp;WSL2가&amp;nbsp;정식&amp;nbsp;릴리스되었다는&amp;nbsp;점입니다.&amp;nbsp; &lt;br /&gt;WSL2는&amp;nbsp;Windows&amp;nbsp;Home에서도&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있어,&amp;nbsp;WSL2를&amp;nbsp;기반으로&amp;nbsp;Docker&amp;nbsp;Desktop을&amp;nbsp;사용하는&amp;nbsp;것이&amp;nbsp;가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Hyper-V: Windows에서 가상 머신(Virtual Machine)을 실행할 수 있도록 도와주는 기술(완전히&amp;nbsp;독립된&amp;nbsp;리눅스&amp;nbsp;운영체제를&amp;nbsp;설치해서&amp;nbsp;실행하는&amp;nbsp;방식)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * WSL2(Windows Subsystem for Linux 2): Windows 안에서 Linux(리눅스) 환경을 사용할 수 있게 해주는 기능(Windows&amp;nbsp;안에&amp;nbsp;리눅스&amp;nbsp;커널을&amp;nbsp;포함해서,&amp;nbsp;리눅스&amp;nbsp;앱과&amp;nbsp;명령어를&amp;nbsp;실행할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;환경을&amp;nbsp;제공)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전 Home이기에 WSL2를 사용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. PowerShell 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우&amp;nbsp;10&amp;nbsp;버전&amp;nbsp;2004(빌드&amp;nbsp;19041&amp;nbsp;이상)이나&amp;nbsp;윈도우&amp;nbsp;11에는&amp;nbsp;기본적으로&amp;nbsp;wsl&amp;nbsp;명령어가&amp;nbsp;포함되어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Powershell을 &lt;b&gt;관리자 모드&lt;/b&gt;로 열어서 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735997458665&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# WSL 기능을 Windows에 활성화
# 리눅스 환경을 실행할 수 있는 기본 기능(WSL1)을 활성화
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

# VirtualMachinePlatform 기능 활성화
# WSL2가 리눅스 커널을 실행할 수 있도록 지원하는 필수 기능
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

# WSL2와 기본 리눅스 배포판(Ubuntu)을 설치
wsl --install

# 리눅스 배포판을 설치할 때 WSL2를 기본으로 사용하도록 설정
# Docker 자체는 Windows에서 실행되지만, 컨테이너 내부에서는 리눅스 배포판이 작동하기 때문에 이를 지원하기 위한 WSL2 설정이 필요
wsl --set-default-version 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; wsl --install 시, 서버실행이 실패했습니다라는 오류가 발생할 경우, 아래 영상을 따라합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmd에서 동작이 안되서 껐다 킨 후, 모니터가 안켜지는 위험이 발생했지만, 3번째 껐다키니 모니터가 돌아왔습니다..&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=RAg0VadDgH4&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/cvTdt7/hyXWAWeNUV/Bby3qP2C22qtTNnyAigSqk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/sOS4h/hyXWAvaqb0/c2luY3G70nUgA22rhsq5k0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;Fix Server Execution Failed Error in WSL in Windows 11/10 [Tutorial]&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/RAg0VadDgH4&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Docker Desktop 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.docker.com/products/docker-desktop/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735998101083&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;Docker Desktop: The #1 Containerization Tool for Developers | Docker&quot; data-og-description=&quot;Docker Desktop is collaborative containerization software for developers. Get started and download Docker Desktop today on Mac, Windows, or Linux.&quot; data-og-host=&quot;www.docker.com&quot; data-og-source-url=&quot;https://www.docker.com/products/docker-desktop/&quot; data-og-url=&quot;https://www.docker.com/products/docker-desktop/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b2UMq6/hyXWAWaBD6/2Wk2Dto0kjTaPwXgkPasr1/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/bNtYDh/hyXWsX7HKh/cWsw7bGjBwsAL7pcAI3xOk/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580&quot;&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.docker.com/products/docker-desktop/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b2UMq6/hyXWAWaBD6/2Wk2Dto0kjTaPwXgkPasr1/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/bNtYDh/hyXWsX7HKh/cWsw7bGjBwsAL7pcAI3xOk/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580');&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;Docker Desktop: The #1 Containerization Tool for Developers | Docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docker Desktop is collaborative containerization software for developers. Get started and download Docker Desktop today on Mac, Windows, or Linux.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Window 설정은 ADM, ARM 2가지 버전이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARM64 버전과 AMD64 버전은 CPU 아키텍처(CPU가&amp;nbsp;데이터를&amp;nbsp;처리하고&amp;nbsp;명령을&amp;nbsp;실행하는&amp;nbsp;방식과&amp;nbsp;구조)를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 정보 &amp;rarr; 시스템 종류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * x64 기반 PC: AMD64 버전 선택&lt;br /&gt;&amp;nbsp; * ARM64 기반 PC: ARM64 버전 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의&amp;nbsp;Windows&amp;nbsp;PC는&amp;nbsp;AMD64&amp;nbsp;CPU를&amp;nbsp;사용하며,&amp;nbsp;일반적으로&amp;nbsp;Microsoft&amp;nbsp;Surface&amp;nbsp;Pro&amp;nbsp;X와&amp;nbsp;같은&amp;nbsp;특정&amp;nbsp;장치가&amp;nbsp;ARM&amp;nbsp;CPU를&amp;nbsp;사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  Docker Desktop을 설치하면, Ahn Lab에서 hosts 파일이 변경되었다며, 이전으로 복구하겠냐는 물음이 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, hosts 파일 보기를 클릭하면, docker desktop이 추가된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hosts 파일은 컴퓨터에서 도메인 이름을 IP 주소로 매핑하는 파일로, 일반적으로&amp;nbsp;DNS(Domain&amp;nbsp;Name&amp;nbsp;System)가&amp;nbsp;이&amp;nbsp;작업을&amp;nbsp;처리하지만,&amp;nbsp;hosts&amp;nbsp;파일을&amp;nbsp;통해&amp;nbsp;특정&amp;nbsp;도메인&amp;nbsp;이름을&amp;nbsp;수동으로&amp;nbsp;IP&amp;nbsp;주소에&amp;nbsp;연결할&amp;nbsp;수도&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Desktop은 리눅스 환경인 WSL2를 사용해 컨테이너를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때,&amp;nbsp;Windows와&amp;nbsp;WSL2&amp;nbsp;간의&amp;nbsp;원활한&amp;nbsp;연결을&amp;nbsp;위해&amp;nbsp;IP&amp;nbsp;주소를&amp;nbsp;매핑하는&amp;nbsp;작업이&amp;nbsp;필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&amp;nbsp;hosts&amp;nbsp;파일에&amp;nbsp;정보를&amp;nbsp;추가하여,&amp;nbsp;Windows에서&amp;nbsp;리눅스&amp;nbsp;환경으로&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;도와줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 돌아가면 안된다는 이야기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hosts 파일 위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * C:\Windows\System32\drivers\etc&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Chat GPT&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://myanjini.tistory.com/entry/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90-%EB%8F%84%EC%BB%A4-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://myanjini.tistory.com/entry/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90-%EB%8F%84%EC%BB%A4-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91-%EC%84%A4%EC%B9%98&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735996617340&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;윈도우에 도커 데스크탑 설치&quot; data-og-description=&quot;Install Docker Desktop on Windows Docker Desktop은 컨테이너화된 애플리케이션 및 마이크로서비스를 구축하고 공유할 수 있는 Mac, Linux, Windows 환경용 원클릭 설치 애플리케이션으로, 설치된 머신에서 컨테&quot; data-og-host=&quot;myanjini.tistory.com&quot; data-og-source-url=&quot;https://myanjini.tistory.com/entry/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90-%EB%8F%84%EC%BB%A4-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91-%EC%84%A4%EC%B9%98&quot; data-og-url=&quot;https://myanjini.tistory.com/entry/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90-%EB%8F%84%EC%BB%A4-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91-%EC%84%A4%EC%B9%98&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/boCS2e/hyXWohaHP8/vKR0dgvDEtzgpacFx4DR11/img.png?width=800&amp;amp;height=453&amp;amp;face=0_0_800_453,https://scrap.kakaocdn.net/dn/c28ff4/hyXWsjxj1K/YYjDN8iVxIPDkxfvmAv3G1/img.png?width=800&amp;amp;height=453&amp;amp;face=0_0_800_453,https://scrap.kakaocdn.net/dn/IFejl/hyXWuPge2L/bI4AOBUjkr3KxQ5dFrtHZ0/img.png?width=1270&amp;amp;height=720&amp;amp;face=0_0_1270_720&quot;&gt;&lt;a href=&quot;https://myanjini.tistory.com/entry/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90-%EB%8F%84%EC%BB%A4-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://myanjini.tistory.com/entry/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90-%EB%8F%84%EC%BB%A4-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91-%EC%84%A4%EC%B9%98&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/boCS2e/hyXWohaHP8/vKR0dgvDEtzgpacFx4DR11/img.png?width=800&amp;amp;height=453&amp;amp;face=0_0_800_453,https://scrap.kakaocdn.net/dn/c28ff4/hyXWsjxj1K/YYjDN8iVxIPDkxfvmAv3G1/img.png?width=800&amp;amp;height=453&amp;amp;face=0_0_800_453,https://scrap.kakaocdn.net/dn/IFejl/hyXWuPge2L/bI4AOBUjkr3KxQ5dFrtHZ0/img.png?width=1270&amp;amp;height=720&amp;amp;face=0_0_1270_720');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;윈도우에 도커 데스크탑 설치&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Install Docker Desktop on Windows Docker Desktop은 컨테이너화된 애플리케이션 및 마이크로서비스를 구축하고 공유할 수 있는 Mac, Linux, Windows 환경용 원클릭 설치 애플리케이션으로, 설치된 머신에서 컨테&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;myanjini.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.lainyzine.com/ko/article/a-complete-guide-to-how-to-install-docker-desktop-on-windows-10/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.lainyzine.com/ko/article/a-complete-guide-to-how-to-install-docker-desktop-on-windows-10/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735996643204&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;윈도우 Docker 설치 완벽 가이드(Home 포함)&quot; data-og-description=&quot;Docker는 경량 가상화 기술인 리눅스 컨테이너 도구입니다. 윈도우 Home과 Pro에서도 몇 가지 설정을 통해 Docker Desktop을 사용할 수 있습니다. 이 글에서는 윈도우에서 Docker Desktop을 설치하는 방법을 &quot; data-og-host=&quot;www.lainyzine.com&quot; data-og-source-url=&quot;https://www.lainyzine.com/ko/article/a-complete-guide-to-how-to-install-docker-desktop-on-windows-10/&quot; data-og-url=&quot;https://www.lainyzine.com/ko/article/a-complete-guide-to-how-to-install-docker-desktop-on-windows-10/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FXVVT/hyXWsKFNuV/IzoBegBxzY44q95WrW4G6K/img.png?width=1430&amp;amp;height=1000&amp;amp;face=0_0_1430_1000,https://scrap.kakaocdn.net/dn/bD9e0i/hyXWoaqPrI/KV4EB3tPD0kJfsN5RkCg81/img.png?width=1500&amp;amp;height=849&amp;amp;face=0_0_1500_849,https://scrap.kakaocdn.net/dn/eA0YDM/hyXWzbXt8k/HpdaqhwUyboYh8080X7rGK/img.png?width=1500&amp;amp;height=782&amp;amp;face=0_0_1500_782&quot;&gt;&lt;a href=&quot;https://www.lainyzine.com/ko/article/a-complete-guide-to-how-to-install-docker-desktop-on-windows-10/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.lainyzine.com/ko/article/a-complete-guide-to-how-to-install-docker-desktop-on-windows-10/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FXVVT/hyXWsKFNuV/IzoBegBxzY44q95WrW4G6K/img.png?width=1430&amp;amp;height=1000&amp;amp;face=0_0_1430_1000,https://scrap.kakaocdn.net/dn/bD9e0i/hyXWoaqPrI/KV4EB3tPD0kJfsN5RkCg81/img.png?width=1500&amp;amp;height=849&amp;amp;face=0_0_1500_849,https://scrap.kakaocdn.net/dn/eA0YDM/hyXWzbXt8k/HpdaqhwUyboYh8080X7rGK/img.png?width=1500&amp;amp;height=782&amp;amp;face=0_0_1500_782');&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;윈도우 Docker 설치 완벽 가이드(Home 포함)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docker는 경량 가상화 기술인 리눅스 컨테이너 도구입니다. 윈도우 Home과 Pro에서도 몇 가지 설정을 통해 Docker Desktop을 사용할 수 있습니다. 이 글에서는 윈도우에서 Docker Desktop을 설치하는 방법을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.lainyzine.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://answers.microsoft.com/ko-kr/windows/forum/all/wsl-%EC%84%A4%EC%B9%98-%EC%8B%9C%EC%97%90/479d52f8-7a59-4fee-bcf2-6ebb56c3d3ee&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://answers.microsoft.com/ko-kr/windows/forum/all/wsl-%EC%84%A4%EC%B9%98-%EC%8B%9C%EC%97%90/479d52f8-7a59-4fee-bcf2-6ebb56c3d3ee&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735997833285&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;리디렉션 중&quot; data-og-description=&quot;&quot; data-og-host=&quot;login.microsoftonline.com&quot; data-og-source-url=&quot;https://answers.microsoft.com/ko-kr/windows/forum/all/wsl-%EC%84%A4%EC%B9%98-%EC%8B%9C%EC%97%90/479d52f8-7a59-4fee-bcf2-6ebb56c3d3ee&quot; data-og-url=&quot;https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=a81d90ac-aa75-4cf8-b14c-58bf348528fe&amp;amp;redirect_uri=https%3A%2F%2Fanswers.microsoft.com&amp;amp;response_type=code%20id_token&amp;amp;scope=openid%20profile&amp;amp;state=OpenIdConnect.AuthenticationProperties%3Dk2oecej-AMyd3jPT3p6Q0VI6yw5AGggD8yEXL2EhST74uOM908Ase9-sMUmRxBiSLMxv99pNLxL54JS0C0rZXIhMkkfK8nfFXGUc45TU60Ldqr3sVk3jb5rgG1qF_0njOxSM8_8FYSbV_jP348k1qjgohSUM7aj-GkIxHxVqgEZtmB-PwjezlZab_vSsuJgMedypyNNlVQV0271aoq9Hw6dGYg4eAFVrwvl569KLJetj5LIfujDI1w7qGHZM9R4BGEs6madbSUDP6mVHn8huiQjWs4_gJKusaLNa0la_Bl_yA6sWZyTDjaGZt7PzFDPTLb28S-M_pU7WHilsTT29NykQZCLz2q4l5vVzpusFxfXKMk2Vtjrfxr1Zc1FL9LFd&amp;amp;response_mode=form_post&amp;amp;nonce=638715946399430743.YTBlYWUyZjEtZmYwMi00YTVmLTgwMTUtY2Y2ZTc4OWUxZjU4MDYxOTZmNDItYWZiOC00NjAzLWE5YmItOWM3YzViZWNhMzMy&amp;amp;nopa=2&amp;amp;prompt=none&amp;amp;x-client-SKU=ID_NET472&amp;amp;x-client-ver=7.6.0.0&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://answers.microsoft.com/ko-kr/windows/forum/all/wsl-%EC%84%A4%EC%B9%98-%EC%8B%9C%EC%97%90/479d52f8-7a59-4fee-bcf2-6ebb56c3d3ee&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://answers.microsoft.com/ko-kr/windows/forum/all/wsl-%EC%84%A4%EC%B9%98-%EC%8B%9C%EC%97%90/479d52f8-7a59-4fee-bcf2-6ebb56c3d3ee&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;리디렉션 중&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;login.microsoftonline.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://customer.gabia.com/manual/hosting/2501/2507#:~:text=%E2%91%A0%20C%3A%5CWindows%5CSystem32,%EC%9D%84%20%EB%A7%88%EC%9A%B0%EC%8A%A4%20%EB%8D%94%EB%B8%94%20%ED%81%B4%EB%A6%AD%ED%95%A9%EB%8B%88%EB%8B%A4.&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://customer.gabia.com/manual/hosting/2501/2507#:~:text=%E2%91%A0%20C%3A%5CWindows%5CSystem32,%EC%9D%84%20%EB%A7%88%EC%9A%B0%EC%8A%A4%20%EB%8D%94%EB%B8%94%20%ED%81%B4%EB%A6%AD%ED%95%A9%EB%8B%88%EB%8B%A4.&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735998767171&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;가비아: 국내 1위&quot; data-og-description=&quot; &quot; data-og-host=&quot;customer.gabia.com&quot; data-og-source-url=&quot;https://customer.gabia.com/manual/hosting/2501/2507#:~:text=%E2%91%A0%20C%3A%5CWindows%5CSystem32,%EC%9D%84%20%EB%A7%88%EC%9A%B0%EC%8A%A4%20%EB%8D%94%EB%B8%94%20%ED%81%B4%EB%A6%AD%ED%95%A9%EB%8B%88%EB%8B%A4.&quot; data-og-url=&quot;https://customer.gabia.com/manual/hosting/2501/www.gabia.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Ahvkg/hyXWn3FBXh/1xcb7uMJrHCBI9K7lyVmnk/img.jpg?width=1200&amp;amp;height=1000&amp;amp;face=0_0_1200_1000&quot;&gt;&lt;a href=&quot;https://customer.gabia.com/manual/hosting/2501/2507#:~:text=%E2%91%A0%20C%3A%5CWindows%5CSystem32,%EC%9D%84%20%EB%A7%88%EC%9A%B0%EC%8A%A4%20%EB%8D%94%EB%B8%94%20%ED%81%B4%EB%A6%AD%ED%95%A9%EB%8B%88%EB%8B%A4.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://customer.gabia.com/manual/hosting/2501/2507#:~:text=%E2%91%A0%20C%3A%5CWindows%5CSystem32,%EC%9D%84%20%EB%A7%88%EC%9A%B0%EC%8A%A4%20%EB%8D%94%EB%B8%94%20%ED%81%B4%EB%A6%AD%ED%95%A9%EB%8B%88%EB%8B%A4.&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Ahvkg/hyXWn3FBXh/1xcb7uMJrHCBI9K7lyVmnk/img.jpg?width=1200&amp;amp;height=1000&amp;amp;face=0_0_1200_1000');&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;가비아: 국내 1위&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;customer.gabia.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/아이돌 티켓팅 접속자 대기열 시스템</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/975</guid>
      <comments>https://hj0216.tistory.com/975#entry975comment</comments>
      <pubDate>Sat, 4 Jan 2025 22:58:08 +0900</pubDate>
    </item>
    <item>
      <title>[커피 월드컵] React에서 사용한 기술 정리</title>
      <link>https://hj0216.tistory.com/974</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;coffee-worldcup.jpg&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A4SD6/btsLAbbmPmZ/4ACMZfQ6SKDREqITdhUyoK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A4SD6/btsLAbbmPmZ/4ACMZfQ6SKDREqITdhUyoK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A4SD6/btsLAbbmPmZ/4ACMZfQ6SKDREqITdhUyoK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA4SD6%2FbtsLAbbmPmZ%2F4ACMZfQ6SKDREqITdhUyoK%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;257&quot; height=&quot;437&quot; data-filename=&quot;coffee-worldcup.jpg&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1탄: &lt;a href=&quot;https://hj0216.tistory.com/972&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[커피 월드컵] Netlify React 배포&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2탄: &lt;a href=&quot;https://hj0216.tistory.com/973&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[커피&amp;nbsp;월드컵]&amp;nbsp;React&amp;nbsp;카카오톡&amp;nbsp;공유하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3탄: 현재 글!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React를 한땀 한땀, 살펴볼 예정은 아니지만 어떤 기술을 활용했는지 간단히 정리해두면 좋을 것 같아서 VS Code를 실행해서 복기해 보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 React가 완전 처음은 아니고, 기존에 팀 프로젝트에서 프론트 기술로 사용해본 경험이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 React를 하나도 몰라도 만들 수 있군! 보다는 React를 조금 알아도 만들 수 있군! 정도로 생각해 주시면 좋을 것 같습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찐 시작!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. React Hooks&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰지 않을 수 없는 React Hooks...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에 저는 useState, useEffect, useMemo를 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* useState&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 상태를&amp;nbsp;관리하는&amp;nbsp;훅 &lt;br /&gt;&amp;nbsp; * 현재 상태를 나타내는 state값과 이 상태를 변경하는 setState값을 한 쌍으로 제공&lt;/p&gt;
&lt;pre id=&quot;code_1735611336547&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [selected, setSelected] = useState({
	coffee: null,
	drink: null,
	dessert: null,
});&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;* useEffect&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 컴포넌트가 화면에 렌더링 될 때 마다 실행되는 효과(effect)를 구현할 수 있게 해주는 훅&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * useEffect(function,&amp;nbsp;deps)의&amp;nbsp;형태로&amp;nbsp;사용 &lt;br /&gt;&amp;nbsp; &amp;nbsp; * function: 실행시킬 함수, deps: 의존성 배열 &lt;br /&gt;&amp;nbsp; * 의존성 배열 없이 useEffect를 실행시키게 되면 페이지가 렌더링 될 때마다 데이터를 불러옴 &lt;br /&gt;&amp;nbsp; * 의존성 배열에 빈배열을 담아주게 될 경우에는 첫 렌더링 시에만 함수를 실행 &lt;br /&gt;&amp;nbsp; * 의존성 배열에 state값이나 props로 상속받은 데이터값 등을 담아주게 되면 해당값이 변할 때마다 함수를 실행&lt;/p&gt;
&lt;pre id=&quot;code_1735611580303&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    const shuffledItems = [...items].sort(() =&amp;gt; Math.random() - 0.5);
    setBrands(shuffledItems);
    setDisplays([shuffledItems[0], shuffledItems[1]]);
}, [])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*&amp;nbsp;useMemo &lt;br /&gt;&amp;nbsp;*&amp;nbsp;메모이제이션을&amp;nbsp;활용하여&amp;nbsp;성능&amp;nbsp;개선을&amp;nbsp;위해&amp;nbsp;많이&amp;nbsp;사용 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;메모이제이션:&amp;nbsp;기존에&amp;nbsp;수행한&amp;nbsp;연산의&amp;nbsp;결과&amp;nbsp;값을&amp;nbsp;어딘가에&amp;nbsp;저장해두었다가&amp;nbsp;동일한&amp;nbsp;입력이&amp;nbsp;들어오면&amp;nbsp;재활용하는&amp;nbsp;프로그래밍&amp;nbsp;기법 &lt;br /&gt;&amp;nbsp;&amp;nbsp;*&amp;nbsp;cf.&amp;nbsp;useMemo:&amp;nbsp;메모이제이션된&amp;nbsp;값을&amp;nbsp;반환하는&amp;nbsp;훅,&amp;nbsp;useCallback:&amp;nbsp;메모이제이션&amp;nbsp;된&amp;nbsp;함수를&amp;nbsp;반환하는&amp;nbsp;훅&lt;/p&gt;
&lt;pre id=&quot;code_1735611774734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const jsConfetti = useMemo(() =&amp;gt; new JSConfetti(), []);&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;+ useRef&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;*&amp;nbsp;특정&amp;nbsp;DOM에&amp;nbsp;접근하여&amp;nbsp;DOM&amp;nbsp;조작을&amp;nbsp;가능하게&amp;nbsp;하는&amp;nbsp;훅 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;리액트&amp;nbsp;프로젝트에서도&amp;nbsp;특정&amp;nbsp;요소를&amp;nbsp;선택해야하는&amp;nbsp;상황에&amp;nbsp;사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 상태처럼&amp;nbsp;값을&amp;nbsp;저장하지만&amp;nbsp;리렌더링을&amp;nbsp;트리거하지&amp;nbsp;않는&amp;nbsp;값으로&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;훅&lt;/p&gt;
&lt;pre id=&quot;code_1735612130748&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const inputRef = useRef(null);

// 특정 DOM에 접근
&amp;lt;input ref={inputRef} type=&quot;text&quot; /&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;2. styled component&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;css가 js에 들어가 있는 라이브러리인 styled component! 처음 써봤습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커피 월드컵 만드는 영상에서 styled component를 쓰시는 걸 보고 도전해봤는데, 생각보다 간편하고 좋았습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1735704326688&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ImageContainer, TitleImage, BrandImage } from &quot;./styleGameResult&quot;;

&amp;lt;ImageContainer&amp;gt;
    &amp;lt;TitleImage/&amp;gt;
    &amp;lt;BrandImage/&amp;gt;
&amp;lt;/ImageContainer&amp;gt;


export const ImageContainer = styled.div`
    position: relative;
    width: 100%;
    height: auto;
`;&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;3. React Router&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅(Routing): 웹 애플리케이션에서 사용자가 URL을 통해 페이지에 접근할 때 해당 URL에 따라 서로 다른 컴포넌트를 보여주는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; URL의&amp;nbsp;경로(path)와&amp;nbsp;쿼리(query)&amp;nbsp;부분을&amp;nbsp;분석하여&amp;nbsp;해당하는&amp;nbsp;컴포넌트를&amp;nbsp;렌더링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPA는&amp;nbsp;초기&amp;nbsp;로딩&amp;nbsp;시&amp;nbsp;서버에서&amp;nbsp;모든&amp;nbsp;컨텐츠를&amp;nbsp;불러오는&amp;nbsp;것이&amp;nbsp;아니라&amp;nbsp;필요한&amp;nbsp;데이터만&amp;nbsp;가져와&amp;nbsp;화면을&amp;nbsp;구성하기&amp;nbsp;때문에,&amp;nbsp;URL에&amp;nbsp;따라&amp;nbsp;다른&amp;nbsp;화면을&amp;nbsp;보여주는&amp;nbsp;라우팅이&amp;nbsp;필요&lt;/p&gt;
&lt;pre id=&quot;code_1735706094722&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  return (
    &amp;lt;Router&amp;gt;
      &amp;lt;GlobalStyle /&amp;gt;
      &amp;lt;Routes&amp;gt;
        &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Game /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/result&quot; element={&amp;lt;ResultPage /&amp;gt;} /&amp;gt;
      &amp;lt;/Routes&amp;gt;
    &amp;lt;/Router&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;Route를 통해 여러 페이지를 만들어주게 되면, 페이지간 이동을 위해 Link와 useNevigate를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Link&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * html에서의 &amp;lt;a&amp;gt;와 유사하게 클릭 이벤트를 통해 특정 경로로 이동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 주로 사용자 인터페이스(UI)에서 네비게이션 링크를 생성할 때 사용&lt;/p&gt;
&lt;pre id=&quot;code_1735706778304&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Link } from &quot;react-router-dom&quot;;

function HomePage() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;홈페이지&amp;lt;/h1&amp;gt;
      &amp;lt;Link to=&quot;/about&quot;&amp;gt;About 페이지로 이동&amp;lt;/Link&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * useNaivgate&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 이벤트&amp;nbsp;핸들러&amp;nbsp;내부에서&amp;nbsp;동적으로&amp;nbsp;페이지를&amp;nbsp;이동할&amp;nbsp;때&amp;nbsp;사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 논리적으로 경로를 제어해야 하는 경우에 사용&lt;/p&gt;
&lt;pre id=&quot;code_1735706803720&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useNavigate } from &quot;react-router-dom&quot;;

function LoginPage() {
  const navigate = useNavigate();

  const handleLogin = () =&amp;gt; {
    // 로그인 로직 처리 후 페이지 이동
    navigate(&quot;/dashboard&quot;);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;로그인 페이지&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={handleLogin}&amp;gt;로그인&amp;lt;/button&amp;gt;
    &amp;lt;/div&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;* URLSearchParams&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 웹의&amp;nbsp;URL&amp;nbsp;쿼리&amp;nbsp;문자열을&amp;nbsp;다루는&amp;nbsp;JavaScript의&amp;nbsp;내장&amp;nbsp;클래스&lt;/p&gt;
&lt;pre id=&quot;code_1735707048232&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const queryParams = new URLSearchParams({
    coffee: selected.coffee,
    drink: selected.drink,
    dessert: selected.dessert,
    brand: brand.brand,
}).toString();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * search.location&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 웹&amp;nbsp;브라우저의&amp;nbsp;현재&amp;nbsp;URL에서&amp;nbsp;쿼리&amp;nbsp;문자열을&amp;nbsp;나타내는&amp;nbsp;속성,&amp;nbsp;문자열&amp;nbsp;형태로&amp;nbsp;쿼리&amp;nbsp;문자열(Query&amp;nbsp;String)을&amp;nbsp;나타냄&lt;/p&gt;
&lt;pre id=&quot;code_1735707122545&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const searchParams = new URLSearchParams(location.search);
const selected = {
    coffee: searchParams.get(&quot;coffee&quot;),
    drink: searchParams.get(&quot;drink&quot;),
    dessert: searchParams.get(&quot;dessert&quot;),
};
const brand = searchParams.get(&quot;brand&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  개선할 수 있는 사항&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 값을 get을 통해 지정해야 하는 것과 모든 브라우저에서 지원해주지 않는다는 단점을 가지고 있기 때문에 npm i query-string 을 사용해 해당 단점들을 해결할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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. JSConfetti&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;쉽게 구현할 수 있는 방법과 마지막 옵션 선택에 따라 이모지로 변경할 수 있다는 장점을 기반으로 저는 JSConfetti를 사용했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEAd7M/btsLCDLkk3G/Ejka9Y1YXhsMLhSZLzKMHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEAd7M/btsLCDLkk3G/Ejka9Y1YXhsMLhSZLzKMHk/img.png&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;816&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.9861%; margin-right: 10px;&quot; data-widthpercent=&quot;49.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEAd7M/btsLCDLkk3G/Ejka9Y1YXhsMLhSZLzKMHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEAd7M%2FbtsLCDLkk3G%2FEjka9Y1YXhsMLhSZLzKMHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1468&quot; height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNArK4/btsLCtILV4y/eR9cmwyMuFDBStqPoxSrZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNArK4/btsLCtILV4y/eR9cmwyMuFDBStqPoxSrZ0/img.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;721&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.8512%;&quot; data-widthpercent=&quot;50.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNArK4/btsLCtILV4y/eR9cmwyMuFDBStqPoxSrZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNArK4%2FbtsLCtILV4y%2FeR9cmwyMuFDBStqPoxSrZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1320&quot; height=&quot;721&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  커피월드컵 주소  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hj0216.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hj0216.netlify.app/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735707537071&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Coffee WorldCup&quot; data-og-description=&quot;&quot; data-og-host=&quot;hj0216.netlify.app&quot; data-og-source-url=&quot;https://hj0216.netlify.app/&quot; data-og-url=&quot;https://hj0216.netlify.app/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://hj0216.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hj0216.netlify.app/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Coffee WorldCup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hj0216.netlify.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Chat GPT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://choijying21.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0React-Hooks%EB%9E%80#4)%20useMemo%2C%20useCallback-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://choijying21.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0React-Hooks%EB%9E%80#4)%20useMemo%2C%20useCallback-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735611382276&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;[React] 리액트 훅에 대해 알아보기(React Hooks란?)&quot; data-og-description=&quot;1. React Hooks란? 리액트 훅은 리액트 클래스형 컴포넌트에서 이용하던 코드를 작성할 필요없이 함수형 컴포넌트에서 다양한 기능을 사용할 수 있게 만들어준 라이브러리라고 할 수 있는데 React 16.&quot; data-og-host=&quot;choijying21.tistory.com&quot; data-og-source-url=&quot;https://choijying21.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0React-Hooks%EB%9E%80#4)%20useMemo%2C%20useCallback-1&quot; data-og-url=&quot;https://choijying21.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0React-Hooks%EB%9E%80&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cpMYrN/hyXSvhcWRx/6YD2BiO8bfstnyxdumTv0k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/roqAh/hyXWrc8CON/trzkS2ULO83tjkaGlkKt4K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bNbeDq/hyXWBGPitw/RjW0J90dkao8rOGkUmwukk/img.png?width=750&amp;amp;height=750&amp;amp;face=0_0_750_750&quot;&gt;&lt;a href=&quot;https://choijying21.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0React-Hooks%EB%9E%80#4)%20useMemo%2C%20useCallback-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://choijying21.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0React-Hooks%EB%9E%80#4)%20useMemo%2C%20useCallback-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cpMYrN/hyXSvhcWRx/6YD2BiO8bfstnyxdumTv0k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/roqAh/hyXWrc8CON/trzkS2ULO83tjkaGlkKt4K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bNbeDq/hyXWBGPitw/RjW0J90dkao8rOGkUmwukk/img.png?width=750&amp;amp;height=750&amp;amp;face=0_0_750_750');&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;[React] 리액트 훅에 대해 알아보기(React Hooks란?)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. React Hooks란? 리액트 훅은 리액트 클래스형 컴포넌트에서 이용하던 코드를 작성할 필요없이 함수형 컴포넌트에서 다양한 기능을 사용할 수 있게 만들어준 라이브러리라고 할 수 있는데 React 16.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;choijying21.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-Hooks-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-reacthookstypescript&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-Hooks-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-reacthookstypescript&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735612041239&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;리액트 Hooks 이해하기 [react/hooks/typescript]&quot; data-og-description=&quot;리액트 Hooks는 함수 컴포넌트에서도 상태 관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 useEffect 등의 기능을 제공해 기존의 함수 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 &quot; data-og-host=&quot;woongtech.tistory.com&quot; data-og-source-url=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-Hooks-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-reacthookstypescript&quot; data-og-url=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-Hooks-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-reacthookstypescript&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mpSn7/hyXWyQRL5i/uDEkF0JXi8xrykqdW6N4F1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hJvCX/hyXSAQkUGs/frIYvLEpmmGmoEqbduyMgk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/9vASy/hyXSCHqA8M/E4aKKokSJ87r3KPSIPqd1k/img.png?width=904&amp;amp;height=492&amp;amp;face=0_0_904_492&quot;&gt;&lt;a href=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-Hooks-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-reacthookstypescript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-Hooks-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-reacthookstypescript&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mpSn7/hyXWyQRL5i/uDEkF0JXi8xrykqdW6N4F1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hJvCX/hyXSAQkUGs/frIYvLEpmmGmoEqbduyMgk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/9vASy/hyXSCHqA8M/E4aKKokSJ87r3KPSIPqd1k/img.png?width=904&amp;amp;height=492&amp;amp;face=0_0_904_492');&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;리액트 Hooks 이해하기 [react/hooks/typescript]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리액트 Hooks는 함수 컴포넌트에서도 상태 관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 useEffect 등의 기능을 제공해 기존의 함수 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;woongtech.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=_7WF8TtHD8s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=_7WF8TtHD8s&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=_7WF8TtHD8s&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/kHcHz/hyXStw22Rc/JPWyr52Mttij1cLKKk8TK1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/mNrkW/hyXSyFaRYm/dc5Z7IOF22EPOq09Dji74K/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;React.js 를 사용해 이상형 월드컵 만들기&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/_7WF8TtHD8s&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%8D%A8%EB%B3%B4%EA%B8%B0-reactroutertypescript&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%8D%A8%EB%B3%B4%EA%B8%B0-reactroutertypescript&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735706054219&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;리액트 라우터 써보기 [react/router/typescript]&quot; data-og-description=&quot;라우팅 라우팅(Routing)이란 웹 애플리케이션에서 사용자가 URL을 통해 페이지에 접근할 때 해당 URL에 따라 서로 다른 컴포넌트를 보여주는 것을 의미한다. 일반적으로, 브라우저에서 URL을 입력하&quot; data-og-host=&quot;woongtech.tistory.com&quot; data-og-source-url=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%8D%A8%EB%B3%B4%EA%B8%B0-reactroutertypescript&quot; data-og-url=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%8D%A8%EB%B3%B4%EA%B8%B0-reactroutertypescript&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dgRlTm/hyXSp9fAzF/3iM4PVlo3EAeIKkm7I43q1/img.png?width=800&amp;amp;height=695&amp;amp;face=0_0_800_695,https://scrap.kakaocdn.net/dn/dHNvhD/hyXSrF0Krj/URQ3KrKP0KHWKBIaYdWEfk/img.png?width=800&amp;amp;height=695&amp;amp;face=0_0_800_695,https://scrap.kakaocdn.net/dn/hHxoO/hyXSxM0KRF/WAJ36EzzgIIHjdeFklzN31/img.png?width=750&amp;amp;height=651&amp;amp;face=0_0_750_651&quot;&gt;&lt;a href=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%8D%A8%EB%B3%B4%EA%B8%B0-reactroutertypescript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://woongtech.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%8D%A8%EB%B3%B4%EA%B8%B0-reactroutertypescript&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dgRlTm/hyXSp9fAzF/3iM4PVlo3EAeIKkm7I43q1/img.png?width=800&amp;amp;height=695&amp;amp;face=0_0_800_695,https://scrap.kakaocdn.net/dn/dHNvhD/hyXSrF0Krj/URQ3KrKP0KHWKBIaYdWEfk/img.png?width=800&amp;amp;height=695&amp;amp;face=0_0_800_695,https://scrap.kakaocdn.net/dn/hHxoO/hyXSxM0KRF/WAJ36EzzgIIHjdeFklzN31/img.png?width=750&amp;amp;height=651&amp;amp;face=0_0_750_651');&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;리액트 라우터 써보기 [react/router/typescript]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;라우팅 라우팅(Routing)이란 웹 애플리케이션에서 사용자가 URL을 통해 페이지에 접근할 때 해당 URL에 따라 서로 다른 컴포넌트를 보여주는 것을 의미한다. 일반적으로, 브라우저에서 URL을 입력하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;woongtech.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tthinks.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-Link%EC%99%80-useNavigate&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tthinks.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-Link%EC%99%80-useNavigate&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735706844948&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;[React] 리액트에서 페이지 이동하기 | Link와 useNavigate&quot; data-og-description=&quot;React에서 Link와 useNavigate로 페이지 이동하기 오늘은 페이지 이동을 위한 리액트 hook을 알아볼게요   크게 Link 태그와 useNavigate() 함수가 있습니다. 리액트에서 Link와 useNavigate는 React Router 라이브&quot; data-og-host=&quot;tthinks.tistory.com&quot; data-og-source-url=&quot;https://tthinks.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-Link%EC%99%80-useNavigate&quot; data-og-url=&quot;https://tthinks.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-Link%EC%99%80-useNavigate&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cgLi69/hyXSuQiEND/hsunWgkFwMk6NuqO2Yamg1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hSELl/hyXWt90ry7/vSYdAs7RS0dSWcnkq7bu71/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/86xLn/hyXSpOZs9J/TMo6ULDFtmKGhtQ6VmFi0k/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080&quot;&gt;&lt;a href=&quot;https://tthinks.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-Link%EC%99%80-useNavigate&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tthinks.tistory.com/entry/React-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-Link%EC%99%80-useNavigate&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cgLi69/hyXSuQiEND/hsunWgkFwMk6NuqO2Yamg1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hSELl/hyXWt90ry7/vSYdAs7RS0dSWcnkq7bu71/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/86xLn/hyXSpOZs9J/TMo6ULDFtmKGhtQ6VmFi0k/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080');&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;[React] 리액트에서 페이지 이동하기 | Link와 useNavigate&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;React에서 Link와 useNavigate로 페이지 이동하기 오늘은 페이지 이동을 위한 리액트 hook을 알아볼게요   크게 Link 태그와 useNavigate() 함수가 있습니다. 리액트에서 Link와 useNavigate는 React Router 라이브&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tthinks.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@minzyee/useLocation&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@minzyee/useLocation&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735707142627&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;useLocation, URLSearchParams, location.search, 쿼리 문자열&quot; data-og-description=&quot;리액트에서의 useLocation, URLSearchParams, locatication, 쿼리 스트링&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@minzyee/useLocation&quot; data-og-url=&quot;https://velog.io/@minzyee/useLocation&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bVQPiX/hyXSp2vfse/G7Vp6ftAiBKhuF9sZkHPak/img.png?width=1400&amp;amp;height=900&amp;amp;face=0_0_1400_900,https://scrap.kakaocdn.net/dn/buM9DX/hyXWuA41Yz/DVwjDfs5GPnFBPcKRkj1A0/img.png?width=1400&amp;amp;height=900&amp;amp;face=0_0_1400_900,https://scrap.kakaocdn.net/dn/f8e6g/hyXWBmFSkO/9wbeuKXCPpnUD42a90SEhK/img.png?width=1400&amp;amp;height=900&amp;amp;face=0_0_1400_900&quot;&gt;&lt;a href=&quot;https://velog.io/@minzyee/useLocation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@minzyee/useLocation&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bVQPiX/hyXSp2vfse/G7Vp6ftAiBKhuF9sZkHPak/img.png?width=1400&amp;amp;height=900&amp;amp;face=0_0_1400_900,https://scrap.kakaocdn.net/dn/buM9DX/hyXWuA41Yz/DVwjDfs5GPnFBPcKRkj1A0/img.png?width=1400&amp;amp;height=900&amp;amp;face=0_0_1400_900,https://scrap.kakaocdn.net/dn/f8e6g/hyXWBmFSkO/9wbeuKXCPpnUD42a90SEhK/img.png?width=1400&amp;amp;height=900&amp;amp;face=0_0_1400_900');&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;useLocation, URLSearchParams, location.search, 쿼리 문자열&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리액트에서의 useLocation, URLSearchParams, locatication, 쿼리 스트링&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jikeun.tistory.com/entry/React-params-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-URLSearchParams-%EC%82%AC%EC%9A%A9%EB%B2%95-npm-i-query-string&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jikeun.tistory.com/entry/React-params-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-URLSearchParams-%EC%82%AC%EC%9A%A9%EB%B2%95-npm-i-query-string&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735707252601&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;React : params 사용하기 URLSearchParams 사용법 +) npm i query-string&quot; data-og-description=&quot;React : params 사용하기 URLSearchParams 사용법 +) npm i query-string 1. Params 란? 이런식으로 ? 뒤에 딸려들어가는 key 와 value 값을 params 라고 한다. url주소와 상관없이 params로 들어가면 동일한 url에서도 여러&quot; data-og-host=&quot;jikeun.tistory.com&quot; data-og-source-url=&quot;https://jikeun.tistory.com/entry/React-params-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-URLSearchParams-%EC%82%AC%EC%9A%A9%EB%B2%95-npm-i-query-string&quot; data-og-url=&quot;https://jikeun.tistory.com/entry/React-params-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-URLSearchParams-%EC%82%AC%EC%9A%A9%EB%B2%95-npm-i-query-string&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dF1ldI/hyXWr5rVxS/BqgofGl0E2KuB2F7nqThv1/img.png?width=566&amp;amp;height=490&amp;amp;face=0_0_566_490,https://scrap.kakaocdn.net/dn/nrOEW/hyXWtCbDGd/Wfri3K4tLs0LEoNIueQ8vk/img.png?width=566&amp;amp;height=490&amp;amp;face=0_0_566_490,https://scrap.kakaocdn.net/dn/vPuPj/hyXWmXmFOD/g6rYo80tKqR8mb88pBGb9K/img.png?width=566&amp;amp;height=490&amp;amp;face=0_0_566_490&quot;&gt;&lt;a href=&quot;https://jikeun.tistory.com/entry/React-params-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-URLSearchParams-%EC%82%AC%EC%9A%A9%EB%B2%95-npm-i-query-string&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jikeun.tistory.com/entry/React-params-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-URLSearchParams-%EC%82%AC%EC%9A%A9%EB%B2%95-npm-i-query-string&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dF1ldI/hyXWr5rVxS/BqgofGl0E2KuB2F7nqThv1/img.png?width=566&amp;amp;height=490&amp;amp;face=0_0_566_490,https://scrap.kakaocdn.net/dn/nrOEW/hyXWtCbDGd/Wfri3K4tLs0LEoNIueQ8vk/img.png?width=566&amp;amp;height=490&amp;amp;face=0_0_566_490,https://scrap.kakaocdn.net/dn/vPuPj/hyXWmXmFOD/g6rYo80tKqR8mb88pBGb9K/img.png?width=566&amp;amp;height=490&amp;amp;face=0_0_566_490');&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;React : params 사용하기 URLSearchParams 사용법 +) npm i query-string&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;React : params 사용하기 URLSearchParams 사용법 +) npm i query-string 1. Params 란? 이런식으로 ? 뒤에 딸려들어가는 key 와 value 값을 params 라고 한다. url주소와 상관없이 params로 들어가면 동일한 url에서도 여러&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jikeun.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@tinubee/React-%EC%83%89%EC%A2%85%EC%9D%B4-%EC%A1%B0%EA%B0%81-%EB%82%A0%EB%A6%BC-%ED%9A%A8%EA%B3%BC-feat.-Canvas-Confetti&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@tinubee/React-%EC%83%89%EC%A2%85%EC%9D%B4-%EC%A1%B0%EA%B0%81-%EB%82%A0%EB%A6%BC-%ED%9A%A8%EA%B3%BC-feat.-Canvas-Confetti&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735707279643&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;[React]    색종이 조각 날림 효과 (feat. Canvas-Confetti)&quot; data-og-description=&quot;react-canvas-confetti &amp;amp; js-confetti 를 이용하여 색종이 날림 효과 구현해보기.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@tinubee/React-%EC%83%89%EC%A2%85%EC%9D%B4-%EC%A1%B0%EA%B0%81-%EB%82%A0%EB%A6%BC-%ED%9A%A8%EA%B3%BC-feat.-Canvas-Confetti&quot; data-og-url=&quot;https://velog.io/@tinubee/React-색종이-조각-날림-효과-feat.-Canvas-Confetti&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eePcoe/hyXWspJSSI/gx07bllhIChSdcZ742b1t0/img.gif?width=768&amp;amp;height=403&amp;amp;face=0_0_768_403,https://scrap.kakaocdn.net/dn/nNTBG/hyXSuW3hFw/ivVzpFTEq5knR70wRh9X01/img.gif?width=768&amp;amp;height=403&amp;amp;face=0_0_768_403&quot;&gt;&lt;a href=&quot;https://velog.io/@tinubee/React-%EC%83%89%EC%A2%85%EC%9D%B4-%EC%A1%B0%EA%B0%81-%EB%82%A0%EB%A6%BC-%ED%9A%A8%EA%B3%BC-feat.-Canvas-Confetti&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@tinubee/React-%EC%83%89%EC%A2%85%EC%9D%B4-%EC%A1%B0%EA%B0%81-%EB%82%A0%EB%A6%BC-%ED%9A%A8%EA%B3%BC-feat.-Canvas-Confetti&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eePcoe/hyXWspJSSI/gx07bllhIChSdcZ742b1t0/img.gif?width=768&amp;amp;height=403&amp;amp;face=0_0_768_403,https://scrap.kakaocdn.net/dn/nNTBG/hyXSuW3hFw/ivVzpFTEq5knR70wRh9X01/img.gif?width=768&amp;amp;height=403&amp;amp;face=0_0_768_403');&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;[React]   색종이 조각 날림 효과 (feat. Canvas-Confetti)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;react-canvas-confetti &amp;amp; js-confetti 를 이용하여 색종이 날림 효과 구현해보기.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/커피 월드컵</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/974</guid>
      <comments>https://hj0216.tistory.com/974#entry974comment</comments>
      <pubDate>Wed, 1 Jan 2025 13:59:10 +0900</pubDate>
    </item>
    <item>
      <title>[커피 월드컵] React 카카오톡 공유하기</title>
      <link>https://hj0216.tistory.com/973</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;coffee-worldcup.jpg&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EwZHG/btsLAyCGEkU/8pX64fh6sjDjVtzU54f4G1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EwZHG/btsLAyCGEkU/8pX64fh6sjDjVtzU54f4G1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EwZHG/btsLAyCGEkU/8pX64fh6sjDjVtzU54f4G1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEwZHG%2FbtsLAyCGEkU%2F8pX64fh6sjDjVtzU54f4G1%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;257&quot; height=&quot;437&quot; data-filename=&quot;coffee-worldcup.jpg&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옆의 귀여운 루피와 공유하기는 제 미니미 프로젝트의 핵심 카카오톡 공유하기 화면입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 계획했던 건 커피 월드컵 사이트를 공유하는 것이었는데, 최종에서는 결과를 공유하는 것으로 변경되었습니다. 왜냐면, 이 프로젝트의 핵심은 월드컵이 아니라 월드컵 결과로 친구들에게 기프티콘을 노나주는 것이기 때문이죠. 덕분에 리액트에서 라우터를 추가해서 페이지도 만들고 해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 해놓으면 간단하지만 하기 전에는 조금 어려워보이는 카카오톡 공유하기를 간단히 정리해보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용방법이 엄청 어렵다!는 아니지만, 우편번호 API보다는 난이도가 있습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Kakao Developer&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developers.kakao.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.kakao.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735220309502&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kakao Developers&quot; data-og-description=&quot;카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.&quot; data-og-host=&quot;developers.kakao.com&quot; data-og-source-url=&quot;https://developers.kakao.com/&quot; data-og-url=&quot;https://developers.kakao.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TmAh4/hyXSuhkNJq/zsWhqMzO6jMAQ7V6ku26Pk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/NqcLO/hyXSABRdgv/ruOpKpGbKk0zVtxTinSrY1/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000,https://scrap.kakaocdn.net/dn/YO4KT/hyXSwGd8Tz/0qY6WWpgnYOUN4IcRiaXaK/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000&quot;&gt;&lt;a href=&quot;https://developers.kakao.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.kakao.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TmAh4/hyXSuhkNJq/zsWhqMzO6jMAQ7V6ku26Pk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/NqcLO/hyXSABRdgv/ruOpKpGbKk0zVtxTinSrY1/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000,https://scrap.kakaocdn.net/dn/YO4KT/hyXSwGd8Tz/0qY6WWpgnYOUN4IcRiaXaK/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000');&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;Kakao Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미리보기에서 벌써 말해줍니다. 카카오 API를 활용해서 메시지를 보내라고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 내 애플리케이션 &amp;rarr; 애플리케이션 추가하기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;795&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chrhOk/btsLybhUJYS/rzHaV094TtCP2tlD46r3g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chrhOk/btsLybhUJYS/rzHaV094TtCP2tlD46r3g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chrhOk/btsLybhUJYS/rzHaV094TtCP2tlD46r3g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchrhOk%2FbtsLybhUJYS%2FrzHaV094TtCP2tlD46r3g1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;794&quot; height=&quot;795&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;795&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 내 애플리케이션 &amp;rarr; 앱 설정 &amp;rarr; 플랫폼&lt;/p&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;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd0iE9/btsLASudNHJ/F9lYXyhhNZvUI6Pte7v191/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd0iE9/btsLASudNHJ/F9lYXyhhNZvUI6Pte7v191/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd0iE9/btsLASudNHJ/F9lYXyhhNZvUI6Pte7v191/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd0iE9%2FbtsLASudNHJ%2FF9lYXyhhNZvUI6Pte7v191%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1218&quot; height=&quot;310&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트 도메인을 추가해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;localhost에서 먼저 테스트를 해봐야 하므로, netlify와 더불어 추가해줍니다.&lt;/p&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. React 기준 index.html에 script 추가&lt;/p&gt;
&lt;pre id=&quot;code_1735220788491&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
	&amp;lt;script src=&quot;https://developers.kakao.com/sdk/js/kakao.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&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. React 기준 카카오톡 공유하기 컴포넌트 만들기&lt;/p&gt;
&lt;pre id=&quot;code_1735220962601&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const KakaoShare = (selected, brand) =&amp;gt; {
  if (!window.Kakao.isInitialized()) {
    window.Kakao.init(process.env.REACT_APP_KAKAO_JS_API_KEY);
    console.log(&quot;Kakao SDK initialized:&quot;, window.Kakao.isInitialized());
  }
  
  // 생략

  window.Kakao.Link.sendDefault({
    objectType: &quot;feed&quot;,
    content: {
      title: &quot;내가 어디서 어떤 커피를 먹고싶냐면..&quot;,
      description: `#${drinkDescription} #${coffeeDescription} #${dessertDescription} #${brandDescription}에서 #적당한 #깊티를 #내놔라`,
      imageUrl: &quot;https://raw.githubusercontent.com/HJ0216/dream-coffee-worldcup/main/src/image/loopy-img-share.jpg&quot;,
      link: {
        mobileWebUrl: resultUrl, // 모바일 웹 URL
        webUrl: resultUrl,       // PC 웹 URL
      },
    },
    buttons: [
      {
        title: &quot;자세히 보기&quot;,
        link: {
          mobileWebUrl: resultUrl,
          webUrl: resultUrl,
        },
      },
    ],
  });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 템플릿을 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트 초반에 초기화를 확인하는데, 이 때 API 키가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 애플리케이션 &amp;rarr; 앱 설정 &amp;rarr; 앱 키에서 Javascript 키를 복사해서 넣으면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무료 API키지만 키 값은 소중하므로 &lt;a href=&quot;https://hj0216.tistory.com/882&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;.env&lt;/a&gt;를 활용해 Github 상에 공유되지 않도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; Github에 올라가지 않은 환경변수는 Netlify에서 따로 추가 후 재 빌드를 해줘야 합니다.&lt;/p&gt;
&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;a href=&quot;https://developers.kakao.com/docs/latest/ko/message/js-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.kakao.com/docs/latest/ko/message/js-link&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735221390537&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kakao Developers&quot; data-og-description=&quot;카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.&quot; data-og-host=&quot;developers.kakao.com&quot; data-og-source-url=&quot;https://developers.kakao.com/docs/latest/ko/message/js-link&quot; data-og-url=&quot;https://developers.kakao.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bQQ6EK/hyXSscKZ9w/2X9GJS5EtYcVhakZC1Sfx1/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bMqwLC/hyXSsqikxF/e8KHXgHRO7ACgQKBFGRWq1/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000,https://scrap.kakaocdn.net/dn/bemL1D/hyXSExu1a9/a0zKQKNuYykzk401w6BG00/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000&quot;&gt;&lt;a href=&quot;https://developers.kakao.com/docs/latest/ko/message/js-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.kakao.com/docs/latest/ko/message/js-link&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bQQ6EK/hyXSscKZ9w/2X9GJS5EtYcVhakZC1Sfx1/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bMqwLC/hyXSsqikxF/e8KHXgHRO7ACgQKBFGRWq1/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000,https://scrap.kakaocdn.net/dn/bemL1D/hyXSExu1a9/a0zKQKNuYykzk401w6BG00/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000');&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;Kakao Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 최종 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 가장 먼저 만나보셨던 화려한 루피 사진이 공유하기 화면입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 아래 사이트에서도 직접 체험해보실 수 있습니다.&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;a href=&quot;https://hj0216.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hj0216.netlify.app/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735221495698&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Coffee WorldCup&quot; data-og-description=&quot;&quot; data-og-host=&quot;hj0216.netlify.app&quot; data-og-source-url=&quot;https://hj0216.netlify.app/&quot; data-og-url=&quot;https://hj0216.netlify.app/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://hj0216.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hj0216.netlify.app/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Coffee WorldCup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hj0216.netlify.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;1078&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pei47/btsLz6T0Dh9/2HnKJfFxsUw0tFDpC4BWi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pei47/btsLz6T0Dh9/2HnKJfFxsUw0tFDpC4BWi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pei47/btsLz6T0Dh9/2HnKJfFxsUw0tFDpC4BWi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpei47%2FbtsLz6T0Dh9%2F2HnKJfFxsUw0tFDpC4BWi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1078&quot; height=&quot;449&quot; data-origin-width=&quot;1078&quot; data-origin-height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Chat GPT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developers.kakao.com/docs/latest/ko/message/js-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.kakao.com/docs/latest/ko/message/js-link&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735221561994&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kakao Developers&quot; data-og-description=&quot;카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.&quot; data-og-host=&quot;developers.kakao.com&quot; data-og-source-url=&quot;https://developers.kakao.com/docs/latest/ko/message/js-link&quot; data-og-url=&quot;https://developers.kakao.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bQQ6EK/hyXSscKZ9w/2X9GJS5EtYcVhakZC1Sfx1/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bMqwLC/hyXSsqikxF/e8KHXgHRO7ACgQKBFGRWq1/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000,https://scrap.kakaocdn.net/dn/bemL1D/hyXSExu1a9/a0zKQKNuYykzk401w6BG00/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000&quot;&gt;&lt;a href=&quot;https://developers.kakao.com/docs/latest/ko/message/js-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.kakao.com/docs/latest/ko/message/js-link&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bQQ6EK/hyXSscKZ9w/2X9GJS5EtYcVhakZC1Sfx1/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bMqwLC/hyXSsqikxF/e8KHXgHRO7ACgQKBFGRWq1/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000,https://scrap.kakaocdn.net/dn/bemL1D/hyXSExu1a9/a0zKQKNuYykzk401w6BG00/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000');&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;Kakao Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@dejeong/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%86%A1-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0-JavaScript-API-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@dejeong/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%86%A1-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0-JavaScript-API-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735221576693&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;카카오톡 공유하기 JavaScript API 사용하기&quot; data-og-description=&quot;카카오톡 공유하기 API JavaScript로 간단하게 사용하기&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@dejeong/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%86%A1-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0-JavaScript-API-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95&quot; data-og-url=&quot;https://velog.io/@dejeong/카카오톡-공유하기-JavaScript-API-사용-방법&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkTXrG/hyXSFwo2Lw/2CbnNHnSTuyeyvsppclAHk/img.jpg?width=596&amp;amp;height=266&amp;amp;face=0_0_596_266,https://scrap.kakaocdn.net/dn/uLzLv/hyXSCzFMId/szYDkK7QWav1jkrEX5Hrd1/img.jpg?width=596&amp;amp;height=266&amp;amp;face=0_0_596_266,https://scrap.kakaocdn.net/dn/Ynlko/hyXSyYlX3Y/xETh9uwNaGh3buN2pRz4aK/img.png?width=1569&amp;amp;height=909&amp;amp;face=0_0_1569_909&quot;&gt;&lt;a href=&quot;https://velog.io/@dejeong/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%86%A1-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0-JavaScript-API-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@dejeong/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%86%A1-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0-JavaScript-API-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkTXrG/hyXSFwo2Lw/2CbnNHnSTuyeyvsppclAHk/img.jpg?width=596&amp;amp;height=266&amp;amp;face=0_0_596_266,https://scrap.kakaocdn.net/dn/uLzLv/hyXSCzFMId/szYDkK7QWav1jkrEX5Hrd1/img.jpg?width=596&amp;amp;height=266&amp;amp;face=0_0_596_266,https://scrap.kakaocdn.net/dn/Ynlko/hyXSyYlX3Y/xETh9uwNaGh3buN2pRz4aK/img.png?width=1569&amp;amp;height=909&amp;amp;face=0_0_1569_909');&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;카카오톡 공유하기 JavaScript API 사용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오톡 공유하기 API JavaScript로 간단하게 사용하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://front-kuli.tistory.com/168&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://front-kuli.tistory.com/168&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735221588167&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;React 로 카카오톡 공유하기 기능 추가해보기&quot; data-og-description=&quot;https://developers.kakao.com/tool/demo/message/kakaolink?message_type=scrap Kakao Developers 카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제&quot; data-og-host=&quot;front-kuli.tistory.com&quot; data-og-source-url=&quot;https://front-kuli.tistory.com/168&quot; data-og-url=&quot;https://front-kuli.tistory.com/168&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/czmfYR/hyXSun60Uf/wQiU80oUNtyWvmT7aR1jJk/img.png?width=800&amp;amp;height=495&amp;amp;face=0_0_800_495,https://scrap.kakaocdn.net/dn/l4W08/hyXSFwo2Ms/AkaJpqcswqkEfKfne2fKkk/img.png?width=800&amp;amp;height=495&amp;amp;face=0_0_800_495,https://scrap.kakaocdn.net/dn/bHPf0q/hyXSx6dMkY/k6iUYrrAZCkwPmJt8ndFrk/img.png?width=2098&amp;amp;height=1202&amp;amp;face=0_0_2098_1202&quot;&gt;&lt;a href=&quot;https://front-kuli.tistory.com/168&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://front-kuli.tistory.com/168&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/czmfYR/hyXSun60Uf/wQiU80oUNtyWvmT7aR1jJk/img.png?width=800&amp;amp;height=495&amp;amp;face=0_0_800_495,https://scrap.kakaocdn.net/dn/l4W08/hyXSFwo2Ms/AkaJpqcswqkEfKfne2fKkk/img.png?width=800&amp;amp;height=495&amp;amp;face=0_0_800_495,https://scrap.kakaocdn.net/dn/bHPf0q/hyXSx6dMkY/k6iUYrrAZCkwPmJt8ndFrk/img.png?width=2098&amp;amp;height=1202&amp;amp;face=0_0_2098_1202');&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;React 로 카카오톡 공유하기 기능 추가해보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://developers.kakao.com/tool/demo/message/kakaolink?message_type=scrap Kakao Developers 카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;front-kuli.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/커피 월드컵</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/973</guid>
      <comments>https://hj0216.tistory.com/973#entry973comment</comments>
      <pubDate>Thu, 26 Dec 2024 23:07:06 +0900</pubDate>
    </item>
    <item>
      <title>[커피 월드컵] Netlify React 배포</title>
      <link>https://hj0216.tistory.com/972</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;coffee-worldcup.jpg&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXihvw/btsLxlxtxee/DZiHDpscwEIskK0Y7lkJjK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXihvw/btsLxlxtxee/DZiHDpscwEIskK0Y7lkJjK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXihvw/btsLxlxtxee/DZiHDpscwEIskK0Y7lkJjK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXihvw%2FbtsLxlxtxee%2FDZiHDpscwEIskK0Y7lkJjK%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;257&quot; height=&quot;437&quot; data-filename=&quot;coffee-worldcup.jpg&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다들 연말 잘 보내고 계신가요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해 제 목표 중 하나는 직접 만든 사이트를 친구에게 재밌게 보여주기였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마침 연말이기도 하고, 고마운 마음을 기프티콘을 통해 보내기도 하는데, 마침 저에게는 웹 사이트를 만들 능력이 조금 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 만들어 본 커피 이상형 월드컵..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트를 메인으로 공부하지 않기 때문에, 여기서는 리액트 코드보다는 새로운 시도를 했던 기술들을 간략히 적어보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 첫번째는 바로, Netlify 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친구들 중에 개발을 하지 않는 친구가 더 많기 때문에 내 깃헙으로 들어와서 코드를 클론하고 npm start로 실행해줘라는 부탁은 쉽지 않습니다. 그래서 도메인을 연결해 누구나 접근 가능한 사이트를 하나 갖고 있어야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, 필요한 것이 바로 Netlify!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Netlify는 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;HTML, CSS, JavaScript 등의 정적 파일로 구성된 웹사이트를 간단하게 배포할&lt;span&gt; 수 있는 사이트로, &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;Git 저장소와 통합되어 있어, 코드 변경이 발생할 때 자동으로 빌드 및 배포를 수행할 수 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Netlify를 통한 배포 과정은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Add new site &amp;rarr; Import and existing project&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1509&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ci6bqC/btsLxGBiuma/9hIAgFGb0bjNBe6ZW1ncHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci6bqC/btsLxGBiuma/9hIAgFGb0bjNBe6ZW1ncHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci6bqC/btsLxGBiuma/9hIAgFGb0bjNBe6ZW1ncHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci6bqC%2FbtsLxGBiuma%2F9hIAgFGb0bjNBe6ZW1ncHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1509&quot; height=&quot;347&quot; data-origin-width=&quot;1509&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/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. Deploy project with Github&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 Git 저장소도 있지만, 전 깃헙을 쓰기에 깃헙을 선택했습니다.&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;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7TnxI/btsLxFbo3Dc/VKD6A52QRp52TTHfU8P5Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7TnxI/btsLxFbo3Dc/VKD6A52QRp52TTHfU8P5Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7TnxI/btsLxFbo3Dc/VKD6A52QRp52TTHfU8P5Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7TnxI%2FbtsLxFbo3Dc%2FVKD6A52QRp52TTHfU8P5Y1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1322&quot; height=&quot;173&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/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. Respository 선택&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개만 배포할 예정이라 Only select repositories를 선택했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFbvYe/btsLyuUGbUS/XMRdkAN9iJqBbWOnYNEaW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFbvYe/btsLyuUGbUS/XMRdkAN9iJqBbWOnYNEaW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFbvYe/btsLyuUGbUS/XMRdkAN9iJqBbWOnYNEaW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFbvYe%2FbtsLyuUGbUS%2FXMRdkAN9iJqBbWOnYNEaW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;575&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/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. Build Setting&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB3e1y/btsLxhIyo8r/ITH2AVUh3DvWSzxOlXkrwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB3e1y/btsLxhIyo8r/ITH2AVUh3DvWSzxOlXkrwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB3e1y/btsLxhIyo8r/ITH2AVUh3DvWSzxOlXkrwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB3e1y%2FbtsLxhIyo8r%2FITH2AVUh3DvWSzxOlXkrwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;742&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Branch to deploy: 배포에 사용할 브랜치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base Directory: 배포할 React 프로젝트의 루트 경로 입력, 프론트로만 구성된 프로젝트의 경우 입력 X&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Build&amp;nbsp;command&amp;nbsp;:&amp;nbsp;빌드&amp;nbsp;명령어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Publish&amp;nbsp;directory&amp;nbsp;:&amp;nbsp;빌드가&amp;nbsp;완료된&amp;nbsp;후&amp;nbsp;생성된&amp;nbsp;폴더&amp;nbsp;이름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⭐알아두면 좋을 것⭐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. .env처럼 환경변수를 사용할 경우, github에는 올라가지 않으므로 Netlify에서 따로 설정해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 후, 재배포를 수동으로 해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. esLint 경고가 발생할 경우, Netlify에서 빌드 실패가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경고를 무시하던가 해결하던가 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ &lt;s&gt;아직은 수정 중인 미니미 프로젝트 주소&lt;/s&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;a href=&quot;https://hj0216.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hj0216.netlify.app/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734960041659&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;React App&quot; data-og-description=&quot;&quot; data-og-host=&quot;hj0216.netlify.app&quot; data-og-source-url=&quot;https://hj0216.netlify.app/&quot; data-og-url=&quot;https://hj0216.netlify.app/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://hj0216.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hj0216.netlify.app/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;React App&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hj0216.netlify.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Chat GPT&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@somda/FE-Netlify%EB%A1%9C-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@somda/FE-Netlify%EB%A1%9C-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734959845209&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;[FE] Netlify로 React 프로젝트 빠르게 배포하기&quot; data-og-description=&quot;요즘은 원티드 프리온보딩 프로그램에 등록해 간만에 열몇시간씩 컴퓨터 앞에 붙어있는 생활을 하고 있다. 다양한 세션을 통해 많은 것을 배울 수 있었는데 가장 좋았던 건 S3를 통해 배포하는 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@somda/FE-Netlify%EB%A1%9C-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@somda/FE-Netlify로-React-프로젝트-빠르게-배포하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/z1IWf/hyXOkNGvW8/lnu1FfyISIpLLJF37RlTz1/img.png?width=500&amp;amp;height=250&amp;amp;face=0_0_500_250,https://scrap.kakaocdn.net/dn/h9vS2/hyXSzbjN3k/5Zh4CG0RWK4MHCE02XkN3k/img.png?width=500&amp;amp;height=250&amp;amp;face=0_0_500_250,https://scrap.kakaocdn.net/dn/cBlLCK/hyXOleLp4w/8X1U1cnoUdlQHeko93eXS0/img.png?width=2877&amp;amp;height=1551&amp;amp;face=0_0_2877_1551&quot;&gt;&lt;a href=&quot;https://velog.io/@somda/FE-Netlify%EB%A1%9C-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@somda/FE-Netlify%EB%A1%9C-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/z1IWf/hyXOkNGvW8/lnu1FfyISIpLLJF37RlTz1/img.png?width=500&amp;amp;height=250&amp;amp;face=0_0_500_250,https://scrap.kakaocdn.net/dn/h9vS2/hyXSzbjN3k/5Zh4CG0RWK4MHCE02XkN3k/img.png?width=500&amp;amp;height=250&amp;amp;face=0_0_500_250,https://scrap.kakaocdn.net/dn/cBlLCK/hyXOleLp4w/8X1U1cnoUdlQHeko93eXS0/img.png?width=2877&amp;amp;height=1551&amp;amp;face=0_0_2877_1551');&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;[FE] Netlify로 React 프로젝트 빠르게 배포하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;요즘은 원티드 프리온보딩 프로그램에 등록해 간만에 열몇시간씩 컴퓨터 앞에 붙어있는 생활을 하고 있다. 다양한 세션을 통해 많은 것을 배울 수 있었는데 가장 좋았던 건 S3를 통해 배포하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@easyxxu/React-netlify-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@easyxxu/React-netlify-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734959850653&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;Netlify 배포하기&quot; data-og-description=&quot;netlify로 React 프로젝트 배포해보기&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@easyxxu/React-netlify-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@easyxxu/React-netlify-배포하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bn0RM1/hyXOrsuxZP/tdfKwpVXAzA7HZYgpkTpaK/img.jpg?width=720&amp;amp;height=405&amp;amp;face=0_0_720_405,https://scrap.kakaocdn.net/dn/n7AUb/hyXSzPWvwB/by9qQILIlgIaTebvKarijK/img.jpg?width=720&amp;amp;height=405&amp;amp;face=0_0_720_405,https://scrap.kakaocdn.net/dn/cyLFEa/hyXOlZ790y/g4dKinTDJF2crpinr9Yojk/img.png?width=1632&amp;amp;height=938&amp;amp;face=0_0_1632_938&quot;&gt;&lt;a href=&quot;https://velog.io/@easyxxu/React-netlify-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@easyxxu/React-netlify-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bn0RM1/hyXOrsuxZP/tdfKwpVXAzA7HZYgpkTpaK/img.jpg?width=720&amp;amp;height=405&amp;amp;face=0_0_720_405,https://scrap.kakaocdn.net/dn/n7AUb/hyXSzPWvwB/by9qQILIlgIaTebvKarijK/img.jpg?width=720&amp;amp;height=405&amp;amp;face=0_0_720_405,https://scrap.kakaocdn.net/dn/cyLFEa/hyXOlZ790y/g4dKinTDJF2crpinr9Yojk/img.png?width=1632&amp;amp;height=938&amp;amp;face=0_0_1632_938');&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;Netlify 배포하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;netlify로 React 프로젝트 배포해보기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@yangth/Netlify%EB%A1%9C-%EB%B2%A0%ED%8F%AC-%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@yangth/Netlify%EB%A1%9C-%EB%B2%A0%ED%8F%AC-%ED%95%B4%EB%B3%B4%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735047958029&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;Netlify로 베포 해보기&quot; data-og-description=&quot;Netlify는 깃허브(Github), 깃랩(GitLab) 등과 계정 연동 및 쉬운 호스팅을 제공하고 CDN, Continuous Deployment(지속적 배포), One-Click HTTPS 제공 등 고성능 사이트 / 웹 응용 프로그램을 제작하는데 필요한 쉽&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@yangth/Netlify%EB%A1%9C-%EB%B2%A0%ED%8F%AC-%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@yangth/Netlify로-베포-해보기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ctQWuA/hyXOivHxzE/EmpPd5ByBhxcwoo21Jff7k/img.png?width=936&amp;amp;height=632&amp;amp;face=0_0_936_632,https://scrap.kakaocdn.net/dn/hb2ze/hyXOqAvPko/C9M8noXvn1R8017FxQlsRk/img.png?width=936&amp;amp;height=632&amp;amp;face=0_0_936_632,https://scrap.kakaocdn.net/dn/jnyvX/hyXSyXWnyn/YcWutNapp0MmnoXKUs311k/img.png?width=949&amp;amp;height=786&amp;amp;face=0_0_949_786&quot;&gt;&lt;a href=&quot;https://velog.io/@yangth/Netlify%EB%A1%9C-%EB%B2%A0%ED%8F%AC-%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@yangth/Netlify%EB%A1%9C-%EB%B2%A0%ED%8F%AC-%ED%95%B4%EB%B3%B4%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ctQWuA/hyXOivHxzE/EmpPd5ByBhxcwoo21Jff7k/img.png?width=936&amp;amp;height=632&amp;amp;face=0_0_936_632,https://scrap.kakaocdn.net/dn/hb2ze/hyXOqAvPko/C9M8noXvn1R8017FxQlsRk/img.png?width=936&amp;amp;height=632&amp;amp;face=0_0_936_632,https://scrap.kakaocdn.net/dn/jnyvX/hyXSyXWnyn/YcWutNapp0MmnoXKUs311k/img.png?width=949&amp;amp;height=786&amp;amp;face=0_0_949_786');&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;Netlify로 베포 해보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Netlify는 깃허브(Github), 깃랩(GitLab) 등과 계정 연동 및 쉬운 호스팅을 제공하고 CDN, Continuous Deployment(지속적 배포), One-Click HTTPS 제공 등 고성능 사이트 / 웹 응용 프로그램을 제작하는데 필요한 쉽&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=WJtetccrv3o&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=WJtetccrv3o&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=WJtetccrv3o&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/80Mmz/hyXOq1y6um/QbTzVOblKjHPIK7I2g9AK1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=782_112_1094_452,https://scrap.kakaocdn.net/dn/oo9dW/hyXOmSlana/t5ukKJEFTEUWNzK3QC2P2k/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=782_112_1094_452&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;로컬호스트를 탈출하자! | 웹사이트 배포하는 법, Netlify 면 버튼하나면 충분&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/WJtetccrv3o&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MinimiProject/커피 월드컵</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/972</guid>
      <comments>https://hj0216.tistory.com/972#entry972comment</comments>
      <pubDate>Tue, 24 Dec 2024 22:49:51 +0900</pubDate>
    </item>
    <item>
      <title>[1년 후 마실가실] Spring Security - UserDetailsService</title>
      <link>https://hj0216.tistory.com/971</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;1년 전 진행했던 마실가실 프로젝트를  ️리팩토링하며 정리한 내용입니다.&lt;/i&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 floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;a href=&quot;https://e.kakao.com/t/zanmang-loopy-ver-5&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ER23J/btsKS3ZAfps/bdO30BSulxd8hptKjfr0e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FER23J%2FbtsKS3ZAfps%2FbdO30BSulxd8hptKjfr0e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Security는 끝날 때까지 끝난 게 아닙니다..&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;회원 탈퇴 기능을 추가하면서 User 테이블에 IsUsed를 선언해서 flag 처럼 사용하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 탈퇴한 회원은 조회가 되면 안되기에 검색 로직에서 IsUsed를 추가해주고 로그인을 테스트해본 것이 가까워진 Security와 멀어진 계기가 되었죠..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀어진 Security와 다시 가까워지기 위한 노력을 정리해 보았습니다..&lt;/p&gt;
&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;회원 정보가 없을 때, 제가 Throw한 Exception과 전달받은 Exception이 달랐습니다..&lt;/p&gt;
&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;style2&quot;&gt;LoginFilter.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1732329342019&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request,
      HttpServletResponse response) throws AuthenticationException {

    try {
      LoginRequestDTO loginRequestDto = objectMapper.readValue(request.getInputStream(),
          LoginRequestDTO.class);

      String email = loginRequestDto.getEmail();
      String password = loginRequestDto.getPassword();

      UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
          email, password, null);

      return authenticationManager.authenticate(authToken);

    } catch (IOException e) {
      log.error(&quot;Failed to parse authentication request body {}&quot;, e);

      throw new BusinessException(INVALID_CREDENTIALS);
    }
  }

  @Override
  protected void unsuccessfulAuthentication(HttpServletRequest request,
      HttpServletResponse response,
      AuthenticationException failed) {
    log.error(&quot;Login failed: {}&quot;, failed.getMessage());

    throw new BusinessException(CHECK_LOGIN_ID_OR_PASSWORD);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인의 경우에는 Controller를 사용하지 않고, UsernamePasswordAuthenticationFilter를 상속받아 LoginFilter를 구현해서 사용하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에 명시적으로 적혀있진 않지만, AuthenticationManager의 authenticate()를 호출하면 회원 및 비밀번호 검증이 이뤄지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(관련 내용은 이 &lt;a href=&quot;https://hj0216.tistory.com/963&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;포스팅&lt;/a&gt;에 적어놨었죠.. &lt;span&gt;⭐&lt;/span&gt; 저도 다시 찾아봤습니다^,,^)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 다시 정리해보자면, 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * login&amp;nbsp;Filter는&amp;nbsp;AuthenticationManager를&amp;nbsp;사용하여&amp;nbsp;인증&amp;nbsp;작업을&amp;nbsp;수행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; *&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;AuthenticationManager는 interface이기 때문에 실제로는 AuthenticationProvider가 인증 작업을 수행&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;AuthenticationProvider는 인증 작업을 수행할 때, UserDetailsService를 호출&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; * 저의 경우에는 인터페이스인 UserDetailsService를 구현한 CustomUserDetailsService가 호출되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;UserDetailsService는 loadUserByUsername()를 통해 사용자 정보를 조회한 후, 사용자 인증 정보인 UserDetails를 반환&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * AuthenticationProvider는&amp;nbsp;반환된&amp;nbsp;UserDetails&amp;nbsp;객체와&amp;nbsp;클라이언트가&amp;nbsp;제공한&amp;nbsp;비밀번호를&amp;nbsp;비교하여&amp;nbsp;인증을&amp;nbsp;수행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실제 사용자가 존재하는지를 확인하는 로직은 &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;UserDetailsService를 구현한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;CustomUserDetailsService에 있기에 이곳을 검토해봐야 합니다.&lt;/span&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;CustomUserDetailsService.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1732330270250&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {

  private final UserRepository userRepository;

  @Override
  public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    User user = userRepository.findByEmail(email).orElseThrow(() -&amp;gt;
        new UsernameNotFoundException(&quot;Authentication failed&quot;));

    return UserPrinciple.builder()
                        .user(user)
                        .build();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 회원이 없을 때, UsernameNotFoundException이지만, 기존에는 Custom한 Exception인 BusinessException을 Throw했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Filter에서 Exception 컨트롤을 했으므로, BusinessException이 뜨면서 종료가 되면 문제가 없는데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unsuccessfulAuthentication를 타면서 Exception을 디버깅해보면, 저는 보낸 적 없는 InternalAuthenticationServiceException가 나옵니다. 콩 심은데 콩 나고 팥 심은데 팥 난다는 속담이 의미가 없어지는.. 그런 상황인 거죠..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자, 이제 동작 방식을 이해해 볼 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;UserDetailsService 클래스의 &lt;/span&gt;loadUserByUsername 메서드에서 회원이 존재하지 않을 때 발생하는 BusinessException은 Spring Security에서 InternalAuthenticationServiceException로 래핑이 됩니다. 래핑을 원치 않을 경우, DaoAuthenticationProvider를 커스터마이징하거나 Spring&amp;nbsp;Security에서&amp;nbsp;인증&amp;nbsp;실패로&amp;nbsp;처리하기&amp;nbsp;위한&amp;nbsp;표준&amp;nbsp;예외인&amp;nbsp; UsernameNotFoundException를 던져서 BadCredentialsException로 받아 처리하는 방법이 있습니다.&lt;/p&gt;
&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;하지만, 요새 로그인을 시도해보시면 아실 수도 있겠지만 비밀번호를 틀렸다는 말을 잘 안해줍니다. 아이디나 비밀번호를 다시 확인하라고 합니다. Spring Security에서는 보안상 의도한 것이라하여, 저도 구분하지 않기로 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, LoginFilter에서 인증 과정을 거치기 위해 &lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;UserDetailsService을 구현한 Custom&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;UserDetailsService에서 Custom Exception을 발생시켰지만, 내부적으로 InternalAuthenticationServiceException로 래핑되는 문제가 있었습니다. Filter를 추가하여 복잡성을 올리기보다는 표준 방식을 따르기로 했고, 이에 따라 Custom한 Exception보다는 UsernameNotFoundException을 발생시켜 unsuccessfulAuthentication에서 BadCredentialsException을 처리하는 방식으로 수정하였습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스트는 공부 목적으로 작성하였습니다.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chat GPT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hj0216.tistory.com/963&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hj0216.tistory.com/963&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732411220313&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;[1년 후 마실가실] Spring Security + JWT - LoginFilter&quot; data-og-description=&quot;1년 전 진행했던 마실가실 프로젝트를  ️리팩토링하며 정리한 내용입니다.&amp;nbsp;&amp;nbsp;요즘 제 표정입니다.&amp;nbsp;Spring Security Exception 처리에 막혀서, 이건.. 이건.. 학습이 필요하다는 생각이 간절히 들어 &quot; data-og-host=&quot;hj0216.tistory.com&quot; data-og-source-url=&quot;https://hj0216.tistory.com/963&quot; data-og-url=&quot;https://hj0216.tistory.com/963&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/7sj9N/hyXDab4JuV/wFRdW5sXfiEb9ZkauFPGmk/img.png?width=800&amp;amp;height=421&amp;amp;face=0_0_800_421,https://scrap.kakaocdn.net/dn/XGWFq/hyXDinFrHl/8ujDkszy9S4DCBGFOkc0t1/img.png?width=800&amp;amp;height=421&amp;amp;face=0_0_800_421&quot;&gt;&lt;a href=&quot;https://hj0216.tistory.com/963&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hj0216.tistory.com/963&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/7sj9N/hyXDab4JuV/wFRdW5sXfiEb9ZkauFPGmk/img.png?width=800&amp;amp;height=421&amp;amp;face=0_0_800_421,https://scrap.kakaocdn.net/dn/XGWFq/hyXDinFrHl/8ujDkszy9S4DCBGFOkc0t1/img.png?width=800&amp;amp;height=421&amp;amp;face=0_0_800_421');&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;[1년 후 마실가실] Spring Security + JWT - LoginFilter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1년 전 진행했던 마실가실 프로젝트를  ️리팩토링하며 정리한 내용입니다.&amp;nbsp;&amp;nbsp;요즘 제 표정입니다.&amp;nbsp;Spring Security Exception 처리에 막혀서, 이건.. 이건.. 학습이 필요하다는 생각이 간절히 들어&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hj0216.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/user-details-service.html#page-title&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/user-details-service.html#page-title&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732411260269&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;UserDetailsService :: Spring Security&quot; data-og-description=&quot;This is only used if the AuthenticationManagerBuilder has not been populated and no AuthenticationProviderBean is defined.&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/user-details-service.html#page-title&quot; data-og-url=&quot;https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/user-details-service.html#page-title&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/user-details-service.html#page-title&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/user-details-service.html#page-title&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;UserDetailsService :: Spring Security&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This is only used if the AuthenticationManagerBuilder has not been populated and no AuthenticationProviderBean is defined.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/마실가실 리팩토링</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/971</guid>
      <comments>https://hj0216.tistory.com/971#entry971comment</comments>
      <pubDate>Fri, 29 Nov 2024 09:29:13 +0900</pubDate>
    </item>
    <item>
      <title>[해결 방법] no suitable constructor found for Jackson2JsonRedisSerializer(Class&amp;lt;CAP#1&amp;gt;)</title>
      <link>https://hj0216.tistory.com/970</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Environment&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Language: Java17&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Framework: SpringBoot 3.1.0&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;size18&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;오류&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732251883568&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;constructor Jackson2JsonRedisSerializer.Jackson2JsonRedisSerializer(Class&amp;lt;T&amp;gt;) is not applicable(argument mismatch; Class&amp;lt;CAP#2&amp;gt; cannot be converted to Class&amp;lt;T&amp;gt;)
constructor Jackson2JsonRedisSerializer.Jackson2JsonRedisSerializer(JavaType) is not applicable(argument mismatch; Class&amp;lt;CAP#2&amp;gt; cannot be converted to JavaType)
where T is a type-variable:
  T extends Object declared in method &amp;lt;T&amp;gt;set(String,T,long)
where CAP#1,CAP#2 are fresh type-variables:
  CAP#1 extends Object from capture of ? extends Object
  CAP#2 extends Object from capture of ? extends Object&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jackson2JsonRedisSerializer의&amp;nbsp;생성자에서&amp;nbsp;Class&amp;lt;T&amp;gt;&amp;nbsp;타입을&amp;nbsp;요구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러가 제네릭 타입을 캡처한 결과인 CAP#2는 T 타입으로 변환할 수 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * CAP#1, CAP#2: 컴파일러가 제네릭 타입을 추론할 때 사용하는 임시 이름으로, T가&amp;nbsp;?&amp;nbsp;extends&amp;nbsp;Object&amp;nbsp;(모든&amp;nbsp;타입을&amp;nbsp;포함하는&amp;nbsp;와일드카드)로&amp;nbsp;사용될&amp;nbsp;때&amp;nbsp;부여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;원인&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732252017404&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public &amp;lt;T&amp;gt; void set(String key, T o, long milliseconds) {
  redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer&amp;lt;T&amp;gt;(o.getClass()));
  redisTemplate.opsForValue().set(key, o, milliseconds, TimeUnit.MILLISECONDS);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제너릭 타입(&amp;lt;&amp;gt;)에 파라미터(T)를 명시할 경우, 컴파일러가 T의 타입을 자동으로 추론할 수 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;T의 명확한 타입을 아는 것이 아니라 단순히 와일드 카드(? extends Object)인 것만 알고 있는 상태이므로 o.getClass와 T를 매칭시키지 못함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 타입을 명시했지만 컴파일 시점에는 타입을 알 수 없어 오류 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;해결&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732253204359&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public &amp;lt;T&amp;gt; void set(String key, T o, long milliseconds) {
  redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer&amp;lt;&amp;gt;(o.getClass()));
  redisTemplate.opsForValue().set(key, o, milliseconds, TimeUnit.MILLISECONDS);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터를 명시하지 않고 타입만 선언(&amp;lt;&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스트는 공부 목적으로 작성하였습니다.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chat GPT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Java with Error</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/970</guid>
      <comments>https://hj0216.tistory.com/970#entry970comment</comments>
      <pubDate>Sun, 24 Nov 2024 10:02:44 +0900</pubDate>
    </item>
    <item>
      <title>[1년 후 마실가실] SonarQube와 Immutable List</title>
      <link>https://hj0216.tistory.com/969</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;1년 전 진행했던 마실가실 프로젝트를  ️리팩토링하며 정리한 내용입니다.&lt;/i&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 floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;748&quot;&gt;&lt;a href=&quot;https://kr.pinterest.com/pin/578431145923739290/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIOOrs/btsKSGhpE6k/R6SxKxfVzIoCLryJ09hbpK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIOOrs%2FbtsKSGhpE6k%2FR6SxKxfVzIoCLryJ09hbpK%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; alt=&quot;https://kr.pinterest.com/pin/578431145923739290/&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;239&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/a&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;여러분은 모르셨겠지만, 최근에 얼추 갖춰진 회원 CRUD가 끝났습니다.. 야호..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직도 수정해야할 건 산더미이긴 하지만, 부담갖지 않고 해보고싶은 것들, 궁금한 것들을 위주로 리팩토링을 하자,, 라고 마음을 다지고 있습니다 .&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 큰 산이라고 생각한 이미지를 시작해보기 전에 코드를 좀 다듬는 중입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 조금 간단한 내용으로 정적분석도구 SonarQube를 어떻게 쓰고 있는지를 짧게 남겨보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hj0216.tistory.com/944&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SonarQube 사용하기&lt;/a&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;GlobalExceptionHandler.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1732243855473&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
	  private ErrorResponse makeErrorResponse(BindException e, ErrorCode errorCode) {
    List&amp;lt;ErrorResponse.ValidationError&amp;gt; validationErrorList = e.getBindingResult()
                                                               .getFieldErrors()
                                                               .stream()
                                                               .map(
                                                                   ErrorResponse.ValidationError::of)
                                                               .collect(Collectors.toList());

    return ErrorResponse.builder()
                        .code(errorCode.name())
                        .message(errorCode.getMessage())
                        .errors(validationErrorList)
                        .build();
  }
}&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;* e.getBindingResult()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 회원 가입 시, 응집도를 높이기 위해 어노테이션을 사용해 각 필드값의 유효성 검사를 수행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 이 때, 유효성 검사에 실패할 경우, 해당 결과가 BindningResult에 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* getFieldErrors()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 유효성&amp;nbsp;검증에서&amp;nbsp;실패한&amp;nbsp;필드&amp;nbsp;단위의&amp;nbsp;오류&amp;nbsp;목록을&amp;nbsp;반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * FieldError&amp;nbsp;객체들로&amp;nbsp;구성된&amp;nbsp;List&amp;lt;FieldError&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732244322798&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
  FieldError(field=&quot;email&quot;, rejectedValue=&quot;invalid@&quot;, message=&quot;Invalid email format&quot;),
  FieldError(field=&quot;password&quot;, rejectedValue=&quot;123&quot;, message=&quot;Password is too short&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;* stream()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 컬렉션 또는 배열과 같은 데이터를 함수형 프로그래밍 스타일로 처리할 수 있도록 지원하는 데이터 처리 도구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 가독성, 확장성 등을 위해서 해당 리스트를 stream으로 변환&lt;/p&gt;
&lt;pre id=&quot;code_1732244705795&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 일반
List&amp;lt;ValidationError&amp;gt; validationErrorList = new ArrayList&amp;lt;&amp;gt;();
for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
    validationErrorList.add(ValidationError.of(fieldError));
}

// Stream
List&amp;lt;ValidationError&amp;gt; validationErrorList = e.getBindingResult()
                                             .getFieldErrors()
                                             .stream()
                                             .map(ValidationError::of)
                                             .toList();&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;* map(ErrorResponse.ValidationError::of)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 기존&amp;nbsp;데이터의&amp;nbsp;각&amp;nbsp;요소를&amp;nbsp;가공하거나&amp;nbsp;다른&amp;nbsp;타입으로&amp;nbsp;변경할&amp;nbsp;때&amp;nbsp;사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 스트림의 각 FieldError 객체를 ErrorResponse.ValidationError.of()를 사용하여 ErrorResponse.ValidationError 객체로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* (기존) collect(Collectors.toList())&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Collectors.toList(): mutable&amp;nbsp;List(수정&amp;nbsp;가능한&amp;nbsp;리스트)를&amp;nbsp;반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Validation Error는 한 번 발생한 이후, 수정될 가능성이 없으므로 Immutable List로 반환하는 것이 더 안정적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * Java16 이상에서는 &lt;b&gt;Stream.toList()&lt;/b&gt; / Java10 이상에서는 &lt;b&gt;Collectors.toUnmodifiableList()&lt;/b&gt;로 Immutable List로 사용 가능&lt;/p&gt;
&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; 1. 해당 코드의 의도를 명확히하고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. 수정 작업이 불가능하므로 멀티 스레드 환경에서 동기화 작업에 들어가는 비용을 낮춰, 멀티스레드&amp;nbsp;환경에서&amp;nbsp;더&amp;nbsp;높은&amp;nbsp;성능과&amp;nbsp;낮은&amp;nbsp;복잡도를&amp;nbsp;제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스트는 공부 목적으로 작성하였습니다.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/마실가실 리팩토링</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/969</guid>
      <comments>https://hj0216.tistory.com/969#entry969comment</comments>
      <pubDate>Fri, 22 Nov 2024 12:26:25 +0900</pubDate>
    </item>
    <item>
      <title>[1년 후 마실가실] ExceptionHandlerFilter</title>
      <link>https://hj0216.tistory.com/968</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;1년 전 진행했던 마실가실 프로젝트를  ️리팩토링하며 정리한 내용입니다.&lt;/i&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 floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0AYGg/btsKM00Xd07/rkdZTqMQyKChgBk8aBEdIk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0AYGg/btsKM00Xd07/rkdZTqMQyKChgBk8aBEdIk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0AYGg/btsKM00Xd07/rkdZTqMQyKChgBk8aBEdIk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0AYGg%2FbtsKM00Xd07%2FrkdZTqMQyKChgBk8aBEdIk%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;230&quot; height=&quot;210&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/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;Filter에서 Exception 처리가 되지 않아 길을 잃은지 어느덧 한 달..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 필터를 활용하여 Exception 처리까지 왔습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도치않은 리리팩토링으로 인해 기능 구현은 제자리지만, 포기하지 않으면 과정입니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이팅..⭐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그간의 이야기를 잠깐 해보자면, 저에겐 @RestControllerAdvice를 선언한 GlobalExceptionHanlder가 있었습니다(&lt;s&gt;지금도 있습니다^^&lt;/s&gt;). GlobalExceptionHanlder는 Controller로 들어온 Exception을 처리하는데, Filter에서 오류가 발생하면 Controller로 들어오지 않아 Exception 처리가 안되던 문제가 있었습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, 지금은 ⭐ Filter 전용 Exception Filter⭐를 만들고자 합니다.&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;ExceptionHandlerFilter.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1731738111557&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@Slf4j
public class ExceptionHandlerFilter extends OncePerRequestFilter {

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {
    try {
      filterChain.doFilter(request, response);
    } catch (BusinessException e) {
      setErrorResponse(response, e.getErrorCode());
    }
  }

  private void setErrorResponse(HttpServletResponse response, ErrorCode errorCode) {
    response.setStatus(errorCode.getHttpStatus().value());

    ErrorResponse errorResponse = ErrorResponse.builder()
                                               .code(errorCode.name())
                                               .message(errorCode.getMessage())
                                               .build();

    try {
      ObjectMapper mapper = new ObjectMapper();
      mapper.enable(SerializationFeature.INDENT_OUTPUT);

      String jsonResponse = mapper.writeValueAsString(errorResponse);
      response.getWriter().write(jsonResponse);

    } catch (IOException e) {
      log.error(&quot;Error writing response: {}&quot;, e.getMessage(), e);
    }
  }
}&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;기존에 사용하던 Error Response와 동일한 형식으로 작성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON 형식으로 Error를 반환해주고 싶기에 ObjectMapper를 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값으로 ObjectMapper는 데이터가 한 줄로 출력이되기에, 들여쓰기 옵션도 추가해주었습니다.&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;SecurityConfig.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1731738416088&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

  // 생략

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    http.httpBasic(AbstractHttpConfigurer::disable);

    http.csrf(AbstractHttpConfigurer::disable);

    http.cors(httpSecurityCorsConfigurer -&amp;gt;
        corsConfigurationSource()
    );

    http.sessionManagement(sessionManagement -&amp;gt;
        sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    );

    http.authorizeHttpRequests(auth -&amp;gt;
        auth
            .requestMatchers(PERMIT_ALL_URL).permitAll()
            .requestMatchers(NEED_ROLE_URL).hasRole(&quot;USER&quot;)
            .anyRequest().authenticated()
    );
    http.addFilterBefore(characterEncodingFilter(), CsrfFilter.class);
    http.addFilterBefore(new JWTFilter(jwtUtils), LoginFilter.class);

    LoginFilter loginFilter = new LoginFilter(
        authenticationManager(authenticationConfiguration)
        , jwtUtils
        , redisUtils);
    loginFilter.setFilterProcessesUrl(&quot;/api/v2/users/login&quot;);
    http.addFilterAt(loginFilter, UsernamePasswordAuthenticationFilter.class);

    http.addFilterBefore(new CustomLogoutFilter(jwtUtils, redisUtils), LogoutFilter.class);

    http.addFilterAfter(new ExceptionHandlerFilter(), SecurityContextPersistenceFilter.class);

    return http.build();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exception을 담당하는 필터를 새로 만들어줬기에 SecurityConfig에 추가를 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 모든 필터에서 발생한 예외를 한 곳에서 처리하고자 Spring Security 필터 체인의 마지막에 위치하는 SecurityContextPersistenceFilter의 뒤에 설정하였습니다.&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;LoginFilter.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1731739130795&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

  // 생략

  @Override
  protected void unsuccessfulAuthentication(HttpServletRequest request,
      HttpServletResponse response,
      AuthenticationException failed) {
    log.error(&quot;Login failed: &quot;, failed.getMessage());

    throw new BusinessException(INVALID_CREDENTIALS);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 response에 status만 setting을 했었는데, 지금은 BusinessException을 throw했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 ErrorResponse는 CustomErrorCode라는 별도의 Enum을 활용해서 code와 메시지를 리턴해주는데, 동일한 형식을 가져가려고 CustomException을 발생시켰습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Filter에서 BusinessException이 발생하면, 최종 단계의 필터인 ExceptionHandlerFilter에서 catch로 잡혀 Error 메시지를 리턴하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exception에 따른 response 처리와 비즈니스 로직이 분리되어 단일 책임을 향해가는 기분이 듭니다 .&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿌듯하네요   .&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스트는 공부 목적으로 작성하였습니다.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://thalals.tistory.com/451&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://thalals.tistory.com/451&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731737523108&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;Spring Security Exception Handling - Filter 단 예외 처리하기&quot; data-og-description=&quot;오늘은 Spring Security 를 적용했지만 JWT 가 만료되거나, 잘못된 토큰일 경우 401 코드 뿐만아니라 에러 메세지까지 핸들링 해줄 수 있도록 설정해 주고자 합니다. 1. Spring Security 와 @ControllerAdvice 먼&quot; data-og-host=&quot;thalals.tistory.com&quot; data-og-source-url=&quot;https://thalals.tistory.com/451&quot; data-og-url=&quot;https://thalals.tistory.com/451&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eucoP2/hyXwp9NJ10/tjQTkFHAoFea9UplLZzss1/img.jpg?width=754&amp;amp;height=641&amp;amp;face=0_0_754_641,https://scrap.kakaocdn.net/dn/Zo0oW/hyXzSh96gA/reJqylS4a0qbuX8NDTMkg1/img.jpg?width=754&amp;amp;height=641&amp;amp;face=0_0_754_641,https://scrap.kakaocdn.net/dn/bpCs2y/hyXwjBIOGU/VGPuHFdqRZ2KUzZPyO65sk/img.png?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://thalals.tistory.com/451&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://thalals.tistory.com/451&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eucoP2/hyXwp9NJ10/tjQTkFHAoFea9UplLZzss1/img.jpg?width=754&amp;amp;height=641&amp;amp;face=0_0_754_641,https://scrap.kakaocdn.net/dn/Zo0oW/hyXzSh96gA/reJqylS4a0qbuX8NDTMkg1/img.jpg?width=754&amp;amp;height=641&amp;amp;face=0_0_754_641,https://scrap.kakaocdn.net/dn/bpCs2y/hyXwjBIOGU/VGPuHFdqRZ2KUzZPyO65sk/img.png?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720');&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;Spring Security Exception Handling - Filter 단 예외 처리하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 Spring Security 를 적용했지만 JWT 가 만료되거나, 잘못된 토큰일 경우 401 코드 뿐만아니라 에러 메세지까지 핸들링 해줄 수 있도록 설정해 주고자 합니다. 1. Spring Security 와 @ControllerAdvice 먼&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;thalals.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/마실가실 리팩토링</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/968</guid>
      <comments>https://hj0216.tistory.com/968#entry968comment</comments>
      <pubDate>Sun, 17 Nov 2024 11:18:51 +0900</pubDate>
    </item>
    <item>
      <title>[1년 후 마실가실] @GeneratedValue(strategy = GenerationType.IDENTITY)</title>
      <link>https://hj0216.tistory.com/967</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;1년 전 진행했던 마실가실 프로젝트를  ️리팩토링하며 정리한 내용입니다.&lt;/i&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 floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bymn5i/btsKDzJQXek/N75jrIgLVpOXBDafpXAaZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bymn5i/btsKDzJQXek/N75jrIgLVpOXBDafpXAaZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bymn5i/btsKDzJQXek/N75jrIgLVpOXBDafpXAaZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbymn5i%2FbtsKDzJQXek%2FN75jrIgLVpOXBDafpXAaZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;210&quot; height=&quot;210&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 껐다키면 userId가 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;Spring Security를 전체적으로 리리팩토링을 하고 Postman으로 전체적인 동작을 확인하고 있습니다.&lt;/p&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에서 UserId값을 보는데, 2000번을 넘었더라고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 Create 메서드를 2000번이나 했으려나..? 라는 생각과 함께 Postman으로 Id값이 제대로 증가하는지 확인해보다 애플리케이션을 껐다 키면 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;간단하게 고쳐보는 과정을 남겨봅니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hibernate5부터 MySQL DB로 @GeneratedValue를 사용해서 AUTO를 strategy로 선택하게 되면, GenerationType.TABLE이 선택됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, 애플리케이션을 껐다가 다시 실행하면 값이 50씩 증가하는 이유는 Hibernate가 테이블 기반 시퀀스를 사용하는데, 기본적으로 증가 단위를 50으로 설정하기 때문입니다. 이는 성능을 위해서 미리 ID를 확보해 놓는 방식이며, 애플리케이션이 종료되면 캐시된 ID들이 사라져서 다음 시작 시 새로운 ID 할당이 시작됩니다.&lt;br /&gt;&lt;br /&gt;이를 해결하기위해서는 allocationSize를 명시적으로 지정하면 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;User.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1731223376356&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User extends BaseEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.TABLE, generator = &quot;my_table_generator&quot;)
  @TableGenerator(name = &quot;my_table_generator&quot;, allocationSize = 1)
  @Column(name = &quot;user_id&quot;)
  private Integer id;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 해결은 되는데, 문제는 TABLE 전략을 사용하는 것에 대해 고민을 해봐야 한다는 것입니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐면, Table 전략은 모든 Table들이 하나의 Sequence 테이블을 두고 사용하는 전략인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 여러 트랜잭션이 동시에 ID를 할당받으려 하면 ID 테이블에 대해 잠금(lock)이 발생할 수 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. TABLE 전략을 사용할 때는 allocationSize와 같은 설정을 최적화해야 하는데, 그렇지 않으면 ID 증가 단위가 크거나, 중간에 간격이 생기는 등 예상치 못한 문제들이 (저처럼;;) 발생할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 반해 Identity 전략은 DB에서 직접 ID를 할당하는 방식인데, MySQL의 경우 AUTO_INCREMENT 기능을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ID 생성 시 별도의 테이블 접근이 필요하지 않으므로, 성능 상의 이점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 추가 설정 없이 MySQL의 기본 기능을 사용하기 때문에, 설정이 복잡하지 않고 유지보수도 상대적으로 간단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&amp;nbsp;MySQL에서는&amp;nbsp;성능과&amp;nbsp;관리&amp;nbsp;효율성&amp;nbsp;측면에서&amp;nbsp;@GeneratedValue(strategy&amp;nbsp;=&amp;nbsp;GenerationType.IDENTITY)를&amp;nbsp;사용하는&amp;nbsp;것이&amp;nbsp;일반적으로&amp;nbsp;권장됩니다.&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;style2&quot;&gt;User.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1731223827795&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User extends BaseEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = &quot;user_id&quot;)
  private Integer id;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;  Auto Increment를 사용하기 때문에 MySQL에서 userId값에 해당 설정이 추가되어 있어야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;또한, 기존에 사용했던 Id값보다 +1 값부터 증가시켜줘야 하므로, 추가적인 설정이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1731224070119&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- AUTO_INCREMENT 추가
ALTER TABLE user MODIFY user_id INT NOT NULL AUTO_INCREMENT;

-- 외래키 조건이 있을 경우, 임시 삭제
ALTER TABLE trip DROP FOREIGN KEY 외래키_이름;

-- 외래키 재설정
ALTER TABLE trip
ADD CONSTRAINT 외래키_이름
FOREIGN KEY (user_id) REFERENCES user(user_id);

-- AUTO INCREMENT 시작 번호 지정
ALTER TABLE user AUTO_INCREMENT = 기존_USERID보다_1_더_큰_값;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스트는 공부 목적으로 작성하였습니다.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev-box.tistory.com/101#google_vignette&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dev-box.tistory.com/101#google_vignette&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731223426241&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;[Jpa] 실전을 위한 JPA - #4 @GeneratedValue 컬럼 시퀀스 전략 및 성능 개선(튜닝)&quot; data-og-description=&quot;이번 포스팅에서는 저번 포스팅(#3 엔티티(Entity) 기본 어노테이션)에서 공부했던 것 중에서 @GeneratedValue 에 대해 조금 더 깊게 파헤쳐보려합니다. 그럼 @GeneratedValue 의 자세한 내용을 공부하기 전&quot; data-og-host=&quot;dev-box.tistory.com&quot; data-og-source-url=&quot;https://dev-box.tistory.com/101#google_vignette&quot; data-og-url=&quot;https://dev-box.tistory.com/101&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BXl5e/hyXwiahBea/YTwSMpJ2tMZCmZxjf0suuk/img.png?width=800&amp;amp;height=751&amp;amp;face=0_0_800_751,https://scrap.kakaocdn.net/dn/ogQ71/hyXsTiqAHl/kF9W9fQyZwB3A8A1mDqwKk/img.png?width=800&amp;amp;height=751&amp;amp;face=0_0_800_751,https://scrap.kakaocdn.net/dn/daosT0/hyXwqlRBay/YBpM4SObCocP2EAZ1uYQE1/img.png?width=264&amp;amp;height=200&amp;amp;face=0_0_264_200&quot;&gt;&lt;a href=&quot;https://dev-box.tistory.com/101#google_vignette&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev-box.tistory.com/101#google_vignette&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BXl5e/hyXwiahBea/YTwSMpJ2tMZCmZxjf0suuk/img.png?width=800&amp;amp;height=751&amp;amp;face=0_0_800_751,https://scrap.kakaocdn.net/dn/ogQ71/hyXsTiqAHl/kF9W9fQyZwB3A8A1mDqwKk/img.png?width=800&amp;amp;height=751&amp;amp;face=0_0_800_751,https://scrap.kakaocdn.net/dn/daosT0/hyXwqlRBay/YBpM4SObCocP2EAZ1uYQE1/img.png?width=264&amp;amp;height=200&amp;amp;face=0_0_264_200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Jpa] 실전을 위한 JPA - #4 @GeneratedValue 컬럼 시퀀스 전략 및 성능 개선(튜닝)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 저번 포스팅(#3 엔티티(Entity) 기본 어노테이션)에서 공부했던 것 중에서 @GeneratedValue 에 대해 조금 더 깊게 파헤쳐보려합니다. 그럼 @GeneratedValue 의 자세한 내용을 공부하기 전&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev-box.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/마실가실 리팩토링</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/967</guid>
      <comments>https://hj0216.tistory.com/967#entry967comment</comments>
      <pubDate>Thu, 14 Nov 2024 17:33:50 +0900</pubDate>
    </item>
    <item>
      <title>[해결 방법] &amp;quot;this.bCryptPasswordEncoder&amp;quot; is null</title>
      <link>https://hj0216.tistory.com/966</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;java.lang.NullPointerException: Cannot invoke &quot;org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.encode(java.lang.CharSequence)&quot; because &quot;this.bCryptPasswordEncoder&quot; is null&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Environment&lt;br /&gt;Language: Java 17&lt;br /&gt;Framework: SpringBoot 3.1.0, Spring Security 6.1.0&lt;br /&gt;DB: MySQL, Redis&lt;/blockquote&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;/p&gt;
&lt;pre id=&quot;code_1731214828164&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.NullPointerException: 
Cannot invoke &quot;org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.encode(java.lang.CharSequence)&quot; 
because &quot;this.bCryptPasswordEncoder&quot; is null&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;size18&quot;&gt;원인&lt;/p&gt;
&lt;pre id=&quot;code_1731214919665&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Builder
@AllArgsConstructor
@Getter
public class SignUpRequestDTO {

  private final BCryptPasswordEncoder bCryptPasswordEncoder;

  public User toEntity() {
    return User.builder()
               .status(userType.substring(0, 1).toUpperCase())
               .userType(UserType.valueOf(userType.toUpperCase()))
               .email(email)
               .phone(phone)
               .nickname(nickname)
               .password(bCryptPasswordEncoder.encode(password))
               .role(getRole())
               .build();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bCryptPasswordEncoder 인스턴스가 주입되지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SignUpRequestDTO가 빈으로 등록되지 않았으므로 Spring에서는 @Bean으로 등록된 BCryptPasswordEncoder를 자동으로 주입할 수 없음&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&lt;span&gt;&amp;nbsp;&lt;/span&gt;SignUpRequestDTO을 Bean으로 등록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;DTO는 일반적으로 데이터 전송 객체로 사용되며, 빈으로 관리되는 경우가 거의 없음&lt;/p&gt;
&lt;pre id=&quot;code_1731215104487&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
@Builder
@AllArgsConstructor
@Getter
public class SignUpRequestDTO {

  private final BCryptPasswordEncoder bCryptPasswordEncoder;

  public User toEntity() {
    return User.builder()
               .status(userType.substring(0, 1).toUpperCase())
               .userType(UserType.valueOf(userType.toUpperCase()))
               .email(email)
               .phone(phone)
               .nickname(nickname)
               .password(bCryptPasswordEncoder.encode(password))
               .role(getRole())
               .build();
  }
}&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;2. Service 클래스에서 BCryptPasswordEncoder를&amp;nbsp;주입&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;UserService.java&lt;br /&gt;Bean으로 등록된 UserSerivce에서 BCryptPasswordEncoder를 주입받아 SignUpRequestDTO로 전달&lt;/blockquote&gt;
&lt;pre id=&quot;code_1731215437718&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class UserService {

  private final BCryptPasswordEncoder bCryptPasswordEncoder;

  @Transactional
  public void create(SignUpRequestDTO signUpRequestDTO) {
    emailDuplicateCheck(signUpRequestDTO.getEmail());
    userRepository.save(signUpRequestDTO.toEntity(bCryptPasswordEncoder));

    log.info(&quot;User successfully created for email: {}&quot;, signUpRequestDTO.getEmail());
  }
}&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;style2&quot;&gt;SignUpRequestDTO.java&lt;/blockquote&gt;
&lt;pre id=&quot;code_1731215524975&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Builder
@AllArgsConstructor
@Getter
public class SignUpRequestDTO {

  public User toEntity(BCryptPasswordEncoder bCryptPasswordEncoder) {
    return User.builder()
               .status(userType.substring(0, 1).toUpperCase())
               .userType(UserType.valueOf(userType.toUpperCase()))
               .email(email)
               .phone(phone)
               .nickname(nickname)
               .password(bCryptPasswordEncoder.encode(password))
               .role(getRole())
               .build();
  }
}&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 style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스트는 공부 목적으로 작성하였습니다.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Spring with Error</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/966</guid>
      <comments>https://hj0216.tistory.com/966#entry966comment</comments>
      <pubDate>Tue, 12 Nov 2024 19:27:00 +0900</pubDate>
    </item>
    <item>
      <title>[1년 후 마실가실] CharacterEncodingFilter</title>
      <link>https://hj0216.tistory.com/965</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;1년 전 진행했던 마실가실 프로젝트를  ️리팩토링하며 정리한 내용입니다.&lt;/i&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 floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WkIDo/btsKAImHNYV/kB6eKlc68xBRpdLxIhCGkK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WkIDo/btsKAImHNYV/kB6eKlc68xBRpdLxIhCGkK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WkIDo/btsKAImHNYV/kB6eKlc68xBRpdLxIhCGkK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWkIDo%2FbtsKAImHNYV%2FkB6eKlc68xBRpdLxIhCGkK%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;305&quot; height=&quot;227&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1234&quot;/&gt;&lt;/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;최근에 Security Filter를 리리팩토링하면서 기본 HttpStatus 값을 반환하여, Custom한 Error Code가 쓸 일이 없어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 쓰라고 만들어둔 것이기에 코드를 조금씩 바꿔서 response에 조심스럽게 담아 고객님께 전달해드렸는데 한글이 출력이 안됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이 곳은 한국.. 한국어가 나와야 합니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색해서 나온 방법들은 통하지 않아 네이버 블로그와 GPT와 함께 열심히 찾은 결과를 작성해둡니다..&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;(실패) application.yml&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 설정의 중앙 허브.. application.yml 설정으로 먼저 시작해 봤습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1730972085836&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  servlet:
    encoding:
      charset: UTF-8
      force: true&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;charset을 UTF-8로 강제로 바꾸겠다는 의지는 통하지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;style2&quot;&gt;(실패) Filter.java&lt;br /&gt;Response의 Header와 ContentType 설정&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 나온 해결 방법.. 하지만 제가 Postman에게 전달받은 글자는 물음표 뿐..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 즉, Postman도 저도 모르겠다는 의미입니다..&lt;/p&gt;
&lt;pre id=&quot;code_1730972463641&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private void setResponse(HttpServletResponse response, ErrorCode errorCode) {
  response.setCharacterEncoding(&quot;UTF-8&quot;);
  response.setContentType(&quot;text/plain; charset=UTF-8&quot;);
  response.setStatus(errorCode.getHttpStatus().value());

  try {
    PrintWriter writer = response.getWriter();
    response.getWriter().write(errorCode.getMessage());
  } catch (IOException e) {
    log.warn(e.getMessage());
  }
}&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;blockquote data-ke-style=&quot;style2&quot;&gt;(성공 )SecurityConfig.java&lt;br /&gt;CharacterEncodingFilter 사용&lt;/blockquote&gt;
&lt;pre id=&quot;code_1730973193420&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SecurityConfig {
  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.addFilterBefore(characterEncodingFilter(), CsrfFilter.class);
  }


  @Bean
  public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new CharacterEncodingFilter();
    filter.setEncoding(&quot;UTF-8&quot;);
    filter.setForceEncoding(true);
    return filter;
  }  
}&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;자, 이제 2가지 의문점을 해결해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 왜 Filter 설정을 해야만 한글을 볼 수 있는가.&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;response.getWriter()를 호출하면 서블릿 컨테이너가 해당 응답을 &quot;커밋&quot;(commit)하게 되는데, 이 과정에서 콘텐츠 타입과 인코딩이 이미 지정된 상태로 고정됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그 후, setHeader(&quot;Content-Type&quot;, &quot;text/plain; charset=UTF-8&quot;)나 setCharacterEncoding(&quot;UTF-8&quot;) 같은 설정이 더 이상 반영되지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 한글이 제대로 출력되지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * 기존 코드의 경우, Header 설정을 getWriter() 호출 전 사용하긴 했으나,&amp;nbsp;Spring Security가 적용된 환경에서는 인코딩 설정이 제대로 반영되지 않을 가능성이 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; Spring Security나 다른 필터들이 응답 처리를 할 때, 인코딩 설정을 덮어쓰거나 변경하는 경우가 발생할 수 있기 때문&lt;/p&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. csrfFilter는 누구시고, 왜 그전에 설정해야 하는가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csrfFilter: 사이트 간 요청 위조를 방지하는 필터, 요청마다&amp;nbsp;고유한&amp;nbsp;CSRF&amp;nbsp;토큰을&amp;nbsp;생성하고&amp;nbsp;이를&amp;nbsp;검증하는&amp;nbsp;역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * CSRF 토큰 : 사용자가 서버에 로그인하거나 새 세션을 시작하면, 서버는 CSRF 토큰을 생성하고 이를 세션에 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; * CSRF: 악의적인 사이트가 사용자를 대신해 신뢰된 사이트에 요청을 보내게 하여 피해를 입히는 공격 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 예: 사용자가 은행에 로그인한 상태에서 악의적인 사이트를 방문하면 악의적인 사이트가 사용자 몰래 계좌 이체 등의 요청을 보낼 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CharacterEncodingFilter는&amp;nbsp;요청과&amp;nbsp;응답의&amp;nbsp;문자&amp;nbsp;인코딩을&amp;nbsp;설정하는&amp;nbsp;필터로,&amp;nbsp;요청&amp;nbsp;본문이나&amp;nbsp;응답에&amp;nbsp;대한&amp;nbsp;인코딩을&amp;nbsp;처리함 &lt;br /&gt;인코딩&amp;nbsp;여부에&amp;nbsp;영향을&amp;nbsp;미치는&amp;nbsp;필터들&amp;nbsp;중&amp;nbsp;가장&amp;nbsp;앞에&amp;nbsp;위치해야&amp;nbsp;하고,&amp;nbsp;보통&amp;nbsp;인코딩&amp;nbsp;영향을&amp;nbsp;받는&amp;nbsp;필터&amp;nbsp;중&amp;nbsp;첫&amp;nbsp;번째가&amp;nbsp;CSRF&amp;nbsp;필터 &lt;br /&gt;CSRF&amp;nbsp;토큰&amp;nbsp;검증&amp;nbsp;필터(CsrfFilter)&amp;nbsp;이후&amp;nbsp;설정&amp;nbsp;시,&amp;nbsp;CsrfFilter나&amp;nbsp;그&amp;nbsp;외&amp;nbsp;다른&amp;nbsp;보안&amp;nbsp;필터가&amp;nbsp;요청&amp;nbsp;본문에&amp;nbsp;접근할&amp;nbsp;때&amp;nbsp;이미&amp;nbsp;잘못된&amp;nbsp;인코딩이&amp;nbsp;적용될&amp;nbsp;수&amp;nbsp;있음 &lt;br /&gt;CsrfFilter가&amp;nbsp;요청의&amp;nbsp;인코딩을&amp;nbsp;고려하지&amp;nbsp;않고&amp;nbsp;바로&amp;nbsp;검증을&amp;nbsp;수행하는&amp;nbsp;구조이기&amp;nbsp;때문에,&amp;nbsp;인코딩이&amp;nbsp;올바르게&amp;nbsp;적용되지&amp;nbsp;않은&amp;nbsp;상태에서&amp;nbsp;필터가&amp;nbsp;실행되면&amp;nbsp;CSRF&amp;nbsp;필터를&amp;nbsp;포함한&amp;nbsp;이후&amp;nbsp;필터들이&amp;nbsp;요청을&amp;nbsp;잘못&amp;nbsp;해석할&amp;nbsp;수도&amp;nbsp;있게&amp;nbsp;됨 &lt;br /&gt;CSRF&amp;nbsp;보호가&amp;nbsp;비활성화되어도&amp;nbsp;다른&amp;nbsp;필터들이&amp;nbsp;요청&amp;nbsp;본문이나&amp;nbsp;응답을&amp;nbsp;처리할&amp;nbsp;때&amp;nbsp;올바른&amp;nbsp;인코딩이&amp;nbsp;적용되어야&amp;nbsp;하기&amp;nbsp;때문에,&amp;nbsp;CharacterEncodingFilter는&amp;nbsp;CSRF&amp;nbsp;설정과&amp;nbsp;무관하게&amp;nbsp;항상&amp;nbsp;앞단에&amp;nbsp;두는&amp;nbsp;것이&amp;nbsp;권장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스트는 공부 목적으로 작성하였습니다.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;참고 자료&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@choonbok22/230314-TIL-32&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@choonbok22/230314-TIL-32&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1730973228643&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;230314 TIL #32 getWriter() 한글 깨짐&quot; data-og-description=&quot;Spring 3주차.1 getWriter() 한글 깨짐&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@choonbok22/230314-TIL-32&quot; data-og-url=&quot;https://velog.io/@choonbok22/230314-TIL-32&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bIK0qE/hyXwwlB3Kt/7qw8YszNIKx5yQsGin0kfk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bYkVda/hyXwmpLhZs/J3xYtKADXJqnZQDmt8F1VK/img.jpg?width=512&amp;amp;height=524&amp;amp;face=0_0_512_524&quot;&gt;&lt;a href=&quot;https://velog.io/@choonbok22/230314-TIL-32&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@choonbok22/230314-TIL-32&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bIK0qE/hyXwwlB3Kt/7qw8YszNIKx5yQsGin0kfk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bYkVda/hyXwmpLhZs/J3xYtKADXJqnZQDmt8F1VK/img.jpg?width=512&amp;amp;height=524&amp;amp;face=0_0_512_524');&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;230314 TIL #32 getWriter() 한글 깨짐&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring 3주차.1 getWriter() 한글 깨짐&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/haengro/220549463106&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://m.blog.naver.com/haengro/220549463106&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1730973284977&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;Spring에서의 한글깨짐 문제 (Spring Security 사용시)&quot; data-og-description=&quot;개인 프로젝트를 진행하기위해 가장 최신버전의 Spring을 적용해서 웹페이지를 개발하기로 결정했다 그외...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://m.blog.naver.com/haengro/220549463106&quot; data-og-url=&quot;https://blog.naver.com/haengro/220549463106&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bvuzmG/hyXwna8bTI/WNBNPy7n7G2zNIzzU10pvK/img.png?width=270&amp;amp;height=270&amp;amp;face=0_0_270_270&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/haengro/220549463106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://m.blog.naver.com/haengro/220549463106&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bvuzmG/hyXwna8bTI/WNBNPy7n7G2zNIzzU10pvK/img.png?width=270&amp;amp;height=270&amp;amp;face=0_0_270_270');&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;Spring에서의 한글깨짐 문제 (Spring Security 사용시)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개인 프로젝트를 진행하기위해 가장 최신버전의 Spring을 적용해서 웹페이지를 개발하기로 결정했다 그외...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PlayGround/마실가실 리팩토링</category>
      <author>HJ0216</author>
      <guid isPermaLink="true">https://hj0216.tistory.com/965</guid>
      <comments>https://hj0216.tistory.com/965#entry965comment</comments>
      <pubDate>Sun, 10 Nov 2024 10:33:33 +0900</pubDate>
    </item>
  </channel>
</rss>