<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>olrlobt</title>
    <link>https://olrlobt.tistory.com/</link>
    <description>함께 성장하기위한 개발 기록지</description>
    <language>ko</language>
    <pubDate>Tue, 16 Jun 2026 11:17:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>olrlobt</managingEditor>
    <image>
      <title>olrlobt</title>
      <url>https://tistory1.daumcdn.net/tistory/5900027/attach/982d755a1fcf48f6952fab76d954e5f4</url>
      <link>https://olrlobt.tistory.com</link>
    </image>
    <item>
      <title>[회고록] 난 내가 내 꿈의 근처라도 가보고는 죽어야지 싶더라고</title>
      <link>https://olrlobt.tistory.com/99</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;난 내가 내 꿈의 근처라도 가보고는 죽어야지 싶더라고&lt;br /&gt;- 빈지노 Always Awake 중 -&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 가장 좋아하는 노래의 일부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 갑자기 뜬금없이 이런 걸 알려주냐고?&lt;/p&gt;
&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;그래서 오늘은 취업준비를 위해 내가 했던 것들, 그간 고생한 것들을 모두 청산하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길고도 험했던 취업준비생시절&lt;s&gt;(불과 8시간 전..)&lt;/s&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;이래서 사람들이 일기를 쓰나 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든,&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SSAFY 10기 수료... 그리고 그 이후&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싸피는 길지만 정말 순식간에 지나갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느샌가 수료생이 되어 지원서를 남발하고 있는 나 자신을 발견했다. 이미 친구들은 모두가 아는 그런 기업들에 들어가 일에 적응을 시작하고 있었고, 나는 이 친구들과 어깨를 나란히 했던 사람이었잖아?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 이 친구들에 걸맞은 기업에 들어가고 싶었다.&lt;/p&gt;
&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;내 스펙은 경기 4년제, 영어 못함, 자격증 SQLD 단 하나, KDT 국비교육 1회.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내세울 거라고는 SSAFY 하나밖에 없었는 그냥 그런 사람이었다.&lt;/p&gt;
&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;1927&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTxZWD/btsK595hWF3/qiyawVtBOq8QKVgMv85ONk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTxZWD/btsK595hWF3/qiyawVtBOq8QKVgMv85ONk/img.png&quot; data-alt=&quot;24년 하반기 지원 목록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTxZWD/btsK595hWF3/qiyawVtBOq8QKVgMv85ONk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTxZWD%2FbtsK595hWF3%2FqiyawVtBOq8QKVgMv85ONk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;24년 하반기 서류 지원 목록&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;1927&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;1927&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;24년 하반기 지원 목록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세어보니 취업을 위해 작성한 서류는 총 65개. 이중 싸피 수료 이후인 7월부터 작성한 서류는 56개이다.&lt;/p&gt;
&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;더 잘 쓰고 싶어서, 더 나를 잘 표현하고 싶어서. 서류 하나에만 6시간 이상을 투자한 적도 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서류에 탈락할 때면 현타가 많이 와서, 아무것도 하지 않고 시간을 보내기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;20개쯤 탈락했을 때는 자기소개서에 문제가 있는 게 아닌가 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 SSAFY 수료생을 대상으로 하는 취업 스터디에 참가했다. 이 스터디는 원래 모르는 SSAFY 동기들과 진행했어야 하나, 우리 반에는 아직 취업을 하지 않은 동료들이 많이 있었다. 그래서 이들과 같이 지원하여 함께 스터디를 진행하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로의 포트폴리오를 첨삭해 주고, 자기소개서를 첨삭해 주는 식으로 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 많은 걸 느꼈다. 다른 사람들이 자기소개서를 어떻게 작성하고 있으며, 내가 자기소개서에서 부족한 점은 무엇이었는지를 많이 깨달았던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고는 이를 서류에 반영하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwgjc4/btsK40OKVuK/T9tun6mVQEl5J3UM7f1ej1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwgjc4/btsK40OKVuK/T9tun6mVQEl5J3UM7f1ej1/img.png&quot; data-alt=&quot;노션 댓글 기능을 이용하여 피드백 받았다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwgjc4/btsK40OKVuK/T9tun6mVQEl5J3UM7f1ej1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwgjc4%2FbtsK40OKVuK%2FT9tun6mVQEl5J3UM7f1ej1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;서류 합격을 위한 피드백&quot; loading=&quot;lazy&quot; width=&quot;539&quot; height=&quot;708&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노션 댓글 기능을 이용하여 피드백 받았다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서합률이 좀 괜찮아졌냐고?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니 전혀  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서류의 문제가 아니라 스펙의 문제인 듯싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;30개쯤 탈락했을 때는 자기소개서에 투자하는 시간을 많이 줄였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 쌓인 많은 자기소개서에서 복붙을 해가며 시간을 단축하고, 많이 쓰다 보니 노하우가 늘어서 시간을 많이 단축했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 가장 영향을 많이 끼쳤던 컨설턴트님의 말, &lt;b&gt;&quot;서류 하나에 너무 많은 시간을 투자하지 마라&quot;&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;써야 할 기업은 많고, 떨어지면 멘탈 관리에도 힘들다. 많은 시간을 투자하지 말고, 적절히 멘탈 관리하면서 하라는 충고였다. 이 말이 많이 도움이 됐다.&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;그래서 내 서합률이 어떠냐고? 24년 하반기 서류 합격 자기소개서는 7개.&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;대략 7%?  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;아까울 거 같아&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서류가 종종 안 붙으면 그러려니 하겠는데, 아예 붙지를 않으니까 눈을 낮추려 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 나랑 같이 노션으로 공고를 정리해 가며 지원 현황을 공유하던 친구가 있었는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 답답한 마음에 이 친구에게 눈을 낮춰서 더 다양한 기업에 지원서를 넣어야 할 거 같다고 말했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그랬더니 돌아오는 대답이, &lt;b&gt;&quot;아까울 거 같아&quot;&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 친구는 나랑 SSAFY를 같이 나왔기 때문에, 내가 어떤 사람인지 너무 잘 알고 있었다. 그간 보여준 내 모습들, 실력들을 보았을 때, 너무 아까울 거 같다고 말을 해줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqDcOA/btsK6b9Rg2z/48lLkjmzKjSwXrsvWu3Wck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqDcOA/btsK6b9Rg2z/48lLkjmzKjSwXrsvWu3Wck/img.png&quot; data-alt=&quot;너 덕분에 포기하지 않을 수 있었던거 같아&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqDcOA/btsK6b9Rg2z/48lLkjmzKjSwXrsvWu3Wck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqDcOA%2FbtsK6b9Rg2z%2F48lLkjmzKjSwXrsvWu3Wck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;아깝다고 해준 친구 ㅠㅠ&quot; loading=&quot;lazy&quot; width=&quot;321&quot; height=&quot;319&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;너 덕분에 포기하지 않을 수 있었던거 같아&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 포기를 한 시즌만 늦추기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 시즌까지는 죽어도 내가 가고 싶어 하는 기업만 넣기로 했다. 그 이후에는 되든 안 되든 눈을 낮추어서 어디든 가겠다고 다짐했다. 가서 계단식으로 더 높이 올라가면 되니까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말을 듣고, 가장 먼저 든 생각은 인트로에서 말했던,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&quot;난 내가 내 꿈의 근처라도 가보고는 죽어야지 싶더라고&quot;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;라는&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt; 가삿말이다.&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: left;&quot;&gt;그리고 서류에 번번이 탈락할 때도, 이 친구가 해줬던 아까울 거 같다는 말과 이 가삿말이 자꾸만 떠올랐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;힘들지만 그냥 죽은 셈 치고 버텼던 거 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기회는 꼭 한꺼번에 찾아오더라&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자소서 폭탄이 슬슬 마무리되는 시점인 10월.&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;대략 4~5개 기업의 코딩테스트를 진행했고, SKCT라는 시험도 처음으로 치르게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트야 워낙에 자신감이 넘쳤고, SKCT는 무슨 시험인지도 모르는데 어떡하지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 그냥 무작정 책을 사서 풀었다. 그렇게 많은 시간을 투자하지는 않았지만, 에듀윌 SKCT 책을 사서 1 회독은 한 거 같다. 이후에는 모의고사 위주로 준비를 하며, 타이머를 켜두고 과목당 못해도 12문제는 풀 수 있게 반복적으로 훈련했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고로 20문제에 15분씩 4~5과목을 시험 본다.. 12개 이상은 풀어야 하지만 한계라.. ㅠ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, SKCT는 아쉽게 탈락하게 되었고, 책은 바로 당근행  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;1669&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9TnLG/btsK6qk87rG/9DeWRKxuAneeKwFj43Mfw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9TnLG/btsK6qk87rG/9DeWRKxuAneeKwFj43Mfw1/img.png&quot; data-alt=&quot;당근행 SKCT&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9TnLG/btsK6qk87rG/9DeWRKxuAneeKwFj43Mfw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9TnLG%2FbtsK6qk87rG%2F9DeWRKxuAneeKwFj43Mfw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;347&quot; height=&quot;1669&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;1669&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;당근행 SKCT&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 안 팔렸다 ㅠ. 책은 더럽게 비싸서 26,000원인가? 주고 샀던 거 같다.&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;이때 붙은 코딩테스트 기업들 같은 경우에는 SQL 문제가 출제되는 기업들이 있어서, 일주일정도 하루에 2문제씩은 연습했던 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2358&quot; data-origin-height=&quot;2120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dae60K/btsK4QS9gE4/D17ayVtArEEj0TOrdSzH21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dae60K/btsK4QS9gE4/D17ayVtArEEj0TOrdSzH21/img.png&quot; data-alt=&quot;나름 바빴던 10월, 11월&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dae60K/btsK4QS9gE4/D17ayVtArEEj0TOrdSzH21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdae60K%2FbtsK4QS9gE4%2FD17ayVtArEEj0TOrdSzH21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;2120&quot; data-origin-width=&quot;2358&quot; data-origin-height=&quot;2120&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나름 바빴던 10월, 11월&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일정을 보면 정말 지독했다. 기업 이름 지우고, 인적성 검사 지우니까 뭐 없어 보이긴 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루에 두 번 코테를 보는 날도 있었고, 예비군도 다녀왔으며,&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;&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;일정에는 안 나와있지만 이번 시즌에는 총 9번의 기업 코딩테스트를 진행했다.&lt;/p&gt;
&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;난 다대일은 찢겨...&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 면접 경험은 이번 시즌 전까지 두 번의 온라인 면접이 다였다. 모두 다대일 면접이었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대면 면접은 오로지 싸피에 들어올 때... 9기 때 한 번 떨어지고 10기 붙었으니까.. 그렇게 두 번...?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그게 다였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에 대면 면접이 잡히자, 너무나 떨렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 이 대면 면접에서 가장 걱정했던 것이 무엇일 거 같나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 대답을 해야 내가 어필되나? 자기소개는 어떤 걸 준비해야 하나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2892&quot; data-origin-height=&quot;3020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwuo1C/btsK4NhRU0k/qrmmgeRJr26GkwrDrPNfIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwuo1C/btsK4NhRU0k/qrmmgeRJr26GkwrDrPNfIk/img.png&quot; data-alt=&quot;내 영혼의 단짝&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwuo1C/btsK4NhRU0k/qrmmgeRJr26GkwrDrPNfIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwuo1C%2FbtsK4NhRU0k%2FqrmmgeRJr26GkwrDrPNfIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;우황청심환&quot; loading=&quot;lazy&quot; width=&quot;424&quot; height=&quot;3020&quot; data-origin-width=&quot;2892&quot; data-origin-height=&quot;3020&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내 영혼의 단짝&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 면접은 다대일이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대면으로 진행하는 첫 면접이기도 하고, 구두는 너무 딱딱해서 발이 아프고, 정장도 처음 입어보는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 어색하고 떨려서 미칠 거 같았다. 그래서 청심환도 먹었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고는 그냥 시원하게 말아먹었다. 청심환을 먹었지만 달달달 떨려서 대답도 잘 안 나오고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무슨 건조기를 먹은 것처럼 목이 계속 타들어가서 대답 중간중간 계속 큼큼거렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 다대일 면접의 특징상 기술적인 꼬리질문이 많이 들어오는데, 이에 대해 충분하게 대답을 하지 못했다. 아직 많이 부족했나 보다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 나의 가장 큰 장점이 뭔가!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패를 통해 배우기! 면접을 복기하면서 무조건 떨어졌다는 생각에 상당히 힘들었지만, 중간에 큼큼거리지 말자는 좋은 교훈을 얻었다.&amp;nbsp;&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그치만 다대다를 찢어&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연속된 면접 속에서 첫 면접을 보는 기업이 상당히 괜찮아 보여서, 내가 투자할 수 있는 시간 중에 꽤나 많은 부분을 투자했다. 면접 보기 전 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;따라서 별로 안 떨렸다. 그냥 면접비나 받고 오는 셈 치자~ 좋은 경험 쌓은 셈 치자~ 이런 느낌으로 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다대다는 또 생전 처음이기도 하고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어, 얼마나 준비를 안 했으면 면접장 가는 셔틀버스 안에서 급하게 지원동기를 준비하기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데, 셔틀버스에서 다른 지원자 중에 익숙한 뒤통수가 있어서 봤더니 싸피에서 친하게 지낸 친구를 만났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때부터는 그냥 무슨 여행 가는 것 마냥 기분이 붕 뜨기 시작했다. 물론 떨릴까 봐 청심원은 먹었지만..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1673&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK1Vk2/btsK7bVmn9g/DIaYM3ZXTITLKFY8mPb6f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK1Vk2/btsK7bVmn9g/DIaYM3ZXTITLKFY8mPb6f0/img.png&quot; data-alt=&quot;알아보면 곤란하니까, 이정도만 올릴게&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK1Vk2/btsK7bVmn9g/DIaYM3ZXTITLKFY8mPb6f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK1Vk2%2FbtsK7bVmn9g%2FDIaYM3ZXTITLKFY8mPb6f0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;389&quot; height=&quot;774&quot; data-origin-width=&quot;1673&quot; data-origin-height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;알아보면 곤란하니까, 이정도만 올릴게&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 다대다를 들어갔는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 무슨 일이지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각보다 너무 잘 봤다. 옆사람과 옆옆사람은 모두 말을 너무 빠르게 해서, 개인적으로 나는 집중하기 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대기시간이 길고 청심원 효과 때문에 졸려서 그런가? 싶었을 정도로.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 어차피 준비도 별로 못해서 대답이 한정되어 있었는데, 다대다가 적성에 잘 맞는 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옆사람이 대기하는 시간에 머리가 너무 잘 굴러갔다. 내가 생각해도 조금 돋보이는,&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;이다음 면접 역시 다대다 면접이었다. 여기서도 싸피에서 같이 프로젝트를 진행했던 형을 만났다. 너무 반가웠고 긴장이 싹 풀리더라. 심지어 내가 첫 순서였는데, 나 말고 다른 지원자들이 불참해서 강제로 다대일로 면접을 보게 되었다.&lt;/p&gt;
&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;단 10분만 진행하는 면접으로 무얼 평가하는 거지? 싶은 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P1A2s/btsK5DFyQwH/XniSuMkMpoTdAHgXJCrq3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P1A2s/btsK5DFyQwH/XniSuMkMpoTdAHgXJCrq3K/img.png&quot; data-alt=&quot;다대다가 다대일이 되는 마법..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P1A2s/btsK5DFyQwH/XniSuMkMpoTdAHgXJCrq3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP1A2s%2FbtsK5DFyQwH%2FXniSuMkMpoTdAHgXJCrq3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;301&quot; height=&quot;189&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다대다가 다대일이 되는 마법..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 다대다 면접은 개인적으로 조금 어이가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입사하면 걱정되는 것이 무엇이냐길래, 지원자들은 여러 가지 대답을 했다. 그랬더니 면접관분께서, 저였으면 &quot;이렇게&quot; 대답할 거 같아요~라고 말씀하셨다. 이게 가장 신입다운 거라면서... 뭔가 가장 어이없는 면접 경험이었고, 기대도 안 하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;슬슬 돌아가는 행복회로도 잠시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종이 세 개, 원데이 면접 기업까지 네 개나 되니까 슬슬 머리가 망가지기 시작했다. 릴스에서 맛집들 보이면 저장해 두고, 나 도와줬던 사람들에게 한 턱 쏴야지... 이런 생각이나 하고 있고 말이야.  &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;&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;어느 원데이로 진행되는 면접은 아침 06시까지 가야 해서 집결지 근처에 숙소를 잡고 면접을 보러 간 적도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4922&quot; data-origin-height=&quot;2210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mby0E/btsK4LYTguo/qqvnH9fjXrKW5FCQrGKMLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mby0E/btsK4LYTguo/qqvnH9fjXrKW5FCQrGKMLK/img.png&quot; data-alt=&quot;바리바리 싸들고 숙소로..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mby0E/btsK4LYTguo/qqvnH9fjXrKW5FCQrGKMLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmby0E%2FbtsK4LYTguo%2FqqvnH9fjXrKW5FCQrGKMLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;수많은 짐을 들고 숙소로&quot; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;2210&quot; data-origin-width=&quot;4922&quot; data-origin-height=&quot;2210&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바리바리 싸들고 숙소로..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때도 싸피에서 만난 형과 운이 좋게 시간이 겹쳤는데, 덕분에 숙소비는 아낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이 면접 역시 다대일이었고... 역시나 찢겼다 ㅠㅠ... 글을 쓰고 있는 지금도 결과는 안 나왔지만, 안 봐도 결과가 훤하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어 얼마나 찢겼냐면, 옛날 개그콘서트에서 땀나는 연출을 위해 머리띠 같은 걸 쓰는 거 아나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;135&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CNYTW/btsK5BacLIX/two6slpfVNq8LIiloPw98k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CNYTW/btsK5BacLIX/two6slpfVNq8LIiloPw98k/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CNYTW/btsK5BacLIX/two6slpfVNq8LIiloPw98k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCNYTW%2FbtsK5BacLIX%2Ftwo6slpfVNq8LIiloPw98k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;땀나는 개그&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;135&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;135&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;그리고는 정말 말로만 듣던 피뽑탈을 경험했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피뽑탈은 상상 속의 동물이라고만 생각했는데, 이게 실존했을 줄이야.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2981&quot; data-origin-height=&quot;2344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v4Lfs/btsK6SIvrLi/6TKSvpTdq475SZKqwjHuKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v4Lfs/btsK6SIvrLi/6TKSvpTdq475SZKqwjHuKK/img.png&quot; data-alt=&quot;상상속의 동물 피뽑탈&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v4Lfs/btsK6SIvrLi/6TKSvpTdq475SZKqwjHuKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv4Lfs%2FbtsK6SIvrLi%2F6TKSvpTdq475SZKqwjHuKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;415&quot; height=&quot;2344&quot; data-origin-width=&quot;2981&quot; data-origin-height=&quot;2344&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상상속의 동물 피뽑탈&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 원데이 면접은 망해버렸고, 남은 하나마저 탈락 발표가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 남은 희망은 한 곳... 이제 앞으로 어떻게 더 이 생활을 버텨 나가지?라는 고민밖에 들지 않았다.&amp;nbsp;&lt;/p&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그리고... 끝.&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길고도 막막했던, 험난하고도 도전적이었던, 다시는 돌아가고 싶지 않은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업준비생 생활이 막을 내렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20241204_002757812_01 (소형).jpg&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzhaMA/btsK4WeM17E/IgilmFlMtzybSFQOJZovQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzhaMA/btsK4WeM17E/IgilmFlMtzybSFQOJZovQ1/img.jpg&quot; data-alt=&quot;내 소중한 친구들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzhaMA/btsK4WeM17E/IgilmFlMtzybSFQOJZovQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzhaMA%2FbtsK4WeM17E%2FIgilmFlMtzybSFQOJZovQ1%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;내가 먹은 청심원들&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;348&quot; data-filename=&quot;KakaoTalk_20241204_002757812_01 (소형).jpg&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내 소중한 친구들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;70개의 서류, 58번의 서류탈락&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12번의 코테, 6번의 코테탈락&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9번의 1차 면접, 4번의 1차 탈락&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번의 2차 면접, 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;그리고 1개의 최종 합격&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결국 단 한 번만 기회를 잡으면 된다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 한 번도, 싸피가 없었으면 나에겐 오지 않았을 기회라고 생각한다. 싸피의 서류면제 혜택 덕분에 코테를 볼 수 있었고, 코테는 싸피에서 워낙 준비를 많이 해놨기에 면접까지 갈 수 있었으니까..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이 한 번을 위해 싸피를 두 번이나 도전했고, 이 안에서의 수많은 노력을 말로 다 표현할 수 없다. 결국 싸피를 선택한 것도 다 이 한 번을 위해서였지 않았나.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합격 발표가 나자마자 나를 응원해 주던 친구들이 가장 먼저 생각나서 바로 연락을 돌렸고, 합격 소식을 말해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힘든 취준생활을 잘 버티라고 가끔 밥도 사주고 술도 사주던 형 친구 동생들인데, 다들 내 일처럼 기뻐해주더라. 축하해 주더라. 고마운 사람들.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;299&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byl2JP/btsK6Q4ZCgF/oKXiTaxNimPLF3cbSwmH51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byl2JP/btsK6Q4ZCgF/oKXiTaxNimPLF3cbSwmH51/img.png&quot; data-alt=&quot;형 덕분이라고~ 입 아프다 ~&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byl2JP/btsK6Q4ZCgF/oKXiTaxNimPLF3cbSwmH51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyl2JP%2FbtsK6Q4ZCgF%2FoKXiTaxNimPLF3cbSwmH51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;299&quot; height=&quot;165&quot; data-origin-width=&quot;299&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;형 덕분이라고~ 입 아프다 ~&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아 그리고, 도움을 정말 많이 주신 컨설턴트님께도 바로 연락을 드렸다. 하도 컨설팅 신청을 많이해서 이제는 좀 친해졌는데, 합격 소식을 알려드리자 정말 진심으로 축하해주셨다. 이젠 연락드릴 일이 없어지겠지 ~..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqsB6O/btsK5ooB1jd/eo4TLhuyCCRJK57fhKmaCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqsB6O/btsK5ooB1jd/eo4TLhuyCCRJK57fhKmaCK/img.png&quot; data-alt=&quot;이만큼이나 친해졌다 ㅋㅋ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqsB6O/btsK5ooB1jd/eo4TLhuyCCRJK57fhKmaCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqsB6O%2FbtsK5ooB1jd%2Feo4TLhuyCCRJK57fhKmaCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;296&quot; height=&quot;276&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이만큼이나 친해졌다 ㅋㅋ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;295&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ov51C/btsK6b9RV3W/bbnVbUK4cPvuLevlD0znAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ov51C/btsK6b9RV3W/bbnVbUK4cPvuLevlD0znAK/img.png&quot; data-alt=&quot;다 이유가 있어..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ov51C/btsK6b9RV3W/bbnVbUK4cPvuLevlD0znAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fov51C%2FbtsK6b9RV3W%2FbbnVbUK4cPvuLevlD0znAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;295&quot; height=&quot;188&quot; data-origin-width=&quot;295&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다 이유가 있어..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌 됐든 내 주변엔 이렇게 좋은 사람들이 많았고,&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;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1751&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k70Qo/btsK6ywKdsw/rCJYwJbgNS4kMIITa5u5G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k70Qo/btsK6ywKdsw/rCJYwJbgNS4kMIITa5u5G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k70Qo/btsK6ywKdsw/rCJYwJbgNS4kMIITa5u5G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk70Qo%2FbtsK6ywKdsw%2FrCJYwJbgNS4kMIITa5u5G1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1751&quot; height=&quot;784&quot; data-origin-width=&quot;1751&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pUnDN/btsK5Zn2jlp/Q5lhna6tKKIVzBPrJOT9B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pUnDN/btsK5Zn2jlp/Q5lhna6tKKIVzBPrJOT9B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pUnDN/btsK5Zn2jlp/Q5lhna6tKKIVzBPrJOT9B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpUnDN%2FbtsK5Zn2jlp%2FQ5lhna6tKKIVzBPrJOT9B0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;856&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-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>else/회고</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/99</guid>
      <comments>https://olrlobt.tistory.com/99#entry99comment</comments>
      <pubDate>Wed, 4 Dec 2024 06:19:05 +0900</pubDate>
    </item>
    <item>
      <title>[인증인가] Access Token, Refresh Token의 저장 위치에 대한 고찰</title>
      <link>https://olrlobt.tistory.com/98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트폴리오용 프로젝트를 진행하면서 인증인가 부분은 가장 기피되는 역할 중 하나라 생각한다. 오류가 가장 많이 발생하는 부분이며, 시큐리티를 적용하는 과정 또한 만만치 않은 것이 주된 원인이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 수많은 프로젝트를 진행했지만 인증인가를 맡아본 경험은 없었다. 딱히 기피한 건 아니고.. 더 하고 싶은 부분이 있었을 뿐이지만...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 이번에 좋은 기회가 생겨서 인증인가 부분을 맡게 되었는데, 그간 인증인가를 맡은 친구들이 왜 하나같이 화를 냈는지 이해가 갈 수밖에 없었다. 항상 개발에는 정답이 없다고 수도 없이 들어왔지만, 관습처럼 써 오던 부분은 어느샌가 정답이 되어 고민도 안 하고 사용하고 있었는데, 이 인증인가 부분은 정말로&lt;b&gt; 정답이 없었다.&lt;/b&gt; 수많은 보안적인 고민을 해야 하며, 어떤 방법으로 보안을 더 강화할지, 왜 이 방법을 선택하는지 끊임없이 고민하고 그 이유를 찾아야만 했다. 수많은 포스팅과 개발자들도 어느 한 방법만을 소개할 뿐, 어떤 것이 정답이라고는 말하고 있지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 이 포스팅에서는 내가 선택한 인증 방식과 그 이유에 대해 고민한 흔적들을 작성해 보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다시 한번 말하지만, 정답은 없다!!&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;사용자 인증&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 인증은 시스템이나 애플리케이션에 접근하려는 사용자가 &lt;b&gt;실제로 그 사용자가 맞는지 확인하는 과정&lt;/b&gt;이다. 이런 과정이 없다면, 사용자의 정보를 마음대로 악용할 가능성이 생기므로 보안적으로 중요한 요소가 아닐 수 없다. 또한, 사용자 경험과 확장성도 고려해야 한다. 이미 사용자 인증을 마친 회원이 페이지를 이동할 때마다 사용자 인증을 하게 된다면 사용자에게는 굉장히 불편한 서비스가 될 것이며, 서비스가 성장함에 따라 확장에 유연하지 못하다면, 확장 비용을 무시할 수 없게 된다. 따라서, 사용자 인증 방식들을 &lt;b&gt;보안성, 사용자 경험, 확장성을 중심으로 비교해 보고&lt;/b&gt; &lt;b&gt;선택&lt;/b&gt;하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 인증은 크게 세션 인증 방식과 토큰 인증 방식으로 나눌 수 있으며, 각각의 방식에는 장단점이 존재한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 포스팅에서는 쿠키 인증 방식까지도 다루는데, 사실 쿠키에 세션 ID를 담으면 세션 인증 방식, 쿠키에 토큰을 담으면 토큰 인증 방식이 되어버리므로, 해당 포스팅에서는 다루지 않겠다.&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;/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;하지만 개발에는 정답이 없고, 내 프로젝트와 요구사항에 알맞은 기술을 선택하는 것이 가장 중요하기 때문에 두 방식을 자세히 뜯어보고 각각을 사용하는 이유에 대해 알아볼 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;세션 인증 방식의 특징&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 인증 방식의 가장 큰 특징은 &lt;b&gt;서버에 상태를 저장&lt;/b&gt;하는 &lt;b&gt;상태 유지(stateful)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 로그인을 서버로 요청하면, 요청 정보가 DB와 일치하는지 확인하고 사용자의 세션을 생성한다. 이때 생성한 세션 ID는 서버의 저장소(메모리나 DB)에 저장되는데, 이 값을 서버에 저장하기 때문에 stateful이라 한다. 이 후로는 쿠키에 담아 사용자의 후속 요청마다 세션 ID가 담긴 쿠키를 서버에 전송하게 되고, 해당 세션 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;이러한 방식은 서버에서 인증을 관리하기 때문에, 클라이언트 측에서 정보가 노출될 위험이 적다는 보안적인 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 빈번히 발생하는 사용자의 요청마다 DB에 접근한다는 것은 응답 속도 저하를 일으키는 원인이 되며, 이러한 요청이 수백, 수천만 건이 된다면 서버에 부하를 일으키는 원인까지 될 수 있다. 또한, 요즘은 금융권도 MSA를 도입할 만큼 분산 환경을 자주 볼 수 있는데, 이런 환경에서 세션의 일관성을 유지하기 위해 중앙 저장소를 사용하거나 추가적인 작업을 해야 한다는 점은 세션 인증 방식의 확장성 면에서 큰 단점으로 꼽힐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cO9nAO/btsK39QMaPh/hSI6Zm2i7Se4vd3kkWdKLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cO9nAO/btsK39QMaPh/hSI6Zm2i7Se4vd3kkWdKLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cO9nAO/btsK39QMaPh/hSI6Zm2i7Se4vd3kkWdKLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcO9nAO%2FbtsK39QMaPh%2FhSI6Zm2i7Se4vd3kkWdKLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;stateful 세션 인증 예시&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;245&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;토큰 인증 방식을 사용하는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 기반 인증은 세션 인증 방식과 반대로 &lt;b&gt;서버에 상태를 저장하지 않&lt;/b&gt;는 &lt;b&gt;상태 비저장(stateless)&lt;/b&gt;이 가장 큰 특징이다. 즉, 서버는 사용자의 정보를 검증하기 위해 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DB를 거치지 않고&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;b&gt;오로지 토큰만을 사용한 방법으로 사용자를 검증&lt;/b&gt;한다. 어떻게 토큰만을 사용하여 검증을 진행하는지는 아래에서 살펴보도록 하고, 이 문단에서는 토큰 인증 방식을 사용하는 이유에 대해 집중해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 세션 인증 방식의 가장 큰 단점은 응답 속도 저하로 인한 서버 부하(사용자 경험 저하)와 확장에 어려움이 있다는 점이다. 반면, 토큰 인증 방식은 DB를 거치지 않고 오로지 토큰만을 사용하여 인증하기 때문에 응답속도 저하 문제를 해결할 수 있으며, 확장성면에서도 확실히 우수한 면을 보인다. 또한, 모바일 환경에서는 IP로 세션을 관리하는 경우도 있는데, 이런 경우에는 토큰 방식이 더 선호된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, 세션 인증 방식에 비해 응답속도, 확장성 면에서 우수하여 토큰 방식을 사용하는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7w21y/btsK2Z9o3Q3/okKFFwxJNKjFKezA4AtEk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7w21y/btsK2Z9o3Q3/okKFFwxJNKjFKezA4AtEk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7w21y/btsK2Z9o3Q3/okKFFwxJNKjFKezA4AtEk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7w21y%2FbtsK2Z9o3Q3%2FokKFFwxJNKjFKezA4AtEk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;stateless 토큰 인증 예시&quot; loading=&quot;lazy&quot; width=&quot;504&quot; height=&quot;470&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;세션이 서버에서 관리되는 장점과 상반되는 이유이다. 클라이언트 자체에서 탈취되거나, 악성코드(XSS)등으로 탈취될 가능성도 있고, 클라이언트에서 서버로 전송되는 동안에도 탈취될 가능성이 있다. 만약 해커에게 토큰이 탈취되었다면, 이를 서버 측에서 강제로 해제할 수 있는 방법이 명확하지 않다. 블랙리스트라는 방법으로 토큰을 무효화시킬 수 있지만, 어디까지나 토큰이 탈취된 것을 감지했을 때의 이야기일 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Access Token&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 인증방식은 다양한 방법이 있지만, 주로 OAuth 2.0 프로토콜에서 사용되는 Access Token을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token은 &lt;b&gt;사용자의 인증과 권한&lt;/b&gt;을 나타내는 &lt;b&gt;단기 유효 토큰(문자열)으로,&lt;/b&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;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;기존&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;세션&amp;nbsp; &lt;/b&gt;방식은&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서버가 인증 정보를 관리&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;했다면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Access Token&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;클라이언트가 인증 정보를 직접 관리&lt;/b&gt;할 수 있도록 한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;물론, Access Token의 경우에도 서버에 인증 정보를 관리할 수 있지만, 앞서 세션 방식의 단점을 그대로 안고 가기 때문에 권장되는 방식이 아니다.&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;그렇다면 Access Token은 어떤 식으로 구성되어 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token과 JWT&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Access Token은 문자열 형식&lt;/b&gt;으로 구성되어 있으며, 발급받는 토큰 종류에 따라 구조화된 모양일 수 있고 해석할 수 없는 단순한 문자열일 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이때 다양한 토큰 형식 중, &lt;b&gt;JWT(JSON Web Token)&lt;/b&gt; 형식이 &lt;b&gt;stateless(상태 저장 없음)&lt;/b&gt;하다는 장점 덕분에 주로 사용된다. stateless 하다는 특성은 다시 말해, 서버에서 Access Token 자체를 저장할 필요가 없어진 것으로, DB 통신이 없어질뿐더러 서버 인스턴스가 여러 개&amp;nbsp;있을 때에도 쉽게 확장할 수 있다는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이런 Stateless 한 특징은 JWT의 구조에서 비롯된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;JWT의 구조는 크게 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;헤더(Header), 페이로드(Payload), 서명(Signature)으로 구성되어 있으며, 한 줄의 문자열에서. (점)을 이용해 세 부분으로 구분된다.&amp;nbsp;&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;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;534&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4h7XB/btsK20mTO3Z/77O34mriIdk9BOpkItvKHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4h7XB/btsK20mTO3Z/77O34mriIdk9BOpkItvKHK/img.png&quot; data-alt=&quot;JWT 구성 요약&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4h7XB/btsK20mTO3Z/77O34mriIdk9BOpkItvKHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4h7XB%2FbtsK20mTO3Z%2F77O34mriIdk9BOpkItvKHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;JWT는 헤더와 페이로드, 서명부분으로 이루어진다.&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;534&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;534&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JWT 구성 요약&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;&lt;b&gt;헤더&lt;/b&gt;에는 토큰의 유형과 서명 알고리즘을 포함하며,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;페이로드&lt;/b&gt; 부분에는 사용자 정보 및 클레임(사용자 ID, 권한, 만료시간 등)이 포함된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 페이로드 부분 덕분에 &lt;b&gt;서버가 별도의 DB 조회 없이도 사용자의 정보를 확인&lt;/b&gt;할 수 있어서, 요청 처리 속도가 빨라지고 서버의 부하가 줄어들게 된다. 이러한 특징을 Self-contained(자체 포함)이라 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서명 &lt;/b&gt;부분은 헤더와 페이로드를 조합한 후, 헤더의 서명 알고리즘과 비밀 키를 사용하여 생성되는 부분이다. 이 서명 덕분에 &lt;b&gt;클라이언트가 토큰을 변조하지 않았음을 검증&lt;/b&gt;할 수 있고, 신뢰할 수 있는 토큰임을 보장하게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #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;최종적으로, 이 세 부분은 Base64 Url로 인코딩 되어 하나의 문자열로 결합되어 JWT 토큰을 구성하게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 구성된 JWT는 필요한 모든 정보와 변조 감지를 위한 서명 부분을 토큰 자체에 포함하고 있으므로, 서버에서 별도의 세션 저장소를 유지할 필요가 없어진다. 단순히 클라이언트가 JWT를 가지고 있으면, 서버는 이 토큰을 검증하여 사용자의 인증 상태를 확인할 수 있는 것이다.&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;또한, JWT는 IETF의 RFC 7519에 의해 표준화되어 있어, 다양한 라이브러리와 도구가 지원되는데, 이는 개발자들이 JWT를 쉽게 구현하고 사용할 수 있도록 많은 도움을 준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 이유들로, JWT는 Access Token에서 널리 사용되는 방식이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 앞서 클라이언트가 JWT를 가지고만 있으면 인증이 된다고 하였는데, 이 JWT를 어떻게 저장하고 관리하는 지를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token 저장 위치&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서 Access Token을 이용하여 인증 정보(JWT)를 관리하는 방식으로는 로컬 스토리지(Local Storage), 세션 스토리지(Session Storage), 쿠키(Cookies), 메모리(Memory)에 저장하는 방식으로 나눌 수 있다.&lt;/p&gt;
&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. 로컬 스토리지 (Local Storage) [권장 X]&lt;/b&gt;&lt;br /&gt;로컬 스토리지는 클라이언트 측에 데이터를 영구적으로 저장하는 방법이다. 브라우저를 닫아도 데이터가 유지되며, 명시적으로 삭제하지 않는 한 계속 존재한다. 그러나 &lt;b&gt;XSS&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(교차 사이트 스크립팅, JavaScript 기반 악성 코드를 브라우저에서 실행되도록 하는 공격 방식)&lt;/span&gt; 공격에 취약&lt;/b&gt;하여, 악성 스크립트가 로컬 스토리지에 저장된 액세스 토큰을 탈취할 수 있는 위험이 있다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 세션 스토리지 (Session Storage) &lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[권장 X]&lt;/b&gt; &lt;/b&gt;&lt;br /&gt;세션 스토리지는 브라우저가 닫히면 데이터가 삭제되며, 같은 탭에서만 유효하다는 특징이 있다. 로컬 스토리지보다는 상대적으로 보안 측면에서 안전하지만, 여전히 &lt;b&gt;XSS 공격에 취약&lt;/b&gt;하다는 단점이 있다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3.&amp;nbsp;쿠키&amp;nbsp;(Cookies)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠키는 기본적으로 HTTP의 무상태(Stateless) 특성을 보완하기 위해 등장한 데이터 쪼가리다. 따라서, 모든 클라이언트 요청에 자동적으로 포함된다는 특징이 있다. 이는 Access Token에 사용될 때도&amp;nbsp; 마찬가지로, 클라이언트에서 쿠키를 별도로 포함시키지 않아도 된다는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠키는 만료 날짜를 지정하는 영구 쿠키와, 브라우저가 닫힐 때 삭제되는 세션 쿠키로 구분된다. 홈페이지 특성에 따라 보안이 중요하다면, 세션 쿠키를 사용하는 것이 권장된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 쿠키는 앞선 두 방식에서의 XSS 공격 취약점을 어느 정도 예방할 수 있다. HttpOnly 플래그를 사용하여 JavaScript에서 쿠키에 접근할 수 없게 함으로써 XSS 공격에 대한 보안을 높이는 방식이다. 하지만, XSS공격은 다양한 방법으로 이루어 지므로 완벽하게 방어가 된다고 볼 수는 없으며, 추가적인 보안 조치가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;또한, Secure 플래그를 사용하면 HTTPS 연결에서만 쿠키가 전송되게 하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;중간자 공격(MITM)으로부터 쿠키를 보호할 수 있으며, SameSite 쿠키 속성을 설정하면 CSRF(교차 사이트 요청 위조) 공격을 방어할 수 있다.&lt;/p&gt;
&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. 메모리 (Memory)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token을 클라이언트 서버의 메모리에 저장하는 방식이다. 예를 들어, JavaScript의 private 변수에 JWT를 저장하는 것이다. 이렇게 하면, 매 요청 시마다 API 호출 시 Access Token에 접근이 쉬워지지만, 브라우저의 메모리는 세션 단위로 관리되기 때문에 페이지를 이동하면 Access Token이 소멸하는 문제가 발생한다. 따라서 이 방법은 SPA(&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Single Page Application)에서 주로 사용된다. 예를 들어 React를 사용하여 클라이언트 서버를 구현했다면, 페이지가 이동하는 것처럼 보여도 실제로는 이동하지 않기 때문에 private 변수가 그대로 유지된다. 하지만, 페이지를 새로고침한다면 private 변수가 소멸되기 때문에 다시 로그인을 해주어야 하는 상황이 발생한다.&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;메모리에 저장한다면 스크립트가 메모리 공간을 직접적으로 제어할 수 없기 때문에 XSS 공격에 의해 쉽게 탈취될 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다 해도 XSS 공격에 완벽히 안전한 것은 아니다. 만약 웹 애플리케이션 자체가 XSS 취약점을 가지고 있다면, 악의적인 스크립트로 Access Token을 탈취당할 위험성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 네 가지 방법 중 로컬 스토리지와 세션 스토리지의 경우, XSS 공격에 매우 취약하기 때문에 권장되지 않는 방식이다. 메모리와 쿠키의 경우에도 완벽히 안전하다고는 할 수 없지만, 다른 방식들에 비해서는 비교적 안전한 방식으로 많이 사용된다. 메모리 방식은 Access Token만 사용하는 경우에서 불편하게 보일 수 있지만, &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이후에 다루는 Refresh Token까지 고려해 본다면 충분히 안정적으로 사용 가능한 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 쿠키와 메모리 중 적절한 방법을 선택하고, 이에 맞는 보안 대책을 수립하는 것이 필수적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token 사용자 인증 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 JWT를 Access Token으로 주로 이용하는 이유를 알아보았다. 본격적으로 이 JWT를 통해 어떻게 사용자 인증이 이루어지는지를 알아보자. JWT와 쿠키를 이용한 Access Token 인증 방식은 간단하게 아래와 같은 플로우로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5rjid/btsK2xSQdaR/2wprw76BUBo9FzGVKKLsok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5rjid/btsK2xSQdaR/2wprw76BUBo9FzGVKKLsok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5rjid/btsK2xSQdaR/2wprw76BUBo9FzGVKKLsok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5rjid%2FbtsK2xSQdaR%2F2wprw76BUBo9FzGVKKLsok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Access Token을 이용한 사용자 인증 방식&quot; loading=&quot;lazy&quot; width=&quot;475&quot; height=&quot;417&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. Access Token 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 요청 시 JWT를 생성하여 클라이언트에게 반환하는 과정이다.&lt;/p&gt;
&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;br /&gt;&lt;b&gt;JWT 발급:&lt;/b&gt; 서버는 사용자의 인증 정보를 DB와 일치하는지 확인한 후, JWT를 생성하여 클라이언트에 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. &lt;b&gt;Access Token&lt;/b&gt; 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서는 JWT가 스스로 정보를 인증하므로 DB에서 사용자 정보와 일치하는지 검증할 필요가 없다.&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; 헤더에 JWT를&lt;/span&gt;&lt;/span&gt; 모든 요청에 포함하여 사용자 인증에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;후속 요청:&lt;/b&gt; 클라이언트가 서버에 요청을 보낼 때, 헤더에 Access Token을 포함한다. (쿠키의 경우 자동으로 포함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버에서&amp;nbsp;JWT&amp;nbsp;검증:&lt;/b&gt; 서버는 요청받은 Access Token에서 사용자의 인증 상태를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 인증 방식에서 Access Token의 사용이 무수히 많이 일어났다고 생각해 보자. 요청이 수백 번 일어나더라도, 서버에서 사용자 인증을 위해 DB에 접근하는 부분은 JWT를 발급하는 로그인 부분밖에 없다. 이러한 점이 JWT의 Stateless 한 특징이 가장 도드라지는 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token 로그아웃&lt;/b&gt;&lt;b&gt; 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이 구조에서 로그아웃은 어떻게 하면 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 인증이 이루어지는 부분을 생각해 보면, 단순히 Access Token을 사용하기 때문에 이 부분만 무효화해 주면 된다는 결론에 이를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 메모리의 경우 Access Token 제거하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 서버 private 변수에 Access Token을 저장한 경우, Access Token 자체를 제거해 주면 된다. 간단히 null 처리를 통해 로그아웃을 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. HTTP-Only 쿠키의 경우 Access Token 만료시키기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Http-only 쿠키의 경우에는 JavaScript로 쿠키에 접근하는 방법은 불가능하다. 따라서, 서버에 요청을 보내 로그아웃을 구현한다. JWT는 토큰 자체에 만료 시간을 포함할 수 있지만, 이미 발급된 JWT를 서버에서 직접 만료시키는 것은 불가능하기 때문에 쿠키의 만료시간을 강제하여 쿠키를 삭제하는 방법으로 로그아웃한다.&lt;/p&gt;
&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;letter-spacing: 0px;&quot;&gt;서버가 Set-Cookie 헤더에 Expires 또는 Max-Age 속성을 설정하여 쿠키를 제거하도록 지시한다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이렇게 하면 브라우저는 해당 쿠키를 삭제하므로, 클라이언트는 더 이상 Access Token을 사용할 수 없게 되며, 성공적으로 로그아웃 된다.&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;size18&quot;&gt;&lt;b&gt;3. BlackList에 추가하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 토큰을 무효화시키는 방법이다. 서버에서 발급한 JWT를 블랙리스트에 추가하여 해당 토큰의 유효성을 없앤다. 이 방법은 추가적인 저장소를 필요로 한다는 단점이 있으며, 매번 블랙리스트에 있는 Access Token인지 검증해야 할 필요가 생기므로 오버헤드가 발생한다. 또한, JWT를 블랙리스트에 저장하는 과정에서 Stateless 하다는 장점을 잃게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token 탈취&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게만 하면, 간단하게 로그인부터 로그아웃까지 Access Token 하나만으로 구현이 가능하다. 하지만, Access Token 하나만을 사용한 상황에서 해커가 Access Token을 탈취하게 되면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token은 토큰 자체 하나만으로 사용자가 인증되는 강력한 기능을 갖고 있다. 따라서 이 토큰이 탈취된다면, Access Token으로 접근한 요청이 사용자의 요청인지, 해커의 요청인지 구분할 수 없게 되어 피해를 막을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1097&quot; data-origin-height=&quot;914&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVt8PV/btsK2lSH2pJ/oa6TmNlZk40t1Rrr9pkM91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVt8PV/btsK2lSH2pJ/oa6TmNlZk40t1Rrr9pkM91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVt8PV/btsK2lSH2pJ/oa6TmNlZk40t1Rrr9pkM91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVt8PV%2FbtsK2lSH2pJ%2Foa6TmNlZk40t1Rrr9pkM91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Access Token이 탈취되었을 경우, 해커인지 사용자인지 구분할 수 없다.&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;914&quot; data-origin-width=&quot;1097&quot; data-origin-height=&quot;914&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token을 사용 못하게 하는 방법은 무효화하는 것이다. 하지만 서버는 해커가 가진 stateless 한 Access Token에 관한 정보가 하나도 없다. 따라서 만료 시간이 다 될 때까지 아무런 조치도 취할 수 없다. 심지어 Access Token을 하나만 사용한다면, 사용자의 편의를 위해 긴 만료 시간을 갖고 있을 것이다. 이 경우, 서버에서 취할 수 있는 유일한 조치가 로그아웃 부분에서 언급한 BlackList에 추가하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 BlackList에 추가하는 것 역시 이상 패턴이 발견됐을 경우의 이야기이므로, 이상 패턴이 발견되지 않는다면 피해는 막을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 반대로, Access Token의 만료시간을 짧게 하면 해커로부터의 피해를 최소화할 수 있지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커는 Access Token을 탈취하였기 때문에 만료시간까지 공격할 수 있겠지만, 금방 만료되어 버린 토큰은 사용이 불가능해지므로 피해가 최소화되는 효과를 가져온다. 하지만, 이는 사용자의 경우에도 마찬가지이다. 쇼핑을 하는데, 10분마다 다시 로그인을 하라고 한다면, 사용자 경험이 저하되어 사용자는 줄어들게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 이 토큰 유효기간의 딜레마를 해결하기 위해 Access Token과 함께 Refresh Token을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Refresh Token&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token은 Access Token과 함께 발급되는 별도의 토큰으로, &lt;b&gt;새로운 Access Token을 재발급받기 위해 사용되는 토큰&lt;/b&gt;이다. 보안을 위해 짧은 만료기간을 설정한 Access Token이 만료되었다면, 클라이언트는 긴&amp;nbsp; 만료 기간을 가진 Refresh Token의 유효성을 검증하고 인증 서버로부터 새 Access Token을 요청한다. 이를 통해, &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;따라서, 만료기간이 긴 Refresh Token이 탈취되었다면, 해커가 Access Token을 재발급받아 사용할 수 있는 위험성 또한 존재한다. 이것이 Access Token 보다 Refresh Token의 보안성이 더 중요하게 여겨지는 이유이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Refresh Token은 Access Token의 탈취 위험을 완벽하게 해소할 수 없다. Access Token의 유효 기간을 짧게 설정할 수 있게 함으로써, 해커에게 탈취당했을 때 피해를 최소화하는 것에 목적이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Refresh Token과 JWT&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token은 Access Token과 마찬가지로 JWT로 주로 구현한다. 이를 통해 토큰 하나만으로 Refresh Token의 유효성 검증을 진행할 수 있으며, 페이로드 부분에 저장된 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 최소한의 데이터(user_id 또는 token, id)만을 이용하여 DB에 접근한 후&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Access Token을 재발급한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Refresh Token에서 JWT의 Self-contained(자체 데이터 포함)한 특징을 이용하여 DB의 접근을 없앨 수 있지만, 이렇게 구성하면 Refresh Token의 목적인 Access Token의 유효성 연장 이외의 정보들을 포함하게 되며, 보안적으로 취약점이 생길 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 Self-contained 한 특성은 사용하지 않는 것이 권장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, Refresh Token은 어디에서 관리될 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Refresh&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;Token 저장 위치&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트에서 Refresh Token을 관리하는 방법으로는 Access Token과 마찬가지로 로컬 스토리지(Local Storage), 세션 스토리지(Session Storage), 쿠키(Cookies), 메모리(Memory)에 저장하는 방식으로 나눌 수 있다.&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;&lt;b&gt;1. 로컬 스토리지 (Local Storage) [권장 X]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;2. 세션 스토리지 (Session Storage)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[권장 X]&lt;/b&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;3. HTTP-only 쿠키 (Cookies)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 메모리 (Memory) &lt;b&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[권장 X]&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;서버에서 Refresh Token을 관리하는 방법은 주로 DB를 이용한 방법을 사용하며, 응답 속도를 빠르게 하기 위하여 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;b&gt;5. 서버 DB에서 관리 (Redis, RDBMS)&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;로컬 스토리지와 세션 스토리지의 경우는 앞서 XSS 공격의 취약점이 크기 때문에 Access Token에서도 권장하지 않는 방식이라 하였다. 메모리의 경우, 페이지 이동과 새로고침시 소멸하는 문제가 있어 Refresh Token에는 적합하지 않다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 쿠키보다는 서버가 보안적으로 우수한 방식인 것을 알 수 있고, 서버보다는 쿠키가 stateless 하다는 토큰 인증 방식의 특징을 잘 살린 것을 알 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, Refresh Token의 작동 방식과 탈취되었을 때를 알아보고, 더 적절한 방식을 고민해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;Refresh&amp;nbsp;Token의&amp;nbsp;작동&amp;nbsp;방식&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞서 Access Token은 클라이언트에 저장되어 있고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Refresh Token은 클라이언트 측(쿠키)에서 관리하는 경우와 서버에서 관리하는 경우로 나누었다.&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;따라서, 요청 시 클라이언트가 Access Token 하나만 보내는 경우와 Access Token과 Refresh Token을 같이 보내는 경우로&amp;nbsp; 나누어 작동 방식을 알아보자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2295&quot; data-origin-height=&quot;1224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbx5oi/btsK1TCyzzM/kGw5eKJUTXUdMKmNfJB1a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbx5oi/btsK1TCyzzM/kGw5eKJUTXUdMKmNfJB1a1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbx5oi/btsK1TCyzzM/kGw5eKJUTXUdMKmNfJB1a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcbx5oi%2FbtsK1TCyzzM%2FkGw5eKJUTXUdMKmNfJB1a1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;쿠키와 서버 각각에 Refresh Token을 저장했을 때, 작동 방식&quot; loading=&quot;lazy&quot; width=&quot;2295&quot; height=&quot;1224&quot; data-origin-width=&quot;2295&quot; data-origin-height=&quot;1224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. Access Token, Refresh Token 생성&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로그인 요청:&lt;/b&gt;&amp;nbsp;사용자가 로그인 정보를 입력하고 서버에 로그인 요청을 보낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;토큰&amp;nbsp;발급:&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서버는 사용자의 인증 정보를 DB와 일치하는지 확인한 후,&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token과 Refresh Token을 함께 발급한다. Access Token은 짧은 만료 시간을 가지며, Refresh Token은 상대적으로 긴 만료 시간을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이후, 클라이언트나 서버에 알맞은 위치로 저장한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Access Token&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&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;b&gt;API 요청:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;클라이언트는 API 요청 시 Access Token을 헤더에 포함하여 요청을 보낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버에서&amp;nbsp;JWT&amp;nbsp;검증:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서버는 요청받은 Access Token의 만료 기간을 확인한다. Access Token이 유효한 동안에는 정상적으로 요청이 처리된다.&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;여기까지는 앞서 살펴본 Access Token만 사용했을 경우와 동일하다.&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;size18&quot;&gt;&lt;b&gt;3. Access Token 만료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;API 요청:&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token이 만료되었다면, 클라이언트는 더 이상 API 요청을 수행할 수 없다. 이후 저장 위치에 따라 아래와 같이 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;클라이언트에 저장한 경우 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;클라이언트는 서버로부터 Access Token 만료 요청을 받아, Refresh Token을 요청에 포함하여 재요청한다. 쿠키의 경우에는 자동으로 Refresh Token이 요청에 포함되어 있으므로 이 과정이 생략된다. 이후, Refresh Token을 이용하여 재발급을 요청한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버에 저장한 경우 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;서버는 Access Token 만료를 확인하였으니, 서버 저장소에 저장된 Refresh Token을 이용하여 재발급을 요청한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Refresh Token 유효성 검증:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;Refresh Token의 서명 부분을 이용하여 유효성을 검증한다. 만약, 만료되었다면 재로그인을 요청한다.&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;새로운&amp;nbsp;Access&amp;nbsp;Token&amp;nbsp;발급:&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Refresh Token이 유효하다면 Refresh Token의 페이로드 부분을 이용해 새로운 Access Token을 발급한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Refresh Token을 서버에 저장한 경우와 클라이언트에 저장한 방식의 가장 큰 차이점은 서버가 Refresh Token을 획득하는 방법이다. 서버에 저장되어 있는 경우에는, 서버 DB에서 조회를 한 번 더 해야 하는 과정이 이루어진다. 이를 통해 보안성은 당연히 높겠지만, 토큰 인증 방식의 사용 목적인 응답속도 면에서는 쿠키 방식보다 떨어지지 않을까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;Refresh Token의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;로그아웃 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자가 로그아웃 요청을 했을 때, Refresh Token 역시 로그아웃 조치를 취하여야 한다.&amp;nbsp;각 토큰들이 저장하는 곳에 따라 다음과 같은 방식으로 로그아웃을 진행한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 메모리, 토큰 Null 처리하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. HTTP-Only 쿠키, Set-Cookie 헤더를 통해 쿠키를 만료시키기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 서버, DB에서 토큰 제거하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. BlackList에 추가하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 방식 모두 이해에 큰 어려움이 없을 것이라 판단하여, 자세한 설명은 생략한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 가장 중요한 Refresh Token이 탈취당했을 경우를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Refresh Token 탈취&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token이 탈취당했다면 어떤 상황이 발생할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token은 Access Token을 재발급받을 수 있기 때문에, 해커는 Access Token을 재발급받아 다양한 공격에 사용한다. 사용자가 로그아웃 요청을 해서 Refresh Token을 제거하였어도, 해커가 Refresh Token을 이미 탈취한 상태고 서버 측에서 무효화 로직이 없다면 공격을 막을 수 없을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;1212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGeIzS/btsK2wmhG8t/zzy5oQNpmThfaZdDx0QxcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGeIzS/btsK2wmhG8t/zzy5oQNpmThfaZdDx0QxcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGeIzS/btsK2wmhG8t/zzy5oQNpmThfaZdDx0QxcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGeIzS%2FbtsK2wmhG8t%2Fzzy5oQNpmThfaZdDx0QxcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Refresh Token을 탈취당했다면 공격을 막을 수 없다.&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;1212&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;1212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, Refresh Token을 무효화할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 역시 앞서 다룬 내용과 마찬가지로 BlackList에 등록하는 방법을 사용할 수 있다. 이 경우 서버에서 Refresh Token을 관리하고 있는 경우에만 추적하여 사용이 가능하고, Access Token처럼 stateless 할 경우 이상현상을 감지하여 BlackList에 추가하는 방법을 사용한다.&lt;/p&gt;
&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;서비스에서 완벽한 보안을 보장하는 것은 매우 어려운 과제이다. 보안성을 높이다 보면 편의성이 떨어지고, 편의성을 높이다 보면 보안성이 떨어진다. 따라서, 이 둘의 적절한 균형을 찾고 보안적인 솔루션을 더하여 피해를 최소화하는 방법을 도입하는 것이다. 이 것이 Refresh Token의 도입 목적이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token이 탈취당했을 때 피해를 최소화하기 위하여 RTR 같은 기법을 도입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RTR(&lt;b&gt;Refresh Token Rotation)&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token이 탈취당했을 경우, Refresh Token의 만료 기간을 짧게 가져가면 피해를 최소화할 수 있지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에, Refresh-Refresh-Token을 도입할 수는 없으니, Refresh Token Rotation 기법을 이용하여 피해를 최소화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;Refresh Token Rotation은 Access Token이 만료되어 Refresh Token을 요청할 때, 새로운 Refresh Token을 함께 발급받는 방법&lt;/b&gt;이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&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;2295&quot; data-origin-height=&quot;1227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lCBG4/btsK2s5gVdn/Qg7QbaW3a2PStLy1zgeVh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lCBG4/btsK2s5gVdn/Qg7QbaW3a2PStLy1zgeVh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lCBG4/btsK2s5gVdn/Qg7QbaW3a2PStLy1zgeVh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlCBG4%2FbtsK2s5gVdn%2FQg7QbaW3a2PStLy1zgeVh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;RTR(Refresh Token Rotation)은 해커든 사용자든 Refresh Token에 대한 Replay Attack이 감지되면, 토큰을 모두 무효화시킨다.&quot; loading=&quot;lazy&quot; width=&quot;2295&quot; height=&quot;1227&quot; data-origin-width=&quot;2295&quot; data-origin-height=&quot;1227&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 사용자가 Refresh Token을 이용하여 새로운 Refresh Token(RT2)를 발급받는다. 그러면 서버는 기존 Refresh Token(RT1)을 블랙리스트 처리하여 Replay Attack을 방지한다. 이후, 해커가 기존에 탈취한 Refresh Token(RT1)을 이용하여 재발급을 요청한다면 Replay Attack으로 간주하여 사용자와 연결된 모든 Refresh Token을 무효화시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 Refresh Token을 모두 무효화시키는 이유는 해커가 먼저 재발급 요청을 했을 때를 방지하기 위함이다. 오른쪽 그림에서, 해커가 먼저 Refresh Token을 재발급하였어도, 사용자의 Refresh Token 재발급 요청에 의해 해커의 Refresh Token을 무효화시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;b&gt; RTR(Refresh Token Rotation)은 해커든 사용자든 Refresh Token에 대한 Replay Attack이 감지되면, 토큰을 모두 무효화시키는 방법으로 보안을 강화&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Access Token과 Refresh Token의 저장 위치 조합&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 내가 생각한 Access Token과 Refresh Token의 저장 위치는 다음과 같다.&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;size18&quot;&gt;&lt;b&gt;1. Access Token (메모리) + Refresh Token(HttpOnly 쿠키) + &lt;b&gt;Refresh Token 유효성(서버 관리)&lt;/b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token은 메모리에 저장하여 접근성을 높이고, Refresh Token은 HttpOnly 쿠키에 저장하여 stateless 하다는 특성을 살린다. 이 경우, 페이지가 새로고침 되면 Access Token이 소멸되기 때문에, Refresh Token 만으로 Access Token을 재발급받는 로직을 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Refresh Token을 HttpOnly 쿠키와 서버에 모두 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하는 이유는, 클라이언트 측에만 Refresh Token이 저장될 경우 비정상적인 활동이 감지되었을 때 토큰을 강제로 만료시킬 방법이 없다. 또한 JWT의 stateless 함을 살리고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 Access Token이 만료되었다면, 우선적으로 HttpOnly의 Refresh Token을 JWT 서명부를 통해 인증하고 만료기간이 다 되었다면 로그인 창으로 이동시킨다. 해당 로직을 통해 stateless 함을 잘 활용할 수 있을 것이라 생각했다. Refresh Token이 만료되지 않았다면, DB와의 검증을 통해 Refresh Token이 유효한지 확인한다. Refresh Token이 BlackList에 추가되어 있거나, isActive 한 지 확인하는 과정을 거쳐 최종적으로 RTR 기법을 통해 두 토큰을 다시 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;1472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qndna/btsK3RJBZ8z/2pIRgGKQ9dNCESMdCFm2EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qndna/btsK3RJBZ8z/2pIRgGKQ9dNCESMdCFm2EK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qndna/btsK3RJBZ8z/2pIRgGKQ9dNCESMdCFm2EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQndna%2FbtsK3RJBZ8z%2F2pIRgGKQ9dNCESMdCFm2EK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Access Token (메모리) + Refresh Token(HttpOnly 쿠키) + Refresh Token 유효성(서버 관리)의 요청 플로우&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;1472&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;1472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;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;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Access Token (HttpOnly 쿠키) + Refresh Token(HttpOnly 쿠키)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 토큰 모두 클라이언트에 저장하여 stateless 한 특성을 잘 살리고, HttpOnly 쿠키에 저장하여 보안성 또한 높인 방식이다.&amp;nbsp; 보안적으로 하나의 쿠키에 두 토큰을 모두 저장하는 것이 아닌, 각각의 토큰별로 쿠키를 만들어 사용한다. 쿠키의 특성에 따라 XSS 공격에 대한 저항력이 높고, 모든 요청에 두 토큰이 항상 포함되기 때문에 클라이언트 측에서 토큰을 헤더에 포함하는 구현이 불필요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로, 모든 요청에 두 토큰이 항상 포함되기 때문에 요청 시 크기가 커지며, 모든 요청에 Access Token이 포함되어 있기 때문에 CSRF에 취약하다는 단점이 생긴다. 또한, 쿠키에는 크기 제한이 있어서 Access Token의 크기를 고려하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 1번 방법과 마찬가지로 Refresh Token 추적을 위해 서버 측 관리가 필요할 것으로 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;3.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Access Token (메모리 / HttpOnly 쿠키) + Refresh Token(서버) &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token을 서버에 저장하여 stateless 한 특성을 포기하고, 보안성을 높인 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token이 만료되면 Refresh Token을 DB에서 조회해야 하기 때문에 응답 속도 면에서는 떨어지겠지만, Refresh Token의 탈취 걱정을 할 필요가 없다. 하지만 이 역시 많은 사용자가 Access Token 갱신을 요청하는 상황이 온다면 서버 측에 부하가 올 수 있지 않을까? Access Token만 사용하는 방식보다는 토큰 인증 방식의 장점을 잘 살릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 세 가지 방식은 각각 장단점이 있으며, 보안에 완벽히 안전하다고는 할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 인증 시스템을 설계할 때는 &lt;b&gt;편의성과 보안성 간의 균형&lt;/b&gt;을 고려하여 서비스 특성에 맞는 방식을 선택하는 것이 중요하다. 또한, 특정 방식의 단점이나 취약점을 보완하기 위해 추가적인 보안 조치(예: CSRF 방어, 이상 행동 감지, 탈취 방지 메커니즘 등)를 충분히 마련해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 서비스든 완벽한 보안을 장담하기는 어렵고, 위 세 가지 방식 외에도 추가적인 보안 조치를 충분히 고려한다면 다양한 방식의 조합으로 사용이 가능할 것이다. 어떤 방법이 정답이라고 따라 하기보다는, &lt;b&gt;적절한 보안 조치를 지속적으로 적용하고 개선하며, 현실적인 위협에 대비하는 자세&lt;/b&gt;를 가져야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/98</guid>
      <comments>https://olrlobt.tistory.com/98#entry98comment</comments>
      <pubDate>Sat, 30 Nov 2024 23:15:31 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Monitoring 도구 Prometheus란?</title>
      <link>https://olrlobt.tistory.com/97</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Prometheus란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Prometheus는 애플리케이션의 성능 모니터링과 경고 알림을 위해 널리 사용되는 오픈소스 시스템으로, 특히 &lt;b&gt;시계열 데이터(Time Series Data)&lt;/b&gt; 수집과 분석에 특화된 툴이다. 시계열 데이터는 특정 시간에 기록된 메트릭 값을 기반으로 시간의 흐름에 따라 변화하는 데이터를 추적하고 저장하는 방식이다. 이러한 방식은 &lt;b&gt;시스템 성능을 모니터링하고, 문제 발생 시 그 원인을 파악하는 데 유용&lt;/b&gt;하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Prometheus는 특히 &lt;b&gt;클라우드 네이티브 환경&lt;/b&gt;과 &lt;b&gt;분산 시스템&lt;/b&gt;에서 널리 사용되며, &lt;b&gt;안정적이고 확장 가능한 모니터링 솔루션&lt;/b&gt;을 제공한다. 다양한 플랫폼에서 손쉽게 통합할 수 있으며, 애플리케이션의 리소스 사용률, 요청 처리 시간, 데이터베이스 성능 등 중요한 메트릭을 실시간으로 수집하고 분석할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Prometheus의 &lt;b&gt;알림(Alerting)&lt;/b&gt; 기능은, 지정된 조건에 맞춰 알림을 설정하여 CPU 사용률이 일정 수치를 초과하거나 메모리 사용량이 급증하는 상황을 즉시 파악하고 대응할 수 있다. 이러한 알림은 이메일, Slack 등 다양한 채널을 통해 전달되며, 운영자의 빠른 대응을 도와준다.&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: #333333;&quot;&gt;결론적으로, Prometheus는 현대 IT 시스템의 &lt;b&gt;실시간 모니터링과 경보 시스템&lt;/b&gt; 구축에 매우 적합한 도구로, 특히 대규모의 분산된 클라우드 환경에서 안정성을 유지하고 성능을 최적화하는 데 중요한 역할을 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span style=&quot;text-align: start;&quot;&gt;Prometheus는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Grafana와 같은&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;시각화 도구와도 쉽게 통합되어, 직관적인 대시보드로 실시간 모니터링을 가능하게 한다. 하지만 나는 모니터링 도구의 사용이 처음이기 때문에, Grafana의 도입은 다음 포스팅에서 다루고 이번 포스팅에서는&amp;nbsp;&amp;nbsp;&lt;/span&gt;Prometheus에만 집중하려 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Spring Actuator&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Spring Actuator&lt;/b&gt;는 &lt;b&gt;Spring Boot 애플리케이션의 모니터링 및 관리 기능&lt;/b&gt;을 제공하는 라이브러리로, 개발자가 애플리케이션의 상태와 운영 정보를 쉽게 확인하고 관리할 수 있도록 도와준다. 특히, 애플리케이션의 &lt;b&gt;상태와 성능 관련 데이터를 자동으로 수집하고 노출&lt;/b&gt;함으로써, 별도의 코드 작성 없이도 JVM 메모리 사용량, CPU 사용률, 쓰레드 상태, 데이터베이스 커넥션 상태 등을 손쉽게 모니터링할 수 있다.&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: #333333;&quot;&gt;Actuator는 &lt;b&gt;Prometheus&lt;/b&gt;와 같은 모니터링 도구가 필요한 메트릭을 '/actuator/prometheus' 엔드포인트에서 제공하여, 애플리케이션 내부에서 메트릭을 직접 관리할 필요 없이 Prometheus가 이를 수집해 갈 수 있도록 한다. 또한, Actuator는 여러 가지 &lt;b&gt;기본적인 성능 메트릭&lt;/b&gt;을 내장하고 있어, 이를 Prometheus에서 바로 활용할 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이처럼 Actuator는 Spring 애플리케이션과 Prometheus 간의 통합을 간단하게 처리하여, 시스템 성능 모니터링과 운영 관리의 복잡성을 크게 줄여준다.&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;
&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;h3 style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Spring Actuator 설정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1729054928371&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '3.2.5'&lt;/code&gt;&lt;/pre&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: #333333;&quot;&gt;Build.gradle에 Spring Actuator의 의존성을 추가해 주었다.&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Actuator는 다양한 관리 및 모니터링 기능을 제공하며, 이를 위해 여러 엔드포인트를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;Actuator 엔드포인트를 기본적으로 노출&lt;/b&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;b&gt;application.properties 또는 application.yml&lt;/b&gt; 파일에서 설정을 통해 &lt;b&gt;필요하지 않은 엔드포인트에 대한 접근을 제한하자&lt;/b&gt;. 이번 포스팅에서는 prometheus만 필요하기 때문에 , health를 제외한 나머지 불필요한 엔드포인트는 접근을 차단하도록 설정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729054937095&quot; class=&quot;java&quot; style=&quot;background-color: #f6f7f8; color: #555555; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;management:
  endpoints:
    web:
      exposure:
        include: prometheus, health  # prometheus와 health 엔드포인트 활성화
  prometheus:
    metrics:
      export:
        enabled: true  # Prometheus 메트릭 활성화
  endpoint:
    prometheus:
      enabled: true  # Prometheus 엔드포인트 활성화
    health:
      enabled: true  # Health 엔드포인트 활성화&lt;/code&gt;&lt;/pre&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;b&gt;Spring Actuator&lt;/b&gt;를 통해 메트릭 정보를 얻어올 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;localhost:8080/actuator/prometheus로 &lt;b&gt;GET 요청&lt;/b&gt;을 보내면, &lt;b&gt;Prometheus&lt;/b&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJB3vu/btsJ8wNzauB/FMbRAFYa8Ft2TpPaP7ogdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJB3vu/btsJ8wNzauB/FMbRAFYa8Ft2TpPaP7ogdk/img.png&quot; data-alt=&quot;Spring Actuator로 메트릭 값을 간편하게 얻을 수 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJB3vu/btsJ8wNzauB/FMbRAFYa8Ft2TpPaP7ogdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJB3vu%2FbtsJ8wNzauB%2FFMbRAFYa8Ft2TpPaP7ogdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;472&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Actuator로 메트릭 값을 간편하게 얻을 수 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 형식은 메트릭 이름, 라벨, 그리고 값으로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Prometheus는 이 엔드포인트를 통해 주기적으로 &lt;b&gt;스크래핑&lt;/b&gt;하여 메트릭 데이터를 수집한다. Prometheus 서버는 이 데이터를 가져와 내부적으로 &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;위 정보에서 &lt;b&gt;메트릭 이름&lt;/b&gt;, &lt;b&gt;라벨&lt;/b&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Prometheus 설치&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Prometheus 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Build.gradle 또는 pom.xml에 Prometheus 관련 의존성을 추가해 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1729059703998&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'io.micrometer:micrometer-registry-prometheus'&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;Spring Boot 애플리케이션에서 메트릭을 제공하고, Prometheus가 이를 수집할 수 있도록 하려면 Prometheus 서버를 설정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 prometheus.yml을 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1729059716526&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;global:
  scrape_interval: 15s  # 기본적으로 15초마다 메트릭을 수집
  # evaluation_interval: 15s  # 15초마다 알림 규칙을 평가
scrape_configs:
  - job_name: 'spring-actuator'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['spring-blue:8080', 'spring-green:8080']  # Spring Boot 애플리케이션이 실행 중인 호스트와 포트&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;Prometheus 서버의 설정 파일인 prometheus.yml에서 Spring Boot 애플리케이션의 /actuator/prometheus 엔드포인트를 15초마다 스크래핑하도록 설정했다. 실제로 사용해 보니, 생각보다 텀이 길게 느껴지긴 한다. 서비스와 개인 취향에 맞추어 변경하면 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;targets의 경우에는 모니터링할 Spring 서버의 url을 적어주면 된다. 나의 경우에는 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;이제 설정 파일을 scp 명령어를 통해 ubuntu 서버로 옮겨 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1729062523353&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scp -i .\blogwidget2.pem .\github-tistory-posting\prometheus.yml ubuntu@blogwidget.com:/home/ubuntu/prometheus.yml&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;나는 별도의 폴더는 필요 없어서 기본 폴더인 /home/ubuntu에 prometheus.yml 이름으로 옮겨주었다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Prometheus Docker 설치&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window 환경에서 Prometheus를 테스트하고 싶으면 아래 공식 홈페이지를 이용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 이용하면, Ubuntu 서버에 설치하지 않고도 Prometheus를 사용해 볼 수 있으니, Localhost로 테스트해 보는 것도 가능할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1729076332093&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;Download | Prometheus&quot; data-og-description=&quot;An open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.&quot; data-og-host=&quot;prometheus.io&quot; data-og-source-url=&quot;https://prometheus.io/download/&quot; data-og-url=&quot;https://prometheus.io/download/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cwNkMV/hyXhMpZDe5/1epK33O7SNUdKZeuxcQxJ0/img.png?width=192&amp;amp;height=192&amp;amp;face=0_0_192_192&quot;&gt;&lt;a href=&quot;https://prometheus.io/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://prometheus.io/download/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cwNkMV/hyXhMpZDe5/1epK33O7SNUdKZeuxcQxJ0/img.png?width=192&amp;amp;height=192&amp;amp;face=0_0_192_192');&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;Download | Prometheus&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;prometheus.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;나의 경우에는 이미 작동 중인 개인 프로젝트에서 메트릭 정보를 이용할 것이므로, Ubuntu 서버에서 Docker를 이용해 설치해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729060241218&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker pull prom/prometheus&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/botfi7/btsJ6ETSSl9/epzar8bXtuCAAMxXKZCP4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/botfi7/btsJ6ETSSl9/epzar8bXtuCAAMxXKZCP4k/img.png&quot; data-alt=&quot;Docker 이미지를 이용해 프로메테우를 설치할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/botfi7/btsJ6ETSSl9/epzar8bXtuCAAMxXKZCP4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbotfi7%2FbtsJ6ETSSl9%2Fepzar8bXtuCAAMxXKZCP4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;666&quot; height=&quot;265&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker 이미지를 이용해 프로메테우를 설치할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 명령어는 위와 같고, Docker-compose를 사용 중이라면 위의 과정은 생략해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Prometheus Docker-compose 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 Docker-compose를 사용 중이기 때문에 아래와 같이 Prometheus 부분을 추가해 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1729060540017&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3'

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - &quot;80:80&quot;
      - &quot;443:443&quot;
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    depends_on:
      - certbot
    networks:
      - monitor-network

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--web.external-url=/prometheus/'
    volumes:
      - /home/ubuntu/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - &quot;9090:9090&quot;
    restart: always
    networks:
      - monitor-network

  spring-blue:
    container_name: spring-blue
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - &quot;8080:8080&quot;
    networks:
      - monitor-network

  spring-green:
    container_name: spring-green
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - &quot;8081:8080&quot;
    networks:
      - monitor-network

networks:
  monitor-network:&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;프로메테우스는 기본적으로 9090 포트를 사용하기 때문에, 이에 맞게 포트를 설정해 주고,&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt; 컨테이너 내부 설정 파일을 외부에서 간편하게 수정&lt;/b&gt;할 수 있도록 &lt;b&gt;volumes를 통하여 마운트&lt;/b&gt; 해주었다. 또한, &lt;/span&gt;prometheus.yml에서 DNS로 설정을 해주었기 때문에 &lt;b&gt;같은 networks를 이용하여 Spring과 Nginx, Prometheus를 연결&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;Command 설정의 경우는 다음과 같으며, 설치 과정에서 만난 오류를 해결한 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나와 다르게 설정을 진행한 경우에는 해당 내용은 추가하지 않아도 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--config.file=/etc/prometheus/prometheus.yml: Prometheus가 설정 파일의 위치를 명확히 알 수 있도록 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--web.external-url=/prometheus/: 외부에서 접근할 때의 기본 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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Prometheus Nginx 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prometheus의 기본 웹 대시보드에 접근하기 위해서는 기본적으로 &lt;b&gt;Prometheus 컨테이너 내부에 접근&lt;/b&gt;하면 된다. 이를 위해 기본적으로 이 전에 설정한 포트를 포함하여 &quot;http://&amp;lt;your-domain-or-ip&amp;gt;:9090&quot; 이 기본 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;여기에 접근하기 위하여 Nginx 설정을 바꾸어 주자.&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_1729068020932&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name blogwidget.com;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name blogwidget.com;
    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/blogwidget.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blogwidget.com/privkey.pem;


    location /prometheus/ {  ## 추가된 부분
        proxy_pass http://prometheus:9090/prometheus/; ## /prometheus/ 로 들어오는 요청을
        proxy_set_header Host $host;                 ## prometheus 컨테이너로 리버스 프록시
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location / {
        proxy_pass http://spring-green:8080;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서 주의할 점!!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우에는 docker-compose에서 &quot;--web.external-url=/prometheus/&quot;를 사용하여 기본 url을 바꾸어 주었다. 따라서 prometheus:9090이라는 DNS뒤에 &quot;--web.external-url=/prometheus/&quot;에 설정한 /prometheus/ 가 추가 된 것에 유의하자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본적으로는 해당 부분을 지우고 9090으로 마무리 지으면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Prometheus 시작&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx까지 설정을 마쳤다면, 설정해 준 URL을 통해 Prometheus에 접근이 가능할 것이다.&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;1919&quot; data-origin-height=&quot;1030&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBxjmy/btsJ76WsDsT/QIkU0kkqfbWwTiVW6RFlR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBxjmy/btsJ76WsDsT/QIkU0kkqfbWwTiVW6RFlR1/img.png&quot; data-alt=&quot;Prometheus의 메인 웹 대시보드 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBxjmy/btsJ76WsDsT/QIkU0kkqfbWwTiVW6RFlR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBxjmy%2FbtsJ76WsDsT%2FQIkU0kkqfbWwTiVW6RFlR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1030&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;1030&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Prometheus의 메인 웹 대시보드 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 메인 화면은 아무런 설정이 없는 메인 화면이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Prometheus의 &lt;b&gt;Expression&lt;/b&gt;(쿼리 창)에서는 &lt;b&gt;PromQL&lt;/b&gt;(Prometheus Query Language)을 사용하여 다양한 메트릭을 조회하고, 분석 및 시각화할 수 있다.&lt;/p&gt;
&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;PromQL이란&lt;/b&gt; Prometheus에 수집된 메트릭 데이터를 조회, 집계, 필터링, 그리고 계산하는 데 사용되는 언어정도로 이해하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2022&quot; data-origin-height=&quot;755&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euxlDL/btsJ7P8wJnH/KVZHjqkLLtbSfqAjVvt1M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euxlDL/btsJ7P8wJnH/KVZHjqkLLtbSfqAjVvt1M1/img.png&quot; data-alt=&quot;&amp;quot;up&amp;quot; 키워드를 통해 PromQL을 간단하게 테스트한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euxlDL/btsJ7P8wJnH/KVZHjqkLLtbSfqAjVvt1M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuxlDL%2FbtsJ7P8wJnH%2FKVZHjqkLLtbSfqAjVvt1M1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2022&quot; height=&quot;755&quot; data-origin-width=&quot;2022&quot; data-origin-height=&quot;755&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;&quot;up&quot; 키워드를 통해 PromQL을 간단하게 테스트한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음으로 up을 치고 Execute를 눌러보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Spring서버와 정상적으로 연결이 되었다면, blue 서버와 같이 1이 반환될 것이고, 연결이 되지 않았다면 green 서버와 같이 0이 반환될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;719&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nujUF/btsJ7mlbbHL/DakwcyH1oK9K6syocpamx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nujUF/btsJ7mlbbHL/DakwcyH1oK9K6syocpamx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nujUF/btsJ7mlbbHL/DakwcyH1oK9K6syocpamx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnujUF%2FbtsJ7mlbbHL%2FDakwcyH1oK9K6syocpamx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1915&quot; height=&quot;719&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;719&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 상단 탭의 Status &amp;gt; Targets 메뉴에서 간편하게 확인도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Up으로 표시된 서버의 Endpoint부터 작동된 시간까지 정보를 한눈에 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 연결이 잘 안 되었다면, prometheus의 설정이 정확이 되었는지 다시 확인해 보길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Prometheus 간단하게 사용해 보기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prometheus에서 지원하는 &lt;b&gt;PromQL&lt;/b&gt;는 아직 내게 너무 어색하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서칭을 통해 여러 가지 쿼리들을 실행해 보고 있으며, 사용법이 익숙해질 때쯤 포스팅을 업데이트하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 여러 가지 시험 삼아 작동해 보았던 쿼리들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. scrape_samples_post_metric_relabeling : 각 타깃에서 수집한 메트릭의 개수 &lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2021&quot; data-origin-height=&quot;753&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QsFJo/btsJ8ZvfSzt/ksC8C4uKVDFSdYxnyclkq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QsFJo/btsJ8ZvfSzt/ksC8C4uKVDFSdYxnyclkq0/img.png&quot; data-alt=&quot;scrape_samples_post_metric_relabeling 쿼리 작동 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QsFJo/btsJ8ZvfSzt/ksC8C4uKVDFSdYxnyclkq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQsFJo%2FbtsJ8ZvfSzt%2FksC8C4uKVDFSdYxnyclkq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2021&quot; height=&quot;753&quot; data-origin-width=&quot;2021&quot; data-origin-height=&quot;753&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;scrape_samples_post_metric_relabeling 쿼리 작동 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prometheus가 각 타깃에서 수집한 메트릭의 개수를 나타낸다. 현재 표기 방식은 Table이며, green의 경우 down 서버이기에 0으로 표시된 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. http_server_requests_seconds_sum : HTTP 요청의 총 응답 시간 &lt;/b&gt; &lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2008&quot; data-origin-height=&quot;1455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br25Py/btsJ7T3QHKI/H8TkTg37U07VjuXMjLFQYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br25Py/btsJ7T3QHKI/H8TkTg37U07VjuXMjLFQYk/img.png&quot; data-alt=&quot;http_server_requests_seconds_sum 쿼리 작동 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br25Py/btsJ7T3QHKI/H8TkTg37U07VjuXMjLFQYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr25Py%2FbtsJ7T3QHKI%2FH8TkTg37U07VjuXMjLFQYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2008&quot; height=&quot;1455&quot; data-origin-width=&quot;2008&quot; data-origin-height=&quot;1455&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http_server_requests_seconds_sum 쿼리 작동 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 요청의 총 응답 시간을 나타낸다. 쿼리 작성부 및의 Graph를 클릭하면, 시간대 별로 쿼리의 응답값을 볼 수 있다. 하단의 정보를 통해 메서드 별로 보는 것도 가능하고, 시간대를 설정해서 보는 것도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. jvm_memory_used_bytes : JVM에서 사용 중인 메모리 양 &lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2307&quot; data-origin-height=&quot;1574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciTszo/btsJ9sDTHDe/aaxIXktDyNXQKFEdhopdrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciTszo/btsJ9sDTHDe/aaxIXktDyNXQKFEdhopdrk/img.png&quot; data-alt=&quot;jvm_memory_used_bytes 쿼리 작동 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciTszo/btsJ9sDTHDe/aaxIXktDyNXQKFEdhopdrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciTszo%2FbtsJ9sDTHDe%2FaaxIXktDyNXQKFEdhopdrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2307&quot; height=&quot;1574&quot; data-origin-width=&quot;2307&quot; data-origin-height=&quot;1574&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;jvm_memory_used_bytes 쿼리 작동 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM에서 사용 중인 메모리 양을 조회하는 쿼리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 재밌는 점은 Eden Space, Survivor Space처럼 Heap영역도 다 표시된다는 점이고, Eden 영역이 치솟았다가 0으로 사라지는 모습은 Minor GC를 나타내는 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 이 외&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;http_server_requests_seconds_count&lt;/b&gt; : Spring Boot Actuator에서 제공하는 HTTP 요청 수를 조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; jvm_memory_committed_bytes&lt;/b&gt; JVM에 할당된 총 메모리 크기를 조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; jvm_gc_pause_seconds_sum&lt;/b&gt; JVM에서 수행된 가비지 컬렉션에 소요된 총 시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; jvm_threads_live_threads&lt;/b&gt; 현재 실행 중인 JVM 쓰레드 수를 조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; process_uptime_seconds&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;이 외에는 위와 같은 쿼리들이 있으며, 모두 Spring Actuator에서 지원하는 부분들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽게도 내가 원하는 기능인 CPU 사용량 같은 건, Spring Actuator만으로는 지원하지 않는 기능으로 쿼리를 사용할 수 없다. 이를 사용할 수 있게 하려면 Node 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 Prometheus를&amp;nbsp;Spring Actuator와 함께 사용하여 애플리케이션의 성능을 모니터링하는 방법을 알아보았다. Actuator를 통해 간편하게 메트릭을 노출하고, Prometheus가 이를 수집하여 실시간으로 분석하는 것을 웹 대시보드를 통해 간편하게 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 Prometheus에 대해 미숙하기 때문에, 여러 가지 기능들도 건드려보고 Node Exporter를 이용한 확장과 Grafana를 이용한 시각화 등 다양한 도전을 해 볼 예정이다.&lt;/p&gt;
&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;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/97</guid>
      <comments>https://olrlobt.tistory.com/97#entry97comment</comments>
      <pubDate>Wed, 16 Oct 2024 20:55:07 +0900</pubDate>
    </item>
    <item>
      <title>[Java] CompletableFuture로 비동기 프로그래밍 구현하기</title>
      <link>https://olrlobt.tistory.com/96</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;비동기 프로그래밍&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 프로그래밍은 작업을 병렬로 실행하여 CPU의 효율을 극대화하고, 응답 시간을 줄이기 위해 중요한 기법이다. 특히 네트워크 요청, 파일 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;자바에서는 Thread, Runnable, Future 등을 사용해 비동기 작업을 처리할 수 있지만, 기존 방법들은 다소 복잡하거나 제한적일 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Future&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Future는 Java 5에서 처음 도입된 인터페이스로, 비동기 작업의 결과를 나타내는 객체이다. 현재 실행 중인 작업이 완료될 때까지 기다리지 않고, 미래의 어느 시점에서 그 작업의 결과를 받을 수 있게 함으로써 &lt;b&gt;비동기 프로그래밍&lt;/b&gt;을 구현할 수 있다. Java에서 &lt;b&gt;멀티스레딩&lt;/b&gt;이나 &lt;b&gt;병렬 처리&lt;/b&gt;를 구현할 때 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Future, FutureTask 예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Future는 인터페이스이기 때문에 비동기 작업을 직접 실행할 수 없다. 대신, 작업의 상태나 결과를 추적하는 역할을 한다. 비동기 작업을 실행하려면 Future의 구현체인 FutureTask를 사용해야 하는데, FutureTask는 Runnable과 Future를 모두 구현한 클래스로, 직접 &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;일반적으로 Future를 사용하기 위해서는 ExecutorService를 통해 비동기 작업을 처리해야 한다. 하지만 이 예제에서는 ExecutorService를 사용하지 않고, &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_1728376273892&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) {

        Callable&amp;lt;String&amp;gt; task = () -&amp;gt; { // Callable 작업 생성
            Thread.sleep(2000); // 2초 동안 작업 수행
            return &quot;작업 완료!&quot;;
        };

        FutureTask&amp;lt;String&amp;gt; futureTask = new FutureTask&amp;lt;&amp;gt;(task); // FutureTask 생성

        Thread thread = new Thread(futureTask); // Thread를 통해 FutureTask 실행
        thread.start();
        System.out.println(&quot;작업을 제출하고 다른 작업을 수행 중입니다...&quot;);

        try {
            while (!futureTask.isDone()) { // 비동기 작업이 완료될때까지 기다린다
                System.out.println(&quot;작업이 아직 완료되지 않았습니다...&quot;);
                Thread.sleep(500); // 0.5초 간격으로 작업 상태 확인
            }
            String result = futureTask.get();  // FutureTask에서 결과를 가져옴 // 작업이 완료될 때까지 블로킹
            System.out.println(&quot;FutureTask 결과: &quot; + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}&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초가 소요되는 다른 작업을 한다고 가정하고 비동기 프로그래밍이 정상적으로 작동하는지 확인했다. Callable 작업을 FutureTask를 통해 비동기적으로 실행하고, 그 상태와 결과를 추적하는 식으로 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728376438712&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;작업을 제출하고 다른 작업을 수행 중입니다...
작업이 아직 완료되지 않았습니다...
작업이 아직 완료되지 않았습니다...
작업이 아직 완료되지 않았습니다...
작업이 아직 완료되지 않았습니다...
FutureTask 결과: 작업 완료!

Process finished with exit code 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;출력 결과에서 확인할 수 있듯이, 비동기 작업이 진행되는 동안 while문에서 로그가 정상적으로 출력되고, 작업이 완료되면 그 결과를 출력하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Future의 한계&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;콜백 기능 부족&lt;/b&gt;: Future는 비동기 작업이 완료되면 그 결과를 받을 수 있지만, 작업이 완료되었을 때 자동으로 실행되는 &lt;b&gt;콜백&lt;/b&gt; 처리가 없다. 즉, 작업 완료 후 추가 작업을 지정할 수 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예외 처리 제한적&lt;/b&gt;: Future는 비동기 작업 도중 발생한 예외를 처리하는 데 있어 다소 제한적이다. 예외가 발생한 경우에도 get()을 호출해야만 해당 예외를 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작업 병합 및 흐름 제어 부족&lt;/b&gt;: Future는 여러 작업을 조합하여 처리하는 데 있어 유연성이 부족하다. 후속 작업을 연결하거나 복합적인 비동기 작업을 다루기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 한계를 보완하기 위해, Java 8에서는 &lt;b&gt;CompletableFuture&lt;/b&gt;가 도입되었다. CompletableFuture는 콜백 처리, 예외 처리, 작업 병합 등의 기능을 제공하여, 더 유연하고 강력한 비동기 프로그래밍을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; CompletableFuture&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CompletableFuture는 &lt;b&gt;Java 8&lt;/b&gt;에서 도입된 클래스로, &lt;b&gt;비동기 프로그래밍을 보다 유연하게 처리할 수 있게&lt;/b&gt; 해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Future의 기능을 확장하여, 비동기 작업의 결과뿐만 아니라, 콜백 작업을 지정할 수 있고, 여러 비동기 작업을 &lt;b&gt;결합&lt;/b&gt;하거나 &lt;b&gt;체인&lt;/b&gt;으로 연결할 수 있는 다양한 기능을 제공한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;supplyAsync(),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;runAsync()&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; : 비동기 작업 실행&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;supplyAsync()와 runAsync()를 사용해 비동기 작업을 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;supplyAsync()는 값을 반환하는 작업을, runAsync()는 반환 값이 없는 작업을 처리할 때 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728377934066&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// supplyAsync()는 반환 값이 없다.
CompletableFuture&amp;lt;String&amp;gt; future = CompletableFuture.supplyAsync(() -&amp;gt; { 
    try {
        System.out.println(&quot;비동기 작업 시작 .. &quot;);
        Thread.sleep(2000); // 2초 동안 데이터 처리 시뮬레이션
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return &quot;비동기 작업 완료 !&quot;;
});

// runAsync()는 반환 값이 없다.
CompletableFuture&amp;lt;Void&amp;gt; future = CompletableFuture.runAsync(() -&amp;gt; {
    try {
        System.out.println(&quot;비동기 작업 시작...&quot;);
        Thread.sleep(2000);  // 2초 동안 작업 수행
        System.out.println(&quot;비동기 작업 완료!&quot;);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; join(), get()&amp;nbsp; : 비동기 작업 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;join()과 get()은 둘 다 CompletableFuture에서 비동기 작업이 완료될 때까지 기다리는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘의 차이는 예외 처리의 필요성인데, get() 메서드는 InterruptedException과 ExecutionException을 발생시킬 수 있다. 반면 join()은 예외를 처리하지 않고, 런타임 예외로 던진다. 따라서 서비스에 따라 적절한 메서드를 선택하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728378361442&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// get()은 예외 처리 필요
try {
    String result = future.get(); // 작업이 완료될 때까지 기다림
    System.out.println(&quot;비동기 작업 결과: &quot; + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
        
// join()은 예외 처리 불필요       
String result = future.join(); // 작업이 완료될 때까지 기다림
System.out.println(&quot;비동기 작업 결과: &quot; + result);&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: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;runAsync()를 사용하여 반환 값이 없는 경우에도, join()과 get()을 이용하여 반환 값 없이 처리하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728378589224&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;future.join();  // 작업이 끝날 때까지 대기
System.out.println(&quot;모든 작업이 완료되었습니다.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;thenApply(), &lt;b&gt;thenAccept(), &lt;b&gt;thenRun() : &lt;/b&gt;&lt;/b&gt;&lt;/b&gt;콜백 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CompletableFuture는 다양한 콜백 메서드를 제공하며, 이를 통해 작업이 완료된 후 추가 작업을 처리할 수 있다. 주요 메서드는 다음과 같으며 차이가 있으니 상황에 맞게 잘 사용하자.&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;thenApply()&lt;/b&gt;: &lt;b&gt;비동기 &lt;/b&gt;&lt;b&gt;결과 값을 변환해서 다른 값으로 반환&lt;/b&gt;하는 메서드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thenAccept()&lt;/b&gt;: &lt;b&gt;비동기 &lt;/b&gt;&lt;b&gt;결과를 반환하지 않고, 단순히 처리&lt;/b&gt;하는 메서드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thenRun()&lt;/b&gt;: 작업의 &lt;b&gt;결과 값을 사용하지 않고&lt;/b&gt;, &lt;b&gt;그저 후속 작업만 실행&lt;/b&gt;하는 메서드&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thenApply(), thenAccept(), thenRun()는 이전 작업이 완료된 후 같은 스레드에서 &lt;b&gt;즉시&lt;/b&gt; 실행된다. 이 콜백 작업 역시 비동기적으로 작업되기를 원하면 thenApplyAsync(), thenAcceptAsync(), thenRunAsync()를 사용하여 &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_1728379497897&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) {
        CompletableFuture&amp;lt;String&amp;gt; future = CompletableFuture.supplyAsync(() -&amp;gt; {
            try {
                Thread.sleep(2000);  // 2초 지연
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return &quot;작업 완료!&quot;;
        });

        future.thenAccept(result -&amp;gt; { // 작업 결과를 받아 출력
            System.out.println(&quot;비동기 작업 결과: &quot; + result);
        });


    // Thread.sleep(3000);  // 비동기 작업이 끝날 때까지 대기해야 결과 확인 가능
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; exceptionally(), handle(), whenComplete() : 예외처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CompletableFuture에서 예외 처리는 비동기 작업 도중 발생할 수 있는 예외를 처리하기 위한 방법을 제공한다. 주로 exceptionally(), handle(), whenComplete() 같은 메서드를 사용하여 예외를 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exceptionally()는 예외가 발생한 경우 기본 값을 반환하거나 예외 처리 로직을 수행하는 데 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728476437382&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.CompletableFuture;

public class ExceptionallyExample {
    public static void main(String[] args) {
        CompletableFuture&amp;lt;String&amp;gt; future = CompletableFuture.supplyAsync(() -&amp;gt; {
            if (true) {
                throw new RuntimeException(&quot;Something went wrong!&quot;);
            }
            return &quot;Success!&quot;;
        }).exceptionally(ex -&amp;gt; {
            System.out.println(&quot;Exception occurred: &quot; + ex.getMessage());
            return &quot;Default Value&quot;;
        });

        // 결과 출력
        System.out.println(future.join());  // &quot;Default Value&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;handle()는 정상적으로 완료되든 예외가 발생하든 상관없이 결과를 처리할 수 있다. 이 메서드는 두 개의 인자를 받으며, 하나는 결과 값이고, 다른 하나는 예외이다.&lt;/p&gt;
&lt;pre id=&quot;code_1728476752716&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) {
        CompletableFuture&amp;lt;String&amp;gt; future = CompletableFuture.supplyAsync(() -&amp;gt; {
            if (true) {
                throw new RuntimeException(&quot;Something went wrong!&quot;);
            }
            return &quot;Success!&quot;;
        }).handle((result, ex) -&amp;gt; {
            if (ex != null) {
                System.out.println(&quot;Exception occurred: &quot; + ex.getMessage());
                return &quot;Handled Error&quot;;
            }
            return result;
        });

        // 결과 출력
        System.out.println(future.join());  // &quot;Handled Error&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;이 외에도 whenComplete()는 비동기 작업이 완료된 후 결과와 예외를 처리할 수 있다. 이 메서드는 결과를 반환하지 않고, 단순히 완료 후 후속 작업을 수행하는 데 사용하고 handle()과 달리 반환 값을 변경할 수는 없지만, 결과나 예외에 대해 후처리 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, completeExceptionally()는 특정 시점에서 의도적으로 예외를 발생시키고 싶은 경우에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 다양한 예외처리 메서드를 제공하기 때문에, 서비스와 의도에 맞게 선택해서 사용하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; thenCompose(), thenCombine(), allOf(), anyOf() : 작업 결합&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; thenCompose()&lt;/b&gt;는 두 개의 비동기 작업을 순차적으로 연결할 때 사용된다. 첫 번째 비동기 작업이 완료된 후 그 결과를 받아 또 다른 비동기 작업을 실행할 때 유용하다. 예를 들어, 첫 번째 작업의 결과에 따라 두 번째 비동기 작업이 실행되는 경우에 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1728477512791&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CompletableFuture&amp;lt;String&amp;gt; future = CompletableFuture.supplyAsync(() -&amp;gt; &quot;Hello&quot;)
    .thenCompose(result -&amp;gt; CompletableFuture.supplyAsync(() -&amp;gt; result + &quot; World&quot;));

// 결과 출력
System.out.println(future.join());  // 출력: &quot;Hello World&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;이렇게만 생각해 보면 thenApplyAsync()와 차이가 별로 없어 보인다. 실제로도 거의 차이는 없고, 목적과 반환 값에서만 약간의 차이가 있다. thenApplyAsync()의 경우에는&amp;nbsp;&lt;b&gt; 결과 값을 받아 &lt;b&gt;변환하는 것에&lt;/b&gt;&lt;/b&gt; 목적을 두고, thenCompose()의 경우에는 결과를 받아 &lt;b&gt;새로운 비동기 작업&lt;/b&gt;을 실행하는 것에 목적을 둔다. 또한, thenApplyAsync()의 경우에는 변환된 값(&amp;lt;T&amp;gt;)이 반환값이 되는 반면, thenCompose()는 새로운 비동기 작업인 (CompletableFuture &amp;lt;T&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; thenCombine()&lt;/b&gt;은 두 개의 독립적인 비동기 작업을 동시에 실행하고, 두 작업이 완료된 후 그 결과를 조합하여 새로운 결과를 반환하는 데 사용된다. 두 작업이 동시에 수행되지만 결과는 서로 결합된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728477470590&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CompletableFuture&amp;lt;String&amp;gt; future1 = CompletableFuture.supplyAsync(() -&amp;gt; &quot;Hello&quot;);
CompletableFuture&amp;lt;String&amp;gt; future2 = CompletableFuture.supplyAsync(() -&amp;gt; &quot;World&quot;);

CompletableFuture&amp;lt;String&amp;gt; combinedFuture = future1.thenCombine(future2, (result1, result2) -&amp;gt; result1 + &quot; &quot; + result2);

// 결과 출력
System.out.println(combinedFuture.join());  // 출력: &quot;Hello World&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; CompletableFuture.allOf()&lt;/b&gt;는 여러 개의 비동기 작업이 모두 완료될 때까지 기다리는 메서드이다. 여러 비동기 작업이 완료되면, 각각의 결과를 처리할 수 있어서 주로 여러 작업이 모두 완료되어야 할 때 유용하게 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728477757885&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CompletableFuture&amp;lt;String&amp;gt; future1 = CompletableFuture.supplyAsync(() -&amp;gt; &quot;Task 1&quot;);
CompletableFuture&amp;lt;String&amp;gt; future2 = CompletableFuture.supplyAsync(() -&amp;gt; &quot;Task 2&quot;);
CompletableFuture&amp;lt;String&amp;gt; future3 = CompletableFuture.supplyAsync(() -&amp;gt; &quot;Task 3&quot;);

CompletableFuture&amp;lt;Void&amp;gt; allFutures = CompletableFuture.allOf(future1, future2, future3);

// 모든 작업이 완료된 후 결과 수집
CompletableFuture&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; allResults = allFutures.thenApply(v -&amp;gt; {
    return List.of(future1, future2, future3).stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
});

// 결과 출력
allResults.get().forEach(System.out::println);  // 출력: &quot;Task 1&quot;, &quot;Task 2&quot;, &quot;Task 3&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; CompletableFuture.anyOf()&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;pre id=&quot;code_1728477803272&quot; class=&quot;livescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;CompletableFuture&amp;lt;String&amp;gt; future1 = CompletableFuture.supplyAsync(() -&amp;gt; {
    try { Thread.sleep(300); } catch (InterruptedException e) {}
    return &quot;Task 1&quot;;
});

CompletableFuture&amp;lt;String&amp;gt; future2 = CompletableFuture.supplyAsync(() -&amp;gt; {
    try { Thread.sleep(100); } catch (InterruptedException e) {}
    return &quot;Task 2&quot;;
});

CompletableFuture&amp;lt;Object&amp;gt; anyOfFuture = CompletableFuture.anyOf(future1, future2);

// 가장 먼저 완료된 작업의 결과 출력
System.out.println(anyOfFuture.join());  // 출력: &quot;Task 2&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CompletableFuture 병렬 처리 활용하여 개인프로젝트 개선&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음 CompletableFuture를 알아보게 된것은 개인 프로젝트 성능 개선의 목적이 컸다. 이 전 포스팅부터 개인프로젝트 개선을 진행하면서 성능적인 부분을 많이 개선하려고 집중하고 있는데, 특히 오래 걸리는 비즈니스 로직이 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;여러 단계에 거쳐 직렬적으로 연결되어 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 부분을 병렬적으로 처리하면, 한 SVG태그에 여러 부분을 동시에 그리게 되니까 성능이 많이 개선될 것으로 예상했다.&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;1430&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3lVc2/btsJYElVtpv/L2PngJve4LUOoB1P75B13k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3lVc2/btsJYElVtpv/L2PngJve4LUOoB1P75B13k/img.png&quot; data-alt=&quot;SVG이미지를 생성하는데 많은 개선을 했지만, 평균 700ms의 시간이 소요됐다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3lVc2/btsJYElVtpv/L2PngJve4LUOoB1P75B13k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3lVc2%2FbtsJYElVtpv%2FL2PngJve4LUOoB1P75B13k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;SVG이미지를 생성하는데 많은 개선을 했지만, 평균 700ms의 시간이 소요됐다.&quot; loading=&quot;lazy&quot; width=&quot;1430&quot; height=&quot;500&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SVG이미지를 생성하는데 많은 개선을 했지만, 평균 700ms의 시간이 소요됐다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 개선에 앞서 기존 API 호출 시간을 Profiler를 통해 측정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVG 태그를 만드는 createSvgImageBox()가 평균적으로 700ms의 시간이 소요되었고, 여기에 블로그를 크롤링하는 과정까지 합치면 API 호출 시간이 약 1초 이상 걸리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728478966178&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;drawBackground(svgGenerator, postingBase);
drawThumbnail(posting, svgGenerator, postingBase);
drawText(posting, svgGenerator, postingBase);
drawFooter(posting, svgGenerator, postingBase);
drawAuthorImg(posting, svgGenerator, postingBase);
drawAuthorText(posting, svgGenerator, postingBase);
drawWatermark(posting, svgGenerator, postingBase);
drawStroke(svgGenerator, postingBase);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 로직은 위와 같이 작성되어 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배경 그리기, 썸네일 그리기, 제목, 내용, 날짜, 작성자 정보 등 모든 작업을 단계적으로 처리하는 로직이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 과정을 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;CompletableFuture를 이용하여 병렬처리하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;최종적으로 완성된 코드는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728478160196&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CompletableFuture&amp;lt;Void&amp;gt; backgroundTask = CompletableFuture.runAsync(() -&amp;gt; drawBackground(finalSvgGenerator, finalPostingBase));
CompletableFuture&amp;lt;Void&amp;gt; thumbnailTask = CompletableFuture.runAsync(() -&amp;gt; drawThumbnail(posting, finalSvgGenerator, finalPostingBase));
CompletableFuture&amp;lt;Void&amp;gt; textTask = backgroundTask.thenRunAsync(() -&amp;gt; drawText(posting, finalSvgGenerator, finalPostingBase));
CompletableFuture&amp;lt;Void&amp;gt; footerTask = textTask.thenRunAsync(() -&amp;gt; drawFooter(posting, finalSvgGenerator, finalPostingBase));
CompletableFuture&amp;lt;Void&amp;gt; authorImgTask = backgroundTask.thenRunAsync(() -&amp;gt; drawAuthorImg(posting, finalSvgGenerator, finalPostingBase));
CompletableFuture&amp;lt;Void&amp;gt; authorTextTask = footerTask.thenRunAsync(() -&amp;gt; drawAuthorText(posting, finalSvgGenerator, finalPostingBase));
CompletableFuture&amp;lt;Void&amp;gt; watermarkTask = CompletableFuture.runAsync(() -&amp;gt; drawWatermark(posting, finalSvgGenerator, finalPostingBase));
CompletableFuture&amp;lt;Void&amp;gt; strokeTask = CompletableFuture.allOf(thumbnailTask, authorTextTask)
        .thenRunAsync(() -&amp;gt; drawStroke(finalSvgGenerator, finalPostingBase));

CompletableFuture&amp;lt;Void&amp;gt; allTasks = CompletableFuture.allOf(
        backgroundTask, thumbnailTask, textTask, footerTask, authorImgTask, authorTextTask, watermarkTask, strokeTask);

allTasks.get();&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;여기서 중요한 것은 allOf() 메서드를 이용하여 병렬적으로 한 번에 처리하고 있는 것으로 보이지만, 사실 자세히 보면 엄연히 순서가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 백그라운드 작업이 완료된 후에 텍스트를 처리해 주고, 그 후에 Footer나 테두리 작업을 진행하는 순차적인 방식으로 코드를 작성했다. 이렇게 한 이유는 내가 사용한 svgGenerator객체의 설정 값이 공유되기 때문이었는데, 배경을 칠할 때 Color값을 White를 주었다면, 병렬 처리 과정에서 Text를 그려줄 때도 White가 설정되는 오류가 발생할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 순서가 필요한 작업은 의도적으로 순차적으로 연결하여 오류를 줄이며 성능을 끌어올렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;493&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xbdge/btsJZNba0pA/kQQwilekuPym8JOblViptK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xbdge/btsJZNba0pA/kQQwilekuPym8JOblViptK/img.png&quot; data-alt=&quot;비동기 프로그래밍을 도입하여 331ms까지 성능을 끌어올렸다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xbdge/btsJZNba0pA/kQQwilekuPym8JOblViptK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXbdge%2FbtsJZNba0pA%2FkQQwilekuPym8JOblViptK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;비동기 프로그래밍을 도입하여 331ms까지 성능을 끌어올렸다&quot; loading=&quot;lazy&quot; width=&quot;1418&quot; height=&quot;493&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;493&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;비동기 프로그래밍을 도입하여 331ms까지 성능을 끌어올렸다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과, &lt;b&gt;createSvgImageBox()의 호출시간이 664ms -&amp;gt; 331ms로 약 50.15% 개선되었다&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;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/96</guid>
      <comments>https://olrlobt.tistory.com/96#entry96comment</comments>
      <pubDate>Wed, 9 Oct 2024 22:18:33 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Object pooling(오브젝트 풀링) - Apache Commons Pool2로 구현하기</title>
      <link>https://olrlobt.tistory.com/95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Object pooling (오브젝트 풀링)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object pooling은 자주 사용되는 &lt;b&gt;객체를 미리 생성하고 재사용하여 메모리 할당 및 해제의 오버헤드를 줄이는 기술&lt;/b&gt;이다. Java와 같은 언어에서는 객체를 빈번하게 생성하고 폐기하는 작업이 성능에 큰 영향을 미칠 수 있다. 특히 복잡한 객체나 자주 호출되는 객체가 있는 경우, 이러한 작업은 GC(Garbage Collection)에 많은 부하를 줄 수 있으며, 이는 전체 시스템 성능에 악영향을 미치게 된다.&lt;/p&gt;
&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;Object pooling을 사용하면 자주 사용하는 객체들을 미리 만들어 두고 필요할 때 재활용할 수 있다.&lt;/b&gt; 객체를 새로 생성하는 대신, 풀(pool)에서 &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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (13).png&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQAOOq/btsJU0U9BcF/PHKkeqxezOG5CwDxegkrJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQAOOq/btsJU0U9BcF/PHKkeqxezOG5CwDxegkrJk/img.png&quot; data-alt=&quot;메서드 호출시마다 SVGGraphics2D 객체를 새로 생성해준다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQAOOq/btsJU0U9BcF/PHKkeqxezOG5CwDxegkrJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQAOOq%2FbtsJU0U9BcF%2FPHKkeqxezOG5CwDxegkrJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;메서드 호출시마다 SVGGraphics2D 객체를 새로 생성해준다&quot; loading=&quot;lazy&quot; width=&quot;1225&quot; height=&quot;167&quot; data-filename=&quot;image (13).png&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메서드 호출시마다 SVGGraphics2D 객체를 새로 생성해준다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우에는 개인 프로젝트에서 SVGGraphics2D &lt;b&gt;객체를 매번 만들어 주는 작업&lt;/b&gt;이 있었다. 이 과정에서 &lt;b&gt;적지 않은 오버헤드가 발생&lt;/b&gt;하는 것을 느꼈고, 이를 개선하기 위해 미리 만들어둔 설정으로 SVGGraphics2D객체를 만들 수 있으면 좋겠다는 고민을 했다. Clone()과 깊은 복사 등 많은 고민을 했지만,&amp;nbsp;기본적으로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SVGGraphics2D 객체는 깊은 복사를 지원하지 않았고, 얕은 복사를 하게 되면 API 작동 과정에서 다양한 설정들이 겹쳐 원하지 않는 결과물이 탄생하게 되었다. &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;
&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;&amp;nbsp;&lt;/span&gt;깊은 복사 대신,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Object Pooling&lt;/b&gt;을 통해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SVGGraphics2D 객체를 재사용&lt;/b&gt;하는 방법을 도입했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특히 깊은 복사가 불가능한 객체도 미리 생성해 두고 재사용하고 반환할 수 있다는 점은 내 프로젝트에 너무 딱 맞는 기술이었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Java에서 Object Pooling 이해하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1727985641042&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ObjectPool&amp;lt;T&amp;gt; {
    private Queue&amp;lt;T&amp;gt; pool = new LinkedList&amp;lt;&amp;gt;();
    private int maxSize;

    public ObjectPool(int maxSize) {
        this.maxSize = maxSize;
    }

    public synchronized T borrowObject(Supplier&amp;lt;T&amp;gt; creator) {
        if (pool.isEmpty()) {
            return creator.get();
        }
        return pool.poll();
    }

    public synchronized void returnObject(T obj) {
        if (pool.size() &amp;lt; maxSize) {
            pool.offer(obj);
        }
    }
}&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;위 코드는 Supplier&amp;lt;T&amp;gt;를 통해 Java에서 Object Pool을 직접 구현하는 가장 간단한 방법으로, &lt;b&gt;객체 생성 방식을 유연&lt;/b&gt;하게 제공한다. 코드가 직관적이기 때문에 알아보기 쉬울 것이며,&lt;b&gt;&amp;nbsp;borrowObject() 메서드를 사용해 객체를 풀에서 가져오고&lt;/b&gt;, &lt;b&gt;returnObject()로 객체를 반납하는 방식&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;borrowObject()와 returnObject()에서는&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;synchronized를 사용하여 여러 스레드가 동시에 객체 풀에 접근할 때의 안전성을 보장해야 한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 간단한 경우에 유용한 방법이지만, 객체 생명주기 관리 및 동기화 등 여러 복잡한 부분은 추가로 고려해야 하기 때문에 권장되는 방식은 아니며 되도록이면 아래 설명하는 라이브러릴 사용하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Apache Commons Pool2 라이브러리 활용 &lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Java에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Object Pooling&lt;/b&gt;을 구현할 때,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Apache Commons Pool2&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라이브러리를 사용할 수 있다. &lt;b&gt;commons-pool2&lt;/b&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;객체 풀&lt;/b&gt;을 쉽게 관리할 수 있도록 다양한 기능을 제공하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;성능 최적화&lt;/b&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자원 관리&lt;/b&gt;에 유리한 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Object Pooling을 적용하기 전, 나는 이 전 게시물에서 알아본 &lt;b&gt;IntelliJ Profiler&lt;/b&gt;를 이용하여 기존에 얼마만큼의 시간과 메모리가 소모되었는지 측정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727988029483&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static SVGGraphics2D init() {
    DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
    Document document = domImpl.createDocument(NAMESPACE_URL, SVG_ELEMENT, null);

    SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
    svgGenerator.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    svgGenerator.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
    svgGenerator.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
    svgGenerator.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
    svgGenerator.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
    svgGenerator.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
    return svgGenerator;
}&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;기존 SvgUtils의 init() 메서드의 경우, 위와 같이 DOM객체를 생성하여 SVG를 그리기 위해 각종 렌더링 작업을 거친 후 객체를 반환하는 메서드로 구성해 놓았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (13).png&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQAOOq/btsJU0U9BcF/PHKkeqxezOG5CwDxegkrJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQAOOq/btsJU0U9BcF/PHKkeqxezOG5CwDxegkrJk/img.png&quot; data-alt=&quot;SVGGraphics2D 객체를 한 번 생성하는데 약 200ms가 소요된다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQAOOq/btsJU0U9BcF/PHKkeqxezOG5CwDxegkrJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQAOOq%2FbtsJU0U9BcF%2FPHKkeqxezOG5CwDxegkrJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;SVGGraphics2D 객체를 한 번 생성하는데 약 200ms가 소요된다&quot; loading=&quot;lazy&quot; width=&quot;1225&quot; height=&quot;167&quot; data-filename=&quot;image (13).png&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SVGGraphics2D 객체를 한 번 생성하는데 약 200ms가 소요된다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVGGraphics2D 객체를 한 번 생성하는 데 걸리는 시간을 측정해 보니 약 200ms가 소요되었고, API를 호출할 때마다 필수적으로 한 번 호출되는 구조이기 때문에 성능 향상을 위해선 개선이 필수적이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1027&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKCk9Y/btsJUEE2vsu/1weo55WV6Eg3A4FvjMWKoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKCk9Y/btsJUEE2vsu/1weo55WV6Eg3A4FvjMWKoK/img.png&quot; data-alt=&quot;SVGGraphics2D 객체 생성시 약 60MB의 메모리가 사용된다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKCk9Y/btsJUEE2vsu/1weo55WV6Eg3A4FvjMWKoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKCk9Y%2FbtsJUEE2vsu%2F1weo55WV6Eg3A4FvjMWKoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;SVGGraphics2D 객체 생성시 약 60MB의 메모리가 사용된다&quot; loading=&quot;lazy&quot; width=&quot;538&quot; height=&quot;353&quot; data-origin-width=&quot;1027&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SVGGraphics2D 객체 생성시 약 60MB의 메모리가 사용된다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, IntelliJ Profiler를 이용하면 해당 메서드가 소모한 메모리를 확인할 수 있는데, API 호출 한 번당 60.47MB 정도가 소모됐다.&lt;/p&gt;
&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;Object Pooling&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Apache Commons Pool2 적용&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1728056263646&quot; class=&quot;groovy&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation group: 'org.apache.commons', name: 'commons-pool2', version: '2.12.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;먼저 &lt;b&gt;commons-pool2&lt;/b&gt;를 사용하기 위해 Gradle에 의존성을 추가해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&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_1727947038166&quot; class=&quot;scala&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.BasePooledObjectFactory;

@Component
public class SvgFactory extends BasePooledObjectFactory&amp;lt;SVGGraphics2D&amp;gt; {
    @Override
    public SVGGraphics2D create() {
        DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
        Document document = domImpl.createDocument(&quot;http://www.w3.org/2000/svg&quot;, &quot;svg&quot;, null);
        SVGGraphics2D svgGenerator = new SVGGraphics2D(document);

        svgGenerator.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        svgGenerator.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
        svgGenerator.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
        svgGenerator.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
        svgGenerator.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
        svgGenerator.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
        return svgGenerator;
    }

    @Override
    public PooledObject&amp;lt;SVGGraphics2D&amp;gt; wrap(SVGGraphics2D svgGraphics2D) {
        return new DefaultPooledObject&amp;lt;&amp;gt;(svgGraphics2D);
    }

    @Override
    public void passivateObject(PooledObject&amp;lt;SVGGraphics2D&amp;gt; pooledObject) {
        // 객체 반환 시 초기화 작업 (예: 객체 상태 초기화)
        SVGGraphics2D svgGenerator = pooledObject.getObject();
        svgGenerator.setSVGCanvasSize(new java.awt.Dimension(0, 0));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로, 객체 풀을 관리하기 위해 &lt;b&gt;BasePooledObjectFactory&lt;/b&gt; 추상 클래스를 사용하여 풀에서 사용되는 메서드들을 구현했다. BasePooledObjectFactory의 구현 메서드들은 각각 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;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;b&gt;create(): 객체 생성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;풀에 넣을&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체를 생성하는 메서드로&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체가 풀에 처음으로 추가될 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출된다. 기존 init()에 있던 코드를 넣어주었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;wrap(): 객체를 풀에 넣을 때 래핑&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성된 객체를&lt;span&gt;&amp;nbsp;&lt;/span&gt;풀에서 관리할 수 있는 형태로 감싸서&lt;span&gt;&amp;nbsp;&lt;/span&gt;반환한다. 풀 내에서 객체의 상태를 모니터링하거나 관리할 수 있도록 객체를&lt;span&gt;&amp;nbsp;&lt;/span&gt;포장하는 역할을 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DefaultPooledObject&lt;/b&gt;&lt;span&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;commons-pool2에서 제공하는 기본 래핑 클래스&lt;/b&gt;&lt;span&gt;이다. 이를 통해 풀에서 관리할 객체를 감싸고, 객체의 상태를 추적할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;passivateObject(): 객체 반환 시 초기화&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;풀에서 객체가&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용된 후 반환될 때, 객체를&lt;span&gt;&amp;nbsp;&lt;/span&gt;초기 상태로 되돌리는 작업을 한다. 즉, 객체가 다시 사용되기 전에&lt;span&gt;&amp;nbsp;&lt;/span&gt;상태를 재설정하는 것이다. 간단하게 SVGGraphics2D를 0의 크기로 초기화하는 작업을 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 풀을 관리하기 위한 작업을 마쳤으면, 객체를 사용하기 위한 Pool을 구성해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1727947038172&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

@Component
//@Lazy(false)
public class SvgPool {
    private final GenericObjectPool&amp;lt;SVGGraphics2D&amp;gt; pool;

    public SvgPool() {
        GenericObjectPoolConfig&amp;lt;SVGGraphics2D&amp;gt; config = new GenericObjectPoolConfig&amp;lt;&amp;gt;();
        config.setMaxTotal(20); // 최대 풀 크기 설정
        config.setMinIdle(10);   // 최소 유휴 객체 수
        config.setMaxIdle(20);   // 최대 유휴 객체 수
        config.setBlockWhenExhausted(true); // 풀에 여유 객체가 없을 때 대기

        this.pool = new GenericObjectPool&amp;lt;&amp;gt;(new SvgFactory(), config);

        try {
            for (int i = 0; i &amp;lt; pool.getMinIdle(); i++) {
                pool.addObject();
            }
        } catch (Exception ignored) {
        }
    }

    // 객체 대여
    public SVGGraphics2D borrowObject() {
        try {
            return pool.borrowObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 객체 반환
    public void returnObject(SVGGraphics2D svgGraphics2D) {
        pool.returnObject(svgGraphics2D);
    }
}&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;GenericObjectPool은 Apache Commons Pool2 라이브러리에서 제공하는 객체 풀 구현체이다. 해당 코드에서는 SVGGraphics2 D 객체들을 관리하는 Pool 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템이 시작될 때 풀이 생성되도록 @Lazy(false)를 사용할 수 있지만, 나는 이미 애플리케이션 시작 시 Service 클래스에서 생성자 주입 방식을 통해 SvgPool Bean을 생성했다. 만약 서비스에서 바로 Bean을 생성하지 않는다면, Pool에서 객체를 만들어 두는 성능상 이점이 줄어들 수 있으니 조건을 잘 고려해서 설정하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SvgPool이 생성되면,&amp;nbsp;GenericObjectPoolConfig를 이용하여 Pool에 대한 설정을 해준다. 이때,&lt;span&gt;&amp;nbsp;&lt;/span&gt;GenericObjectPool을 통해 객체 풀을 관리하는 클래스를 만들어주고, 여기서는 이전에 만든 SvgFactory를 인자로 넣어주어 객체 관리를 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pool.addObject()의 경우&lt;b&gt; 애플리케이션 시작 시 최소 유휴 객체를 생성하는 부분&lt;/b&gt;이다. 이 부분은 서비스에 따라 다르지만, 내 서비스에 경우 SVGGraphics2D을 생성에 드는 비용이 크기 때문에, &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;borrowObject()는 객체를 대여하는 부분으로, Pool에서 객체를 하나 꺼내어 반환한다. 만약 현재 사용 가능한 객체가 없다면, 설정에 따라 대기하거나 예외를 발생시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드를 통해 애플리케이션이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;필요한 시점에 객체를 가져와 사용&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;returnObject()는 객체를 반환하는 부분으로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;객체를 사용한 후에는 반드시 풀에 반환&lt;/b&gt;해야 다른 작업에서 재사용할 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;반환된 객체는 풀에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;유휴 상태&lt;/b&gt;로 유지되며, 다음 요청 시 재사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;Apache Commons Pool2&lt;/b&gt;를 사용해서 Object pooling을 할 준비가 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (14).png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XSxxX/btsJTJmQPo6/xAWmofe7xr6yTTsZ9ST0I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XSxxX/btsJTJmQPo6/xAWmofe7xr6yTTsZ9ST0I1/img.png&quot; data-alt=&quot;IntelliJ Profiler 힌트가 표기되지 않는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XSxxX/btsJTJmQPo6/xAWmofe7xr6yTTsZ9ST0I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXSxxX%2FbtsJTJmQPo6%2FxAWmofe7xr6yTTsZ9ST0I1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;IntelliJ Profiler 힌트가 표기되지 않는다&quot; loading=&quot;lazy&quot; width=&quot;1308&quot; height=&quot;187&quot; data-filename=&quot;image (14).png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IntelliJ Profiler 힌트가 표기되지 않는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 기존 init()으로 객체를 생성하던 부분을 borrowObject() 메서드를 이용하여 객체를 빌려와 주는 작업으로 바꾸어주고, 빌려온 객체를 돌려주기 위하여 returnObject() 메서드를 사용하는 형식으로 바꿔주었다. &lt;b&gt;이 부분을 특히 IntelliJ Profiler로 성능 측정을 하였지만 Hint가 나오지 않을 정도로 성능상 개선&lt;/b&gt;이 된 것이 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bg1wbM/btsJWO76fg0/M8PAckDxDpKIEXXslmDISK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bg1wbM/btsJWO76fg0/M8PAckDxDpKIEXXslmDISK/img.png&quot; data-alt=&quot;return 이후에 객체를 반환하기 위해 finally구문을 사용했다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bg1wbM/btsJWO76fg0/M8PAckDxDpKIEXXslmDISK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbg1wbM%2FbtsJWO76fg0%2FM8PAckDxDpKIEXXslmDISK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;return 이후에 객체를 반환하기 위해 finally구문을 사용했다.&quot; loading=&quot;lazy&quot; width=&quot;545&quot; height=&quot;117&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;return 이후에 객체를 반환하기 위해 finally구문을 사용했다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 최대한 성능상의 이점을 얻기 위해 반환 작업은 finally 구문을 사용했다. 나의 경우에는 객체 반환 작업에서 SVG의 크기를 조절하는 작업이 포함되어 있는데, 이 부분에서 오버헤드가 발생할 가능성을 고려했다. finally구문을 사용하면 return이 실행된 후 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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Apache Commons Pool2 적용 결과&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확실한 성능 차이를 측정하기 위해 Profiling 결과를 비교해 보았다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2835&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGQWI0/btsJTADEAPk/TNWBRa6VrbkDpLXamNLjP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGQWI0/btsJTADEAPk/TNWBRa6VrbkDpLXamNLjP0/img.png&quot; data-alt=&quot;Object Pooling 기법 도입 후 CPU Time 변화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGQWI0/btsJTADEAPk/TNWBRa6VrbkDpLXamNLjP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGQWI0%2FbtsJTADEAPk%2FTNWBRa6VrbkDpLXamNLjP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Object Pooling 기법 도입 후 CPU Time 변화&quot; loading=&quot;lazy&quot; width=&quot;2835&quot; height=&quot;475&quot; data-origin-width=&quot;2835&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Object Pooling 기법 도입 후 CPU Time 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 시간의 경우 기존&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;743ms -&amp;gt; 664ms로 약 10%, 100ms 가량 개선&lt;/b&gt;되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d3Zw9i/btsJTegDzIe/RQ3wzsY4ViDOlnGVQgC991/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d3Zw9i/btsJTegDzIe/RQ3wzsY4ViDOlnGVQgC991/img.png&quot; data-alt=&quot;Object Pooling 기법 도입 후 메모리 사용량 변화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d3Zw9i/btsJTegDzIe/RQ3wzsY4ViDOlnGVQgC991/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd3Zw9i%2FbtsJTegDzIe%2FRQ3wzsY4ViDOlnGVQgC991%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Object Pooling 기법 도입 후 메모리 사용량 변화&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;335&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Object Pooling 기법 도입 후 메모리 사용량 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 사용량의 경우 &lt;b&gt;60.47MB -&amp;gt; 5.03MB로 약 91.68%가량 개선&lt;/b&gt;되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;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;Object Pooling&lt;/b&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Profiler&lt;/b&gt;를 도입하면서부터 서비스의 병목 지점을 쉽게 찾아낼 수 있었고, 이를 통해 Object Pooling과 같은 성능 최적화 기법을 학습하고 적용할 수 있었다. 이를 통해 많은 개선을 이루어 냈지만, 배포 환경에서는 아직도 개선할 점이 한 투성이다. 특히 IntelliJ Profiler는 개발 환경에서 주로 사용되기 때문에 한계가 많이 느껴지는 부분이 있었다. 다음에는 VisualVM을 서비스에 도입해서 배포환경에서의 병목 지점도 해결해 볼 계획이다.&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/95</guid>
      <comments>https://olrlobt.tistory.com/95#entry95comment</comments>
      <pubDate>Sat, 5 Oct 2024 01:23:19 +0900</pubDate>
    </item>
    <item>
      <title>[Java] IntelliJ Profiler로 병목지점 찾아, Java ImageIO 성능 개선하기</title>
      <link>https://olrlobt.tistory.com/94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY를 진행하면서 개인 프로젝트로, Github README에 블로그 포스팅을 SVG 이미지 박스 형태로 띄워주는 위젯을 개발했었다. 적극적으로 홍보해서 사용자를 늘리고 싶은 마음은 있었지만, 아직 완성도가 낮다고 판단하여 그런 행동을 취하지는 않았었다. 그리고 SSAFY가 끝나고, 취업 준비를 하면서 바쁜 와중에 시간을 조금씩 투자하여 개선을 해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1727936949319&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;많은 개발자들의 고민 나는 깃허브 리드미에 내 블로그를 홍보하기 위해 링크를 해 놓곤 한다. 하지만, 이 전의 내 깃허브 리드미에는 아래와 같이 a 태그를 이용한 조촐한 이미지 링크만 띄워 &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/84&quot; data-og-url=&quot;https://olrlobt.tistory.com/84&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MTJh6/hyXed7DbB0/cXGhkxKPzgFt4oKExH52K0/img.png?width=800&amp;amp;height=832&amp;amp;face=0_0_800_832,https://scrap.kakaocdn.net/dn/nSuMk/hyXd8SMtN5/DlnLyaVmtU8aR9F0teiXqK/img.png?width=800&amp;amp;height=832&amp;amp;face=0_0_800_832,https://scrap.kakaocdn.net/dn/4Mnuk/hyXegQOCDB/1whOhVWYEL5vAXxlKdZWk0/img.png?width=1748&amp;amp;height=291&amp;amp;face=0_0_1748_291&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MTJh6/hyXed7DbB0/cXGhkxKPzgFt4oKExH52K0/img.png?width=800&amp;amp;height=832&amp;amp;face=0_0_800_832,https://scrap.kakaocdn.net/dn/nSuMk/hyXd8SMtN5/DlnLyaVmtU8aR9F0teiXqK/img.png?width=800&amp;amp;height=832&amp;amp;face=0_0_800_832,https://scrap.kakaocdn.net/dn/4Mnuk/hyXegQOCDB/1whOhVWYEL5vAXxlKdZWk0/img.png?width=1748&amp;amp;height=291&amp;amp;face=0_0_1748_291');&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;많은 개발자들의 고민 나는 깃허브 리드미에 내 블로그를 홍보하기 위해 링크를 해 놓곤 한다. 하지만, 이 전의 내 깃허브 리드미에는 아래와 같이 a 태그를 이용한 조촐한 이미지 링크만 띄워&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1727937255274&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 - olrlobt/blog-widget: Github Readme에 블로그 포스팅을 쉽게 노출해보자!&quot; data-og-description=&quot;Github Readme에 블로그 포스팅을 쉽게 노출해보자! Contribute to olrlobt/blog-widget development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/olrlobt/blog-widget&quot; data-og-url=&quot;https://github.com/olrlobt/blog-widget&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IoOJX/hyXedT5Pn0/gRLKmxKOOh0jPu6aFz5FXk/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236,https://scrap.kakaocdn.net/dn/q5yO7/hyXd2kJrvj/ykXumuFo5JaPx1suwmjBJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236&quot;&gt;&lt;a href=&quot;https://github.com/olrlobt/blog-widget&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/olrlobt/blog-widget&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IoOJX/hyXedT5Pn0/gRLKmxKOOh0jPu6aFz5FXk/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236,https://scrap.kakaocdn.net/dn/q5yO7/hyXd2kJrvj/ykXumuFo5JaPx1suwmjBJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236');&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 - olrlobt/blog-widget: Github Readme에 블로그 포스팅을 쉽게 노출해보자!&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Github Readme에 블로그 포스팅을 쉽게 노출해보자! Contribute to olrlobt/blog-widget development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 서비스 안정성 문제 원인 파악&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tYQtI/btsJSPVASfK/WOkp9g2Tw2jNAbWQpMsC01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tYQtI/btsJSPVASfK/WOkp9g2Tw2jNAbWQpMsC01/img.png&quot; data-alt=&quot;엑스박스로 서비스가 제대로 작동되지 않는다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tYQtI/btsJSPVASfK/WOkp9g2Tw2jNAbWQpMsC01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtYQtI%2FbtsJSPVASfK%2FWOkp9g2Tw2jNAbWQpMsC01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;엑스박스로 서비스가 제대로 작동되지 않는다&quot; loading=&quot;lazy&quot; width=&quot;328&quot; height=&quot;424&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;엑스박스로 서비스가 제대로 작동되지 않는다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 서비스 사용자를 적극적으로 늘리지 않은 가장 큰 이유는 &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;Github README 자체에서 API로 내 프로젝트에서 생성된 이미지를 가져오는 과정에서, 어떠한 이유에서인지 위와 같이 &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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lubai/btsJSL6NAJu/RPmPwiormY6doJeJ1mvQR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lubai/btsJSL6NAJu/RPmPwiormY6doJeJ1mvQR0/img.png&quot; data-alt=&quot;이미지 서버의 문제는 없었다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lubai/btsJSL6NAJu/RPmPwiormY6doJeJ1mvQR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLubai%2FbtsJSL6NAJu%2FRPmPwiormY6doJeJ1mvQR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;이미지 서버의 문제는 없었다&quot; loading=&quot;lazy&quot; width=&quot;1313&quot; height=&quot;434&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 서버의 문제는 없었다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 개발자 모드로 Github에서 이미지를 로드하는 과정을 살펴보면 어째서인지 파일 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;여기서 한 가지 추측해 볼 수 있는 것은, &lt;b&gt;이미지 서버 응답시간에 따른 타임아웃&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github README는 빠르고 안전하게 이미지를 보여주어야 하고, 이를 위해 이미지 프록시 서버인 Camo를 사용한다. 사용자가 이미지를 등록하거나 나처럼 API에서 호스팅 해주는 경우에도 모두 Camo서버에 저장하고 가져오는 과정으로 이루어진다.&lt;/p&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;b&gt;Camo가 요청을 타임아웃 처리를 할 수&amp;nbsp; 있기 때문에 엑스박스가 표시된다는 가정&lt;/b&gt;이다. Github docs나 Camo에 관련된 docs를 찾아 응답시간에 관한 정보를 찾고 싶은데, 아무리 찾아도 못 찾겠어서 추측으로 진행하게 되었다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. IntelliJ Profiler로 병목지점 찾기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전에는 성능을 개선을 본격적으로 하지 않았기 때문에 가장 단순한 방법으로 Log를 활용했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 실행 전과 후에 System.currentTimeMills()를 활용하여 시간 차이를 이용하여 단순하게 성능을 측정했다. 다른 Tool들을 학습하지 않고 바로 적용할 수 있는 방법이어서 많이 애용했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다가 SpringAOP를 도입하여 조금 더 구조화시켰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpringAOP를 이용하면 Aspect 관심사 설정으로 간편하게 원하는 메서드를 바꿀 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 매번 성능 개선 코드를 썼다 지우는 것이 아니라, Git Ignore에 작성만 해주면 성능 개선코드를 포함시키지 않고 프로젝트를 Push 할 수 있는 것도 장점이었다.&lt;/p&gt;
&lt;pre id=&quot;code_1727938977488&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Aspect
@Component
@Slf4j
public class ExecutionTimeAspect {

    @Pointcut(&quot;execution(* olrlobt.githubtistoryposting.api..*(..))&quot;)
    public void applicationPackagePointcut() {
        // Pointcut expression
    }

    @Pointcut(&quot;execution(* olrlobt.githubtistoryposting.service.ImageService..*(..))&quot;)
    public void imageServiceMethods() {
        // Pointcut expression
    }

    @Pointcut(&quot;execution(* olrlobt.githubtistoryposting.utils.ScrapingUtils..*(..))&quot;)
    public void scrapingMethods() {
        // Pointcut expression
    }

//    @Around(&quot;scrapingMethods()&quot;)
    public Object measureExecutionTime2(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        Object proceed = joinPoint.proceed(); // 실제 메서드 실행

        long executionTime = System.currentTimeMillis() - start;

        String methodName = joinPoint.getSignature().toShortString();
        log.info(&quot;{} scraping in {} ms&quot;, methodName, executionTime);
        return proceed;
    }

    @Around(&quot;imageServiceMethods()&quot;)
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        Object proceed = joinPoint.proceed(); // 실제 메서드 실행

        long executionTime = System.currentTimeMillis() - start;

        String methodName = joinPoint.getSignature().toShortString();
        log.info(&quot;{} executed in {} ms&quot;, methodName, executionTime);
        return proceed;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이 방법에도 한계가 존재했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOP로 &lt;b&gt;성능 로그를 찍는 자체가&lt;/b&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 한계점을 돌파하고 더 면밀한 성능 개선을 위해 다양한 툴을 찾아본 결과, 프로파일러를 도입하기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 내 프로젝트의 경우에는 DB를 사용하지 않고, API의 속도만 개선하면 되기 때문에 프로파일러는 아주 적합한 도구였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로파일러(Profiler)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로파일러&lt;/b&gt;(Profiler)는 &lt;b&gt;소프트웨어 성능 분석 도구&lt;/b&gt;로, 애플리케이션이 실행되는 동안 발생하는 다양한 성능 관련 데이터를 수집하고 분석하는 데 사용된다. 프로파일러는 주로 &lt;b&gt;CPU 사용량, 메모리 사용량, 메서드 호출 시간, 스레드 상태&lt;/b&gt; 등을 추적하여 성능 병목 구간을 찾아내고, 이를 기반으로 성능을 최적화할 수 있도록 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;대표적인 Java 프로파일러 도구:&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;IntelliJ Profiler&lt;/b&gt;: IntelliJ IDEA에 내장된 프로파일러로, Java 애플리케이션의 성능을 실시간으로 분석하고 플레임그래프 등을 통해 성능 문제를 시각적으로 보여준다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JProfiler&lt;/b&gt;: 상용 도구로, Java 애플리케이션의 CPU, 메모리, 스레드 등 다양한 성능 지표를 심층적으로 분석할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;VisualVM&lt;/b&gt;: 무료로 제공되는 JVM 기반의 성능 분석 도구로, 간단한 CPU 및 메모리 프로파일링을 제공한다.&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;JProfiler와 VisualVM 모두 높은 사용자 점유율을 보인다. 특히 2015년 자료에서 VisualVM이 조금 앞서는 것으로 확인했는데, 이는 JProfiler가 유료이기 때문이라 생각한다. 하지만 StackOverFlow의 대부분의 글에서 JProfiler를 사용하는 것을 봐서는 확실히 유료 값을 하나보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마음 같아선 JProfiler를 이용하고 싶지만, 그럴 순 없으니 &lt;b&gt;IntelliJ Ultimate 사용자에게만 제공되는 &lt;b&gt;IntelliJ Profiler를 사용하기로 했다.&lt;/b&gt;&amp;nbsp;&lt;/b&gt;Ultimate를 사용한 지는 꽤 됐는데, 프로파일링 기능을 제공하는지 사실 몰랐다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;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: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인텔리제이 프로파일러&lt;/b&gt;&lt;b&gt;(IntelliJ Profiler) &lt;/b&gt;&lt;b&gt;사용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkZRjj/btsJT54a7wf/RZ7k3rNS6vk9UsjocxEy4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkZRjj/btsJT54a7wf/RZ7k3rNS6vk9UsjocxEy4K/img.png&quot; data-alt=&quot;IntelliJ Profiler 실행 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkZRjj/btsJT54a7wf/RZ7k3rNS6vk9UsjocxEy4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkZRjj%2FbtsJT54a7wf%2FRZ7k3rNS6vk9UsjocxEy4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;IntelliJ Profiler 실행 방법&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;262&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IntelliJ Profiler 실행 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IntelliJ의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Profiler를 실행하는 것은 아주 간단하다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Run &amp;gt; Profile 'Project' with 'IntelliJ Profiler'를 선택하면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2607&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFOZX6/btsJSMdyKah/5CaCSwnQ8Ka9XjC0fGwjrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFOZX6/btsJSMdyKah/5CaCSwnQ8Ka9XjC0fGwjrk/img.png&quot; data-alt=&quot;IntelliJ Profiler 실행 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFOZX6/btsJSMdyKah/5CaCSwnQ8Ka9XjC0fGwjrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFOZX6%2FbtsJSMdyKah%2F5CaCSwnQ8Ka9XjC0fGwjrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;IntelliJ Profiler 실행 모습&quot; loading=&quot;lazy&quot; width=&quot;2607&quot; height=&quot;789&quot; data-origin-width=&quot;2607&quot; data-origin-height=&quot;789&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IntelliJ Profiler 실행 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 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;Spring 로고 위에서는 &quot;Profiling started&quot;라는 메시지와 함께, &lt;b&gt;우측으로 CPU 사용량과 Heap Memory 사용량을 실시간으로 표기해 준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 관측을 끝냈다면, &quot;Stop Recording and Show Results&quot; 버튼을 통해 &lt;b&gt;데이터 수집을 종료&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 과정에서 사용하고자 하는 API를 한번 호출해 주고, 프로파일링을 종료&lt;/b&gt;하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2620&quot; data-origin-height=&quot;795&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uJGwr/btsJT96qzd3/fX5pkZyXKnhuqyoMMXo8E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uJGwr/btsJT96qzd3/fX5pkZyXKnhuqyoMMXo8E1/img.png&quot; data-alt=&quot;IntelliJ Profiler 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uJGwr/btsJT96qzd3/fX5pkZyXKnhuqyoMMXo8E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJGwr%2FbtsJT96qzd3%2FfX5pkZyXKnhuqyoMMXo8E1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;IntelliJ Profiler 결과&quot; loading=&quot;lazy&quot; width=&quot;2620&quot; height=&quot;795&quot; data-origin-width=&quot;2620&quot; data-origin-height=&quot;795&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IntelliJ Profiler 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 화면으로 결과를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 5개의 탭으로 분류되는데, 각 탭이 나타내는 것은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;플레임 그래프&lt;/b&gt;: 성능 병목을 시각적으로 보여주는 그래프, 시간이 많이 걸린 함수가 넓게 표시됨.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;콜 트리&lt;/b&gt;: 메서드 호출 경로를 트리 구조로 보여주는 도구, 호출 관계와 순서를 분석.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메서드 리스트&lt;/b&gt;: 메서드별 실행 시간과 호출 횟수를 리스트 형태로 제공, 문제 메서드를 세밀하게 분석.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타임라인&lt;/b&gt;: 시간 경과에 따른 CPU, 메모리 사용량 변화 등을 시각적으로 표현, 성능 변화를 시간대별로 추적.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 탭&lt;/b&gt;: Garbage Collection, 스레드 전환 등의 시스템 이벤트를 추적하여 성능 문제의 원인을 파악.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1463&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Rhtow/btsJUN9zwLa/ZmRJ7KpzkJqlZ1rhMaHvrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Rhtow/btsJUN9zwLa/ZmRJ7KpzkJqlZ1rhMaHvrK/img.png&quot; data-alt=&quot;IDE 내부에서 Profiler가 성능을 표기해준다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Rhtow/btsJUN9zwLa/ZmRJ7KpzkJqlZ1rhMaHvrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRhtow%2FbtsJUN9zwLa%2FZmRJ7KpzkJqlZ1rhMaHvrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;IDE 내부에서 Profiler가 성능을 표기해준다&quot; loading=&quot;lazy&quot; width=&quot;1463&quot; height=&quot;369&quot; data-origin-width=&quot;1463&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IDE 내부에서 Profiler가 성능을 표기해준다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &lt;b&gt;IDE 내부에서 Hint 표기로 메서드 실행시간을 표기&lt;/b&gt;해 주는데, 이 기능이 내 프로젝트에서 특히 유용하게 작용하는 것 같다. 덕분에 병목지점을 아주 빠르게 찾을 수 있었고, 점차 개선해 나가고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Java ImageIO 성능 개선하기&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;718&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HNBaI/btsJUhJ3ShS/AOe7YA9BcA2b1hIwBK27QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HNBaI/btsJUhJ3ShS/AOe7YA9BcA2b1hIwBK27QK/img.png&quot; data-alt=&quot;Method List에서 병목지점 찾기, 특히 이미지를 다루는 부분에서 시간이 많이 소모된다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HNBaI/btsJUhJ3ShS/AOe7YA9BcA2b1hIwBK27QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHNBaI%2FbtsJUhJ3ShS%2FAOe7YA9BcA2b1hIwBK27QK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Method List에서 병목지점 찾기, 특히 이미지를 다루는 부분에서 시간이 많이 소모된다&quot; loading=&quot;lazy&quot; width=&quot;1488&quot; height=&quot;718&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;718&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Method List에서 병목지점 찾기, 특히 이미지를 다루는 부분에서 시간이 많이 소모된다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntellJ Profiler의 Method List 탭에서 검색 기능을 이용하여 CPU Time을 분석했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 createSvgImageBox()의 시간이 오래 걸렸고, 이 메서드는 크롤링한 정보들을 바탕으로 Svg 태그를 만드는 역할을 수행한다. 이 과정은 특히 썸네일 이미지를 만들거나, 작성자 이미지를 가져오거나, 제목이나 본문 텍스트를 작성하는 등 세부적인 작업들로 나누어져 있다.&lt;/p&gt;
&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;이미지를 그리는 과정에서 CDN서버에서 가져온 이미지를 SVG에 작성하기 위해서 Java의 ImageIO를 사용&lt;/b&gt;하는데, Java는 이미지 처리에 특화된 언어는 아니기 때문에 성능적인 문제가 자주 발생하곤 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ImageIO는 StackOverFlow에 검색한 결과가 많이 나올 정도로 그 성능 문제가 특히 심각한데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이 ImageIO에서 Write는 사용하지 않고 Read만 사용하기 때문에 비교적 안정적이긴 하지만, 세 이미지를 로드하는 과정이 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;따라서 이를 개선하기 위해 TwelveMonkey 라이브러리를 도입하기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; TwelveMonkey 도입 &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TwelveMonkeys&lt;/b&gt;는 Java용 &lt;b&gt;이미지 I/O 확장 라이브러리&lt;/b&gt;로, 기본 Java 이미지 I/O API에서 지원하지 않는 다양한 이미지 포맷을 읽고 쓸 수 있게 해 준다. 그뿐만 아니라,&amp;nbsp; javax.imageio 패키지를 확장하여 더 많은 이미지 포맷에 대한 지원을 추가하고, 기존 이미지 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;&lt;b&gt;TwelveMonkeys&lt;/b&gt;는 특정 포맷, 특히 &lt;b&gt;TIFF&lt;/b&gt;나 &lt;b&gt;JPEG 2000&lt;/b&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;기존 ImageIO와 완벽하게 호환&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;&lt;a href=&quot;https://mvnrepository.com/search?q=twelvemonkeys&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mvnrepository.com/search?q=twelvemonkeys&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 내가 선택한 이미지 포맷을 사용하는 것이 아니라, 사용자가 크롤링한 데이터의 이미지 포맷을 사용하기 때문에 대부분의 라이브러리를 포함시켜 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1727942545025&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'com.twelvemonkeys.imageio:imageio-webp:3.11.0'
implementation 'com.twelvemonkeys.imageio:imageio-jpeg:3.11.0'
implementation 'com.twelvemonkeys.imageio:imageio-tiff:3.11.0'
implementation 'com.twelvemonkeys.imageio:imageio-core:3.11.0'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Gradle 코끼리를 한 번 돌려주고 테스트한 결과..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2974&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8DbR2/btsJUiCbfKa/QJ440qIkOKAOrDBrUkkgek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8DbR2/btsJUiCbfKa/QJ440qIkOKAOrDBrUkkgek/img.png&quot; data-alt=&quot;TwelveMonkeys 도입 후 성능 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8DbR2/btsJUiCbfKa/QJ440qIkOKAOrDBrUkkgek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8DbR2%2FbtsJUiCbfKa%2FQJ440qIkOKAOrDBrUkkgek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;TwelveMonkeys 도입 후 성능 테스트&quot; loading=&quot;lazy&quot; width=&quot;2974&quot; height=&quot;710&quot; data-origin-width=&quot;2974&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TwelveMonkeys 도입 후 성능 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thmbnail 476ms -&amp;gt; 180ms&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AuthorImg 357ms -&amp;gt; 200ms&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;놀랍게도 &lt;b&gt;약 50% 정도의 성능 향상&lt;/b&gt;을 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했듯이 코드 수정은 거의 진행하지 않고 라이브러리만 추가한 결과이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Watermark와 같이 ImageIO를 사용하지 않는 부분에서는 변화가 없었지만, &lt;b&gt;라이브러리 하나만 적용해서 전체적으로 약 800ms의 처리 시간을 400ms로 절반 가까이 단축한 점&lt;/b&gt;은 매우 인상적이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. OkHttp 개선 &lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 단축 시간을 줄이고 나니 전체적인 Svg 생성 시간은 약 800ms, 그리고 Url 스크래핑 시간은 약 500ms 정도의 시간이 소요됐다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 README의 특성상 빠른 시간 안에 정보를 보여주어야 하니까 1,000ms 이내로 들어오게 하기 위해, 스크래핑 작업도 최적화를 검토했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;709&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRmjTI/btsJTCIdRL9/YmKni0zJR4VUi8f5roCuQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRmjTI/btsJTCIdRL9/YmKni0zJR4VUi8f5roCuQ0/img.png&quot; data-alt=&quot;posting 메서드가 스크래핑을 해주는 부분이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRmjTI/btsJTCIdRL9/YmKni0zJR4VUi8f5roCuQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRmjTI%2FbtsJTCIdRL9%2FYmKni0zJR4VUi8f5roCuQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;posting 메서드가 스크래핑을 해주는 부분이다&quot; loading=&quot;lazy&quot; width=&quot;1485&quot; height=&quot;709&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;709&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;posting 메서드가 스크래핑을 해주는 부분이다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 posting 메서드는 Jsoup을 이용한 웹 스크래핑을 바탕으로 Posting 객체를 만들어 주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 특히 Jsoup을 이용한 웹 스크래핑 과정이 오래 걸렸는데, 이 부분의 원래 코드는 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727943583341&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class ScrapingUtils {
	@Cacheable(cacheNames = &quot;url&quot;, key = &quot;#url&quot;, sync = true)
	public Document byUrl(String url) throws IOException {
		return Jsoup.connect(url).get();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Jsoup 라이브러리는 &lt;/span&gt;&lt;b&gt;HTML 파싱&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에 매우 유용하지만, 네트워크 연결이나 응답 시간에 대한 추가적인 최적화가 부족했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, 네트워크 지연이나 일시적인 연결 문제를 처리하는 데 있어 한계를 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 간결하고 직관적이었지만, 더 빠른 API 호출을 위해 OkHttp 라이브러리를 도입하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727943393591&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

@Component
public class ScrapingUtils {
	private final OkHttpClient client;

	public ScrapingUtils() {
		this.client = new OkHttpClient.Builder()
				.retryOnConnectionFailure(true) // 연결 실패 시 재시도
				.build();
	}

	@Cacheable(cacheNames = &quot;url&quot;, key = &quot;#url&quot;, sync = true)
	public Document byUrl(String url) throws IOException {
		Request request = new Request.Builder().url(url).build();

		try (Response response = client.newCall(request).execute()) {
			if (response.isSuccessful() &amp;amp;&amp;amp; response.body() != null) {
				return Jsoup.parse(response.body().string());
			}
		}
		return 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;OkHttp는 &lt;b&gt;비동기 처리&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;, &lt;/span&gt;&lt;b&gt;연결 실패 시 재시도&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;, 그리고 &lt;/span&gt;&lt;b&gt;커넥션 풀링&lt;/b&gt;과 같은 기능을 제공한다. 이로 인해 네트워크 성능이 향상되며, 안정적으로 데이터를 수집할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 OkHttp에서 &lt;b&gt;TCP 연결을 재사용&lt;/b&gt;할 수 있는 &lt;b&gt;커넥션 풀링&lt;/b&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;이 외에도, OkHttpClient를 재사용할 수 있는 구조로 설계하여 다음번 요청에도 같은 객체를 사용할 수 있게 개선했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2447&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2TVr/btsJTDUwzvg/EyWhreqLTT4AELaI3CzA71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2TVr/btsJTDUwzvg/EyWhreqLTT4AELaI3CzA71/img.png&quot; data-alt=&quot;OkHttp 라이브러리 도입으로 성능 개선 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2TVr/btsJTDUwzvg/EyWhreqLTT4AELaI3CzA71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2TVr%2FbtsJTDUwzvg%2FEyWhreqLTT4AELaI3CzA71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;OkHttp 라이브러리 도입으로 성능 개선 결과&quot; loading=&quot;lazy&quot; width=&quot;2447&quot; height=&quot;578&quot; data-origin-width=&quot;2447&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OkHttp 라이브러리 도입으로 성능 개선 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 기존 &lt;b&gt;486ms -&amp;gt; 299ms로 성능이 약 38% 개선&lt;/b&gt;되었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 Spring에서 아주 기본적인 Bean등록을 제대로 사용하지 못해 발생한 &lt;b&gt;성능적인 문제를&lt;/b&gt; &lt;b&gt;IntellJ Profiler를 통해 쉽게 파악&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;그리고 Object Pooling을 도입하거나 CompletableFuture를 이용하여 병렬 작업을 수행하게 리팩토링하여 성능을 크게 개선했지만, &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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn3Rtr/btsJTe8D1Vt/YlRTZ5z9Vp7cHhsCSFVzfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn3Rtr/btsJTe8D1Vt/YlRTZ5z9Vp7cHhsCSFVzfK/img.png&quot; data-alt=&quot;특정 페이지에서는 아직도 느린 속도를 보여준다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn3Rtr/btsJTe8D1Vt/YlRTZ5z9Vp7cHhsCSFVzfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn3Rtr%2FbtsJTe8D1Vt%2FYlRTZ5z9Vp7cHhsCSFVzfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;특정 페이지에서는 아직도 느린 속도를 보여준다.&quot; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;233&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;특정 페이지에서는 아직도 느린 속도를 보여준다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 많은 개선을 했지만, Github README의 특정 페이지에서는 아직 이미지를 호스팅 하기까지 많은 시간이 소요되는 것으로 관측된다. 특히 내 프로젝트 소개 README에서 이런 현상이 발생해서 엑스박스가 뜨는데,,&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;앞으로도 IntelliJ의 Profiler 기능을 적극 활용할 계획이고, 앞선 문제를 해결하기 위해서는 서비스 자체에 다른 Profiler를 도입하는 것까지 고려해 봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/94</guid>
      <comments>https://olrlobt.tistory.com/94#entry94comment</comments>
      <pubDate>Thu, 3 Oct 2024 18:27:46 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] 전략 패턴(Strategy Pattern), 팩토리 패턴(Factory Pattern), 레지스트리 패턴(Registry Pattern)</title>
      <link>https://olrlobt.tistory.com/93</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략 패턴(Strategy Pattern)과 팩토리 패턴(Factory Pattern), 레지스트리&amp;nbsp;패턴(Registry&amp;nbsp;Pattern)은 객체지향 설계에서 자주 사용되는 디자인 패턴이다. 해당 패턴 모두 객체 생성 및 행위 관련 문제를 해결하는데 도움을 주는데, 전략 패턴과 팩토리 패턴, 레지스트리 패턴을 결합하여 사용하는 경우 장점을 극대화하고, 더 유연하고 확장 가능한 시스템을 만들 수 있다. 오늘이 이 패턴들을 정리하면서, 개인 프로젝트에 어떻게 적용하였는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전략 패턴(Strategy&amp;nbsp;Pattern)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략 패턴(Strategy Pattern)은 객체지향 디자인 패턴 중 하나로, &lt;b&gt;실행 중에 알고리즘을 선택할 수 있게 하는 행위 소프트웨어 디자인 패턴&lt;/b&gt;이다. 이 패턴은 동일한 문제를 해결하는 여러 방법이 있을 때, 이들 방법을 각각의 클래스(전략)로 캡슐화하고, 실행 시점에 필요에 따라 전략을 바꿀 수 있도록 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;전략 패턴의 구조&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;전략 인터페이스(Strategy Interface)&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;구체적인 전략 클래스(Concrete Strategy Classes)&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;컨텍스트 클래스(Context Class)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전략 인터페이스를 통해 알고리즘을 사용하는 클래스. 이 클래스는 클라이언트가 선택한 전략을 사용하여 작업을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;전략 패턴 예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 설명에는 간단하게 내 개인 프로젝트를 활용했다. 간단하게 블로그를 스크래핑하여, Posting 이미지를 만들어주는 예제이며, posting() 메서드는 포스팅 이미지를 만들고, link() 메서드는 link를 반환하며, blog() 메서드는 블로그 이미지를 반환해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;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;b&gt;1. 전략 인터페이스 정의&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_1722254095666&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Blog {
	Posting posting(String blogName, int index, PostingType postingType) throws IOException;
	RedirectView link(String blogName, int index) throws IOException;
	Posting blog(String blogName) throws IOException;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 전략 패턴에서 사용할 인터페이스를 정의한다. 이 인터페이스는 알고리즘을 캡슐화하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 구체적인 전략 클래스&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722254177103&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Tistory implements Blog {

	@Override
	public Posting posting(String blogName, int index, PostingType postingType) throws IOException {
		/** 비즈니스 로직 **/
	}

	@Override
	public RedirectView link(String blogName, int index) throws IOException {
		/** 비즈니스 로직 **/
	}

	@Override
	public Posting blog(String blogName) throws IOException {
		/** 비즈니스 로직 **/
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722254468489&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Velog implements Blog {

	@Override
	public Posting posting(String blogName, int index, PostingType postingType) throws IOException {
		/** 비즈니스 로직 **/
	}

	@Override
	public RedirectView link(String blogName, int index) throws IOException {
		/** 비즈니스 로직 **/
	}

	@Override
	public Posting blog(String blogName) throws IOException {
		/** 비즈니스 로직 **/
	}
}&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;앞서 캡슐화한 인터페이스를 구현하는 구체적인 전략 클래스를 작성한다. 위 예제에서는 Blog 인터페이스를 각각 Tistory, Velog 클래스로 구현하여 각기 다른 비즈니스 로직을 수행하게 된다. 조금 더 자세히 설명하자면, Velog의 경우 Blog API를 지원하여 정보를 갖고 오기 위하여 API를 호출하는 로직을 수행하며, Tistory의 경우 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;size18&quot;&gt;&lt;b&gt;3. 컨텍스트 클래스 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722254245377&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PostingService {

    public Posting posting(String blogName, String platform, int index) throws IOException {
        Blog blog;

        if(platform.equals(&quot;tistory&quot;){
            blog = new Tistory();
        }else if(platform.equals(&quot;velog&quot;){
            blog = new Velog();
        }

        return blog.posting(blogName, index, PostingType. BlogPosting);
    }

    public RedirectView link(String blogName, String platform, int index) throws IOException {
        /** 비즈니스 로직 **/
    }

    public Posting blog(String blogName, String platform) throws IOException {
        /** 비즈니스 로직 **/
    }
}&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;즉, 위 예제에서는 platform 변수의 값을 어떻게 설정하느냐에 따라 Tistory의 크롤링 로직을 수행하기도, Velog의 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; 패턴(Strategy Pattern)이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;팩토리 패턴 (Factory Pattern)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팩토리 패턴(Factory Pattern)은 &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_1722259744377&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PostingService {
    public Posting posting(String blogName, String platform, int index) throws IOException {
        Blog blog;

        if(platform.equals(&quot;tistory&quot;){
            blog = new Tistory();
        }else if(platform.equals(&quot;velog&quot;){
            blog = new Velog();
        }

        return blog.posting(blogName, index, PostingType. BlogPosting);
    }
}&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;이 부분은 팩토리 패턴을 사용했다고 보기 어려운데, 객체 생성 부분의 로직을 분리하지 않아 posting() 메서드 안에 객체 생성 로직이 포함되어 있고, 새로운 블로그 플랫폼을 추가할 때마다 posting() 메서드에 조건문을 추가해야 하므로 확장성이 떨어지기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 부분에 팩토리 패턴을 적용해 보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팩토리 패턴은 크게 단순 팩토리/팩토리 메서드/ 추상 팩토리로 나눌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;단순 팩토리 (Simple Factory)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 팩토리는 객체 생성 로직을 하나의 클래스나 메서드로 분리하여 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BlogFactory 클래스를 선언하고, 해당 클래스 내부에서 객체를 생성하여 반환해 준다. 이렇게 구현하면서 팩토리 클래스에 객체를 생성하는 책임을 부여하게 되고, 확장을 할 때 해당 부분에서 객체 반환 로직을 추가하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1722260182625&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class BlogFactory {
    public static Blog getBlog(String platform) {
        if (platform.equals(&quot;tistory&quot;)) {
            return new Tistory();
        } else if (platform.equals(&quot;velog&quot;)) {
            return new Velog();
        }
        throw new IllegalArgumentException(&quot;Unsupported platform: &quot; + platform);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722260198841&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PostingService {
    public Posting posting(String blogName, String platform, int index) throws IOException {
        Blog blog = BlogFactory.getBlog(platform);
        return blog.posting(blogName, index, PostingType.BlogPosting);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;팩토리 메서드 (Factory Method) / 추상 팩토리 (Abstract Factory)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팩토리 메서드는 객체 생성에 대한 인터페이스를 정의하고, 하위 클래스에서 인스턴스를 생성하도록 한다. 단순 팩토리를 조금 더 구조화한 방식으로 볼 수 있으며, 추상 팩토리 패턴의 경우에는 인터페이스를 추상 클래스로 변경만 하면 되므로 생략한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722260368385&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface BlogFactory {
    Blog createBlog();
}

public class TistoryFactory implements BlogFactory {
    public Blog createBlog() {
        return new Tistory();
    }
}

public class VelogFactory implements BlogFactory {
    public Blog createBlog() {
        return new Velog();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722260377418&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PostingService {
    private final BlogFactory tistoryFactory = new TistoryFactory();
    private final BlogFactory velogFactory = new VelogFactory();

    public Posting posting(String blogName, String platform, int index) throws IOException {
        BlogFactory blogFactory;
        if (platform.equals(&quot;tistory&quot;)) {
            blogFactory = tistoryFactory;
        } else if (platform.equals(&quot;velog&quot;)) {
            blogFactory = velogFactory;
        } else {
            throw new IllegalArgumentException(&quot;Unsupported platform: &quot; + platform);
        }
        Blog blog = blogFactory.createBlog();
        return blog.posting(blogName, index, PostingType.BlogPosting);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두 방법으로 팩토리 패턴을 사용할 수 있으며, 객체의 생성을 별도의 클래스로 분리한다는 점에서 단일 책임 원칙을 지키게 된다. 또한 새로운 기능이 추가되면, 해당 팩토리 내부에서 로직 수정이 이루어지므로 열린-폐쇄 원칙을 지킬 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 팩토리 패턴을 너무 남발하게 된다면 클래스가 많아지게 되므로 오히려 복잡해질 수 있고, 여러 클래스를 오가야 되는 불편함이 증가할 수 있으니 유의해서 사용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 레지스트리 패턴 (Registry Pattern) &lt;/b&gt;&lt;/h2&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레지스트리 패턴 역시, 팩토리 패턴과 같이 사용하면 효과적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팩토리 패턴에서 기능이 추가될 때 객체를 조건문으로 처리하여 반환하는 방식은 확장성 측면에서 한계를 가질 수 있는데, 이러한 한계를 레지스트리 패턴을 사용하면서 어느 정도 보완이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;레지스트리 패턴 예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레지스트리 패턴을 사용하면 조건문을 사용하지 않고도 객체를 동적으로 관리할 수 있다. 객체를 미리 Map에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;static을 통해 전역적으로 &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;아래 예제는 Spring 프레임워크에 Bean으로 등록하여 싱글톤 스코프를 사용하기 때문에, static을 사용하지 않고 구현하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722264830372&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class BlogFactory {
	private final Map&amp;lt;String, Blog&amp;gt; blogStrategyMap;

	public BlogFactory(Tistory tistory, Velog velog, Anything anything) {
		blogStrategyMap = new HashMap&amp;lt;&amp;gt;();
		blogStrategyMap.put(&quot;tistory&quot;, tistory);
		blogStrategyMap.put(&quot;t&quot;, tistory);
		blogStrategyMap.put(&quot;velog&quot;, velog);
		blogStrategyMap.put(&quot;v&quot;, velog);
		blogStrategyMap.put(&quot;else&quot;, anything);
	}

	public Blog getBlog(String platform) {
		return blogStrategyMap.getOrDefault(platform, blogStrategyMap.get(&quot;else&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;앞선 팩토리 패턴에서의 조건문들에 비해 구조가 간단해 보이고 훨씬 가독성이 좋은 것을 알 수 있다. 여기서 객체를 조회할 때는 getBlog()를 통해 간단히 조회가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722265388709&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 PostingService {

	private final BlogFactory blogFactory;

	public Posting posting(String blogName, String platform, int index) throws IOException {
		Blog blog = blogFactory.getBlog(platform);
		return blog.posting(blogName, index, PostingType. BlogPosting);
	}
    
	public RedirectView link(String blogName, String platform, int index) throws IOException {
		Blog blog = blogFactory.getBlog(platform);
		return blog.link(blogName, index);
	}

	public Posting blog(String blogName, String platform) throws IOException {
		Blog blog = blogFactory.getBlog(platform);
		return blog.blog(blogName);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는 비즈니스 로직을 위와 같이 간추릴 수 있었고, 팩토리 클래스를 의존성 주입받아 호출하는 방식으로 동적인 로직을 수행하게 구성하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 추가적인 기능(해당 예제에서는 플랫폼)이 추가된다면, BlogFactory에 객체를 하나 추가해 주고, 해당 클래스의 비즈니스 로직을 구현하기만 하면 기능 추가가 완료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/93</guid>
      <comments>https://olrlobt.tistory.com/93#entry93comment</comments>
      <pubDate>Tue, 30 Jul 2024 00:19:08 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] Blue/Green 무중단 배포(Zero Downtime Deployment)</title>
      <link>https://olrlobt.tistory.com/92</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY 2학기 공통 프로젝트에서 INFRA를 맡았었다. 처음 해보는 인프라였기에 개발서버와 운영서버를 분리할 생각조차 하지 못했고, 프로젝트를 진행하면서 프론트 API 연결작업을 할 때는 운영서버의 API를 가져와 사용했다. 그러다 보니, 배포 과정에서 Downtime이 생기게 되어 서버가 재실행되는 동안 FE에서 API 연결 작업을 할 수 없는 불편함을 느꼈다. 이후, 개발서버 분리와 무중단 배포에 대해서 알게 되었고, 개인적으로 도전하고 싶어서 자율 프로젝트 때 팀원들에게 이 부분은 꼭 내가 하고 싶다고 말했었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 무중단 배포에 대해 알아보고, 자율 프로젝트에서 어떤 식으로 구현했는지 정리해 본다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;다운타임 (Downtime)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 올라가는 동안 API 호출이 불가능한 상태를 일반적으로 &quot;&lt;b&gt;다운타임(downtime)&lt;/b&gt;&quot;이라고 한다. 다운타임은 서버나 서비스가 일시적으로 작동하지 않아 사용자나 다른 시스템이 해당 서버에 접근할 수 없는 상태를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 서버 유지보수, 업데이트, 하드웨어 업그레이드 등 계획된 작업으로 인해 발생하는 다운타임을 &lt;b&gt;계획된 다운타임 (Planned Downtime)이라고&lt;/b&gt; 하며, 서버 오류, 하드웨어 고장, 네트워크 문제 등 예기치 못한 상황으로 인해 발생하는 다운타임을 &lt;b&gt;비계획 다운타임 (Unplanned Downtime)이라&lt;/b&gt; 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;무중단 배포 (Zero Downtime Deployment)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무중단 배포는 애플리케이션이나 서비스의 새 버전을 배포하는 동안 &lt;b&gt;서비스 중단 없이 사용자가 지속적으로 서비스를 이용할 수 있도록 하는 배포&lt;/b&gt; &lt;b&gt;전략&lt;/b&gt;이다&lt;b&gt;.&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;무중단 배포의 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;고가용성&lt;/b&gt;: 서비스 중단 없이 새로운 기능을 배포할 수 있어, 사용자 경험이 유지된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 롤백&lt;/b&gt;: 문제 발생 시 신속하게 이전 버전으로 롤백할 수 있어, 서비스의 안정성이 높아진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;연속적인 업데이트&lt;/b&gt;: 빈번한 배포와 업데이트가 가능해, 빠른 기능 추가와 버그 수정을 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;무중단 배포의 단점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;복잡성 증가&lt;/b&gt;: 동시 운영 환경, 트래픽 전환, 모니터링 등 관리가 복잡해질 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리소스 요구&lt;/b&gt;: 두 개의 운영 환경을 유지해야 하므로, 리소스와 비용이 증가할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인프라 요구&lt;/b&gt;: 무중단 배포를 지원하는 인프라와 도구가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;무중단 배포의 종류&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무중단 배포의 주요 전략으로는 Blue/Green 배포, 롤링 업데이트, 카나리 배포가 있다. 여기서 &lt;b&gt;Blue/Green&lt;/b&gt;의 경우 &lt;b&gt;한 번에 전환&lt;/b&gt;이 이루어지는 방식이고, &lt;b&gt;롤링 업데이트&lt;/b&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Blue/Green 배포&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;환경 분리&lt;/b&gt;: 두 개의 운영 환경(Blue와 Green)을 유지한다. 현재 운영 중인 환경을 Blue라고 하고, 새 버전이 배포될 환경을 Green이라고 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배포 및 테스트&lt;/b&gt;: Green 환경에 새 버전을 배포하고, 충분히 테스트한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트래픽 전환&lt;/b&gt;: Green 환경이 안정적이라면, 로드 밸런서를 통해 트래픽을 Blue 환경에서 Green 환경으로 전환한다. 문제가 발생하면 즉시 Blue 환경으로 롤백한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 롤링 업데이트&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;순차적 업데이트&lt;/b&gt;: 서버나 컨테이너 그룹을 순차적으로 업데이트하여, 모든 서버가 새 버전으로 전환될 때까지 점진적으로 진행한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;부하 분산&lt;/b&gt;: 일부 서버가 업데이트되는 동안 나머지 서버는 여전히 트래픽을 처리하므로, 전체 서비스가 중단되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 카나리 배포&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;소규모 배포&lt;/b&gt;: 새 버전을 소수의 사용자에게 먼저 배포하여, 실사용 환경에서 문제를 확인한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;점진적 확대&lt;/b&gt;: 초기 사용자 그룹에서 문제가 없으면 점진적으로 배포 범위를 확대하여 전체 사용자에게 배포한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 기술과 전략이 그렇지만, 항상 내가 사용하는 환경과 요구사항에 적합한지를 고려해서 선택해야 한다. &lt;b&gt;Blue/Green 배포&lt;/b&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우에는 인프라가 충분히 확보되어 있고, 서비스 중단을 줄이는 것이 목적이며, 개발 환경에서 포트폴리오용 프로젝트이기 때문에 대규모 변경이 많이 일어나는 환경이었다. 따라서 점진적 배포는 불필요하였고, &lt;b&gt;Blue/Green 전략&lt;/b&gt;을 사용하기로 했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Blue/Green 배포 전략&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 간단히 설명을 했지만, 다시 한번 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blue/Green 배포 전략은 두 개의 운영 환경(Blue와 Green)을 사용하는 배포 전략이다. 이때, 이론적으로 Blue 환경은 현재 프로덕션에서 운영 중인 환경을 뜻하고, Green 환경은 새 버전의 애플리케이션이 배포될 환경을 의미한다. 새로운 애플리케이션 버전이 Green 환경에 배포되고 테스트가 완료되면, 로드 밸런서를 사용하여 트래픽을 Blue 환경에서 Green 환경으로 전환하는 방식이다. 이때, 문제가 발생하면 Blue환경이 아직 종료가 되지 않았기 때문에, 즉시 트래픽을 다시 Blue 환경으로 롤백할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 '&lt;b&gt;이론적으로&lt;/b&gt;'라는 표현을 쓴 이유는 &lt;b&gt;특정 색이 항상 운영 중인 환경이어야 할 필요가 없기 때문&lt;/b&gt;이다. 중요한 점은 두 환경 중 하나가 항상 운영 중이어서 사용자에게 서비스를 제공하고, 다른 하나는 새로운 버전 배포와 테스트를 위해 사용된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2598&quot; data-origin-height=&quot;1437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDPfvL/btsItIWzxhj/nTJqlqBroUWvokKHKs6qck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDPfvL/btsItIWzxhj/nTJqlqBroUWvokKHKs6qck/img.png&quot; data-alt=&quot;현재 프로젝트의 로드밸런서는 Nginx를 사용하고 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDPfvL/btsItIWzxhj/nTJqlqBroUWvokKHKs6qck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDPfvL%2FbtsItIWzxhj%2FnTJqlqBroUWvokKHKs6qck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;현재 프로젝트의 로드밸런서는 Nginx를 사용하고 있다&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;1437&quot; data-origin-width=&quot;2598&quot; data-origin-height=&quot;1437&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현재 프로젝트의 로드밸런서는 Nginx를 사용하고 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 내 프로젝트의 경우, Nginx를 로드 밸런서로 사용하고 있다. 따라서, 전체적인 동작 과정은 다음과 같다.&lt;/p&gt;
&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. Docker에 기존에 운영 중인 환경을 Blue로 명칭 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 새로 릴리즈할 환경을 Green으로 명칭 하여 동시에 Ec2서버에 띄운다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Green 환경을 테스트하고 HeathCheck 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Nginx의 트래픽을 Green으로 변경하고, 기존 Blue 서버를 중단한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Blue/Green 무중단 배포 적용하기&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;b&gt;Health Checks를 위한&lt;/b&gt;&amp;nbsp;Spring Actuator&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Actuator는 Spring Boot 애플리케이션의 모니터링 및 관리 기능을 제공하는 라이브러리이다. 개발자가 애플리케이션의 상태와 운영 정보를 쉽게 확인하고 관리할 수 있도록 도와주는 역할을 하는데, 여기서 우리가 이용할 기능은 &lt;b&gt;헬스 체크(Health Checks)&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;헬스 체크는 간단하게 /actuator/health 엔드포인트를 통해 애플리케이션의 상태를 확인할 수 있는데, 이를 통해 릴리즈 환경이 제대로 실행되었는지 확인하는 작업을 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720541287222&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '3.2.5'&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;간단하게 Build.gradle에 위처럼 의존성을 추가해 주면 바로 사용이 가능하고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720541317139&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;management:
  endpoints:
    web:
      exposure:
        include: health
        exclude: '*'&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;'application.properties' 또는 'application.yml'에서 설정을 통해 불필요한 엔드포인트 접근을 방지할 수 있다. 이번 포스팅에서 사용하는 것은 헬스체크뿐이기 때문에, &lt;b&gt;불필요한 엔드포인트는 접근을 막아주자&lt;/b&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;application.yml 동적 포트 설정(선택)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blue/Green 배포에서는 새로운 버전의 애플리케이션을 배포할 때 일시적으로 두 버전이 동시에 실행된다. 따라서 두 버전이 같은 포트를 사용하면 포트 충돌이 발생하여 문제를 발생시킬 수 있으므로, 동적 포트를 할당해 주어야 한다. 하지만, &lt;b&gt;Docker를 사용하는 환경에서는 외부포트와 내부포트가 분리되기 때문에, 접근을 위한 외부 포트만 변경해 주면&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;따라서 Docker를 사용하는 경우 이 설정은 선택적이며, profile을 설정함에 따라 로깅 작업을 할 때 더 편리함이 있음만 알아두고 넘어가자.&lt;/p&gt;
&lt;pre id=&quot;code_1720592593776&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  profiles:
    active: blue                // 기본적으로 활성화할 프로파일
  application:
    name: blog-widget
server:
  servlet:
    encoding:                // 기타 다른 설정들
      charset: utf-8
      enabled: true
      force: true


---                        // Blue 프로파일 설정 : 8080 포트를 사용
spring:
  config:
    activate:
      on-profile: blue
server:
  port: 8080

---                        // Green 프로파일 설정 : 8081 포트를 사용
spring:
  config:
    activate:
      on-profile: green
server:
  port: 8081&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;docker-compose.yml 설정&lt;/b&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 설정한 application.yml 설정 파일이 적용될 수 있도록, docker-compose에서 environment 설정을 해 준다. 마찬가지로 외부 포트를 동일시할 경우에는 똑같이 8080 포트를 바라보게 하면 되고, 분리를 하였다면 그에 맞추어 설정을 추가해 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1720592568405&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3'

services:
  spring-blue:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - &quot;8080:8080&quot;
    environment:
      - TZ=Asia/Seoul
      - SPRING_PROFILES_ACTIVE=production // ,blue

  spring-green:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - &quot;8081:8080&quot; // application.yml 설정에 따라 변경
	environment:
	  - TZ=Asia/Seoul
	  - SPRING_PROFILES_ACTIVE=production // , green
      
/* 기타 다른 서비스 */&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Bash를 이용한 Blue/Green 적용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, Bash를 이용한 방법으로 Blue/Green 무중단 배포를 적용해 보았다. 정상적으로 작동하는지 궁금했고, Blue/Green에 대한 이해가 필요했다. 아래 과정은 메모장에 deploy.sh 파일로 적어서 EC2로 이동해도 되고, ubuntu환경에서 직접 만들어서 사용해도 상관없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;#1 릴리즈 환경 색상 판별과 실행&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1720537410139&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#1
EXIST_BLUE=$(docker-compose ps | grep &quot;spring-blue&quot; | grep Up)

if [ -z &quot;$EXIST_BLUE&quot; ]; then
    docker-compose up -d spring-blue
    BEFORE_COLOR=&quot;green&quot;
    AFTER_COLOR=&quot;blue&quot;
    BEFORE_PORT=8081
    AFTER_PORT=8080
else
    docker-compose up -d spring-green
    BEFORE_COLOR=&quot;blue&quot;
    AFTER_COLOR=&quot;green&quot;
    BEFORE_PORT=8080
    AFTER_PORT=8081
fi

echo &quot;===== ${AFTER_COLOR} server up(port:${AFTER_PORT}) =====&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;앞서 Blue/Green 색상은 별 의미가 없다고 언급했었다. 따라서, &lt;b&gt;현재 어떤 색상의 이름으로 서비스가 실행되고 있는 지를 판별&lt;/b&gt;할 필요가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 EXIST_BLUE 변수에 운영환경이 어떤 색상의 이름으로 실행 중인지 판별하여 저장해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, `docker-compose ps` 명령을 사용하여 현재 실행 중인 컨테이너 목록을 확인한다. 여기서 `grep` 명령어를 통해&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'spring-blue'라는 이름의 서비스를 필터링하고, 이 서비스가 Status가 'Up' 실행 중인지 확인한다.&lt;/p&gt;
&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;EXIST_BLUE에는 True라는 값이 들어가고, 실행 중이지 않다면 빈 값이 들어가게 된다.&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;1548&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGa8G8/btsItHwzOv1/WrmUaRPDzN4KjSkK8mtJvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGa8G8/btsItHwzOv1/WrmUaRPDzN4KjSkK8mtJvK/img.png&quot; data-alt=&quot;#1 명령어 실행 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGa8G8/btsItHwzOv1/WrmUaRPDzN4KjSkK8mtJvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGa8G8%2FbtsItHwzOv1%2FWrmUaRPDzN4KjSkK8mtJvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;#1 명령어 실행 모습&quot; loading=&quot;lazy&quot; width=&quot;1548&quot; height=&quot;339&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;#1 명령어 실행 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;이후 `if [ -z &quot;$EXIST_BLUE&quot; ];` 조건식으로 알맞은 변수를 설정해 준다. 예를 들어 Blue서비스가 이미 실행 중이라면 EXIST_BLUE 변수에 값이 있으므로, else 블록이 실행된다. 이 블록에서는 `spring-green` 서비스를 백그라운드에서 실행하며, 뒤에서 사용할 변수들을 지정해 준다.&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;echo문은 필요하지 않다면 제거해도 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;#2 릴리즈 환경 응답 확인&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1720538746726&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 2
for cnt in {1..10}
do
    echo &quot;===== 서버 응답 확인중(${cnt}/10) =====&quot;;
    UP=$(curl -s ${YOUR_DOMAIN}:${AFTER_PORT}/actuator/health)
    if [ -z &quot;${UP}&quot; ]
        then
            sleep 10
            continue
        else
            break
    fi
done

if [ $cnt -eq 10 ]
then
    echo &quot;===== 서버 실행 실패 =====&quot;
    exit 1
fi&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;#2에서는 Spring Actuator를 이용하여 &lt;b&gt;릴리즈 환경이 정상적으로 실행되었음을 확인하는 과정&lt;/b&gt;이다.&amp;nbsp; 이때, Spring의 경우 실행하는 시간까지 고려하여 10초간 대기하는 과정을 10번 반복하게 코드를 작성하였다. 확인해 보니, 내 서비스의 경우 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;또한, exit 부분에서 서버 실행이 실패되었음에도 Docker에서 프로젝트가 돌아가는 경우가 발생할 수 있다. 이 부분에 ` docker-compose down -v &lt;span&gt;${AFTER_COLOR}`를 추가해서 서비스를 종료하는 코드를 추가적으로 넣는 걸 고려해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;#3 로드 밸런서 Nginx 설정 변경&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1720539402025&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 3
echo &quot;===== Nginx 설정 변경 =====&quot;
docker exec -it nginx /bin/bash -c &quot;sed -i 's/:${BEFORE_PORT}/:${AFTER_PORT}/' /etc/nginx/conf.d/default.conf &amp;amp;&amp;amp; nginx -s reload&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;#2에서 서버가 정상적으로 실행이 되었다면, &lt;b&gt;로드밸런서를 이용하여 트래픽을 릴리즈 서버로 전환&lt;/b&gt;시켜 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 Nginx를 컨테이너로 사용하고 있기 때문에 `docker &lt;span&gt;exec` 명령어를 사용하였다. 그리고 `sed -i ` 명령어를 사용하여 ' /etc/nginx/conf.d/default.conf' 경로의 Nginx 설정 파일의 ':BEFORE_PORT를 ':AFTER_PORT'로 치환해 주었다.&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;size18&quot;&gt;nginx.conf의 Server 블록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, #3의 과정을 거치면 nginx의 default.conf 파일 sever 블록단에 있는 8080 포트가, 8081 포트로 바뀌게 되는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1720593704642&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen 443 ssl;
    server_name blogwidget.com;
    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/blogwidget.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blogwidget.com/privkey.pem;


    location / {
        proxy_pass http://blogwidget.com:8080; // 이 부분이 8081로 변경 됨.
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;#4 기존 운영서버 서비스 종료&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1720539475717&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4
echo &quot;$BEFORE_COLOR server down(port:${BEFORE_PORT})&quot;
docker-compose stop spring-${BEFORE_COLOR}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트래픽 전환까지 마무리가 되었다면, 기존 사용 중인 서버를 종료해주어야 한다. 간단하게 `docker-compose stop` 명령어로 종료해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;# deploy.sh 전체 코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1720537202885&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#1
EXIST_BLUE=$(docker-compose ps | grep &quot;spring-blue&quot; | grep Up)

if [ -z &quot;$EXIST_BLUE&quot; ]; then
    docker-compose up -d spring-blue
    BEFORE_COLOR=&quot;green&quot;
    AFTER_COLOR=&quot;blue&quot;
    BEFORE_PORT=8081
    AFTER_PORT=8080
else
    docker-compose up -d spring-green
    BEFORE_COLOR=&quot;blue&quot;
    AFTER_COLOR=&quot;green&quot;
    BEFORE_PORT=8080
    AFTER_PORT=8081
fi

echo &quot;===== ${AFTER_COLOR} server up(port:${AFTER_PORT}) =====&quot;

# 2
for cnt in {1..10}
do
    echo &quot;===== 서버 응답 확인중(${cnt}/10) =====&quot;;
    UP=$(curl -s http://blogwidget.com:${AFTER_PORT}/actuator/health)
    if [ -z &quot;${UP}&quot; ]
        then
            sleep 10
            continue
        else
            break
    fi
done

if [ $cnt -eq 10 ]
then
    echo &quot;===== 서버 실행 실패 =====&quot;
    exit 1
fi

# 3
echo &quot;===== Nginx 설정 변경 =====&quot;
docker exec -it nginx /bin/bash -c &quot;sed -i 's/${BEFORE_PORT}/${AFTER_PORT}/' /etc/nginx/conf.d/default.conf &amp;amp;&amp;amp; nginx -s reload&quot;

echo &quot;$BEFORE_COLOR server down(port:${BEFORE_PORT})&quot;
docker-compose stop spring-${BEFORE_COLOR}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu로 작성한 스크립트를 이동시켜 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1720877697382&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scp -i /path/to/private/key /fakePath/deploy.sh ubuntu@remote_host:/home/ubuntu/deploy.sh&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;현재는 home 위치로 이동시켰고, 해당 위치에서 bash를 실행하면 docker-compose 명령어가 현재 위치에서 시작되기 때문에, 같은 위치에 docker-compose.yml을 위치시켜야 한다. 또한 해당 docker-compose 설정에 따라 Dockerfile과. jar파일도 적절히 위치시켜야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 다음 명령어를 통해 Bash스크립트를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1720877859639&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./deploy.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nWNJh/btsIyHQP1CR/QW2t23RmZy7bOsiTm5bDZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nWNJh/btsIyHQP1CR/QW2t23RmZy7bOsiTm5bDZ1/img.png&quot; data-alt=&quot;Blue가 이미 실행중 / Green이 이미 실행중&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nWNJh/btsIyHQP1CR/QW2t23RmZy7bOsiTm5bDZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnWNJh%2FbtsIyHQP1CR%2FQW2t23RmZy7bOsiTm5bDZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Blue가 이미 실행중 / Green이 이미 실행중&quot; loading=&quot;lazy&quot; width=&quot;1074&quot; height=&quot;205&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;205&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Blue가 이미 실행중 / Green이 이미 실행중&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트에&amp;nbsp; 따라 docker에서 실행 중 컨테이너 색상을 찾고, 이에 맞게 배포를 실행하는 것을 알 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Jenkins를 이용한 Blue/Green 적용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bash뿐만 아니라, Jenkins의 CI/CD에서도 스크립트를 통하여 무중단 배포를 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적인 구성 방법은 앞선 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;먼저, 앞선 방법과 똑같이 &lt;b&gt;&lt;b&gt;Health Checks를 위한&lt;/b&gt; Spring Actuator, 동적 포트를 할당하고 진행한다.&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Jenkins CD PipeLine&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1720597590433&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pipeline { 
    agent any

    stages {
        stage ('[Test]') {
            /** 생략 **/
        }
        stage ('[Build]') {
             /** 생략 **/
        }
        stage ('[Deploy]') {
           	 /** 생략 **/
        }
        stage ('Service Start') {
            steps {
                 script {
                    def status = sh(script: '''
                        EXIST_BLUE=$(docker-compose ps | grep &quot;backend-blue&quot; | grep Up || echo &quot;NoActive&quot;)
                        echo &quot;EXIST_BLUE: $EXIST_BLUE&quot;
                    ''', returnStdout: true).trim()

                    def BEFORE_COLOR = status.contains(&quot;NoActive&quot;) ? &quot;green&quot; : &quot;blue&quot;
                    def AFTER_COLOR = status.contains(&quot;NoActive&quot;) ? &quot;blue&quot; : &quot;green&quot;
                    def BEFORE_PORT = status.contains(&quot;NoActive&quot;) ? 8001 : 8000
                    def AFTER_PORT = status.contains(&quot;NoActive&quot;) ? 8000 : 8001

                    sh &quot;scp -i /ssh/private/.pem /var/jenkins_home/workspace/lightswitch-web-cd/prometheus.yml ubuntu@ip-172-26-5-31:/home/ubuntu/prometheus/prometheus.yml&quot;
                    sh &quot;export COMPOSE_PROFILES=$AFTER_COLOR &amp;amp;&amp;amp; docker-compose up -d --build&quot;

                    int retryCount = 0
                    while (retryCount++ &amp;lt; 5) {
                        echo &quot;===== Checking server response (${retryCount}/10) =====&quot;
                        def UP = sh(script: &quot;curl -s http://lightswitch.kr:${AFTER_PORT}/api/actuator/health || echo 'NoActive'&quot;, returnStdout: true).trim()
                        if (UP == &quot;NoActive&quot;) {
                            sleep(10)
                        } else {
                            break
                        }
                    }

                    if (retryCount == 5) {
                        error(&quot;Server startup failed&quot;)
                    }

                    sh &quot;&quot;&quot;
                        docker exec nginx /bin/bash -c &quot;cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak &amp;amp;&amp;amp; sed 's/${BEFORE_COLOR}:${BEFORE_PORT}/${AFTER_COLOR}:${AFTER_PORT}/' /etc/nginx/conf.d/default.conf.bak &amp;gt; /etc/nginx/conf.d/default.conf &amp;amp;&amp;amp; nginx -s reload&quot;

                        echo &quot;${BEFORE_COLOR} server down(port:${BEFORE_PORT})&quot;
                        docker-compose stop backend-${BEFORE_COLOR}
                    &quot;&quot;&quot;
                }
            }
            post{
                success {
                    echo 'Success Service Start'
                }
                failure {
                    error 'Fail Service Start'
                }
            }
        }
    }
   
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/92</guid>
      <comments>https://olrlobt.tistory.com/92#entry92comment</comments>
      <pubDate>Sat, 13 Jul 2024 22:11:33 +0900</pubDate>
    </item>
    <item>
      <title>[SSAFY/회고록] 싸피 10기 2학기 수료와 프로젝트 경험 회고</title>
      <link>https://olrlobt.tistory.com/91</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1719753030349&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;[회고록] 싸피9기 인터뷰 탈락 후기&quot; data-og-description=&quot;싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정해서, 탈락 후기를 쓰며 실수를 반복&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/28&quot; data-og-url=&quot;https://olrlobt.tistory.com/28&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Py636/hyWrXswYhW/HIjZ3k8WYXJTKLbMOkiHY1/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/b59GFl/hyWvUt9J1a/KN7DKKVSjqwFfaS8l9T8r0/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/0JkE7/hyWvH2EkxL/FQQtRYuz6AEPQi1xKFvsUK/img.png?width=327&amp;amp;height=425&amp;amp;face=0_0_327_425&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/28&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/28&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Py636/hyWrXswYhW/HIjZ3k8WYXJTKLbMOkiHY1/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/b59GFl/hyWvUt9J1a/KN7DKKVSjqwFfaS8l9T8r0/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/0JkE7/hyWvH2EkxL/FQQtRYuz6AEPQi1xKFvsUK/img.png?width=327&amp;amp;height=425&amp;amp;face=0_0_327_425');&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기 인터뷰 탈락 후기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정해서, 탈락 후기를 쓰며 실수를 반복&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1719752860515&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;[회고록] 싸피 10기 합격 후기&quot; data-og-description=&quot;[회고록] 싸피9기 인터뷰 탈락 후기 싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/58&quot; data-og-url=&quot;https://olrlobt.tistory.com/58&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xYoe2/hyWvXLbtpD/KSgJZigzL6RXEa5zksb1s1/img.png?width=800&amp;amp;height=1237&amp;amp;face=0_0_800_1237,https://scrap.kakaocdn.net/dn/d4PSfa/hyWrVuIhdY/jUHKvsMCzRiErSoTay43f1/img.png?width=800&amp;amp;height=1237&amp;amp;face=0_0_800_1237,https://scrap.kakaocdn.net/dn/OLTWC/hyWrYyd6GI/EgBkrRFe2KEnnrJzGtyxK1/img.png?width=1053&amp;amp;height=1713&amp;amp;face=0_0_1053_1713&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/58&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/58&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xYoe2/hyWvXLbtpD/KSgJZigzL6RXEa5zksb1s1/img.png?width=800&amp;amp;height=1237&amp;amp;face=0_0_800_1237,https://scrap.kakaocdn.net/dn/d4PSfa/hyWrVuIhdY/jUHKvsMCzRiErSoTay43f1/img.png?width=800&amp;amp;height=1237&amp;amp;face=0_0_800_1237,https://scrap.kakaocdn.net/dn/OLTWC/hyWrYyd6GI/EgBkrRFe2KEnnrJzGtyxK1/img.png?width=1053&amp;amp;height=1713&amp;amp;face=0_0_1053_1713');&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;[회고록] 싸피 10기 합격 후기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[회고록] 싸피9기 인터뷰 탈락 후기 싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1719752848322&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;[SSAFY/회고록] 싸피 10기 1학기 수료와 스터디 경험 회고&quot; data-og-description=&quot;[회고록] 싸피9기 인터뷰 탈락 후기 싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/69&quot; data-og-url=&quot;https://olrlobt.tistory.com/69&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tBy9s/hyWrOh3VS9/xkZ9fTYQjuYPG1dQfKyK5K/img.png?width=800&amp;amp;height=470&amp;amp;face=0_0_800_470,https://scrap.kakaocdn.net/dn/fgZsp/hyWvQZA45H/EWAhTgNQUwOmupwI1ixYlK/img.png?width=800&amp;amp;height=470&amp;amp;face=0_0_800_470,https://scrap.kakaocdn.net/dn/e8gSY/hyWvVT8Qxv/KjzQg4KMd8Fwsr4y5xXMHk/img.png?width=1751&amp;amp;height=1077&amp;amp;face=0_0_1751_1077&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/69&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/69&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tBy9s/hyWrOh3VS9/xkZ9fTYQjuYPG1dQfKyK5K/img.png?width=800&amp;amp;height=470&amp;amp;face=0_0_800_470,https://scrap.kakaocdn.net/dn/fgZsp/hyWvQZA45H/EWAhTgNQUwOmupwI1ixYlK/img.png?width=800&amp;amp;height=470&amp;amp;face=0_0_800_470,https://scrap.kakaocdn.net/dn/e8gSY/hyWvVT8Qxv/KjzQg4KMd8Fwsr4y5xXMHk/img.png?width=1751&amp;amp;height=1077&amp;amp;face=0_0_1751_1077');&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;[SSAFY/회고록] 싸피 10기 1학기 수료와 스터디 경험 회고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[회고록] 싸피9기 인터뷰 탈락 후기 싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;길고 길었던 삼성 청년 SW 아카데미 1년의 과정이 모두 끝이 났다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;아직 실감은 나지 않지만, 1년 동안 있었던 많은 일들을 정리해 보며 지난 1년을 돌아보려 한다.&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: #555555; text-align: start;&quot;&gt;싸피에 지원하면서 준비했던 과정과 1학기에 대한 내용이 궁금하다면 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; 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: #555555; text-align: start;&quot;&gt;굉장히 많은 일들이 있었다..&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;따라서 이번 포스팅에서는 1학기에 대한 내용보다는, 1학기가 끝나고 있었던 일들을 시간 순으로 작성해 보려 한다. 특히 진행했던 프로젝트를 위주로 작성하여, 어떤 프로젝트를 하고 어떤 경험을 할 수 있는지 작성했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;그리고 13기부터는 싸피가 마이스터고 졸업생도 대상으로 하니, 이후 입과 할 많은 사람들이 이 글을 보고 싸피에서 무엇을 하는지 간략하게나마 알았으면 좋겠다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SSAFY 2학기에는 무엇을 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5BJV5/btsIg0xEV4R/LVA9FMMkuxFBZWaCRxZ7hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5BJV5/btsIg0xEV4R/LVA9FMMkuxFBZWaCRxZ7hk/img.png&quot; data-alt=&quot;SSAFY 커리큘럼&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5BJV5/btsIg0xEV4R/LVA9FMMkuxFBZWaCRxZ7hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5BJV5%2FbtsIg0xEV4R%2FLVA9FMMkuxFBZWaCRxZ7hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1832&quot; height=&quot;625&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;625&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSAFY 커리큘럼&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY 2학기 과정은 5개월의 심화과정과 1개월의 Job Fair 과정으로 이루어져 있다. 이 과정은 1학기 동안 쌓아온 개발역량과 1학기 마지막 프로젝트 경험을 바탕으로 진행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kZNUr/btsIiSR8M4C/MN9Vak5e7EVb0MYs94sOjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kZNUr/btsIiSR8M4C/MN9Vak5e7EVb0MYs94sOjk/img.png&quot; data-alt=&quot;SSAFY 2학기 심화과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kZNUr/btsIiSR8M4C/MN9Vak5e7EVb0MYs94sOjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkZNUr%2FbtsIiSR8M4C%2FMN9Vak5e7EVb0MYs94sOjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1834&quot; height=&quot;618&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSAFY 2학기 심화과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1학기 JobFair&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1학기 과정을 마치고 나면 JobFair 기간을 1개월 동안 갖는다. 이 기간에는 취업특강, 채용박람회, 채용면접, 채용상담 등 여러 가지 취업 관련한 활동들을 SSAFY에서 지원해 주고, 계절학기 느낌으로 여러 온라인 강의도 들을 수 있다. 대부분 이 시기에 2학기 프로젝트를 준비하는 교육생들이 많으며, 1학기에는 배우지 않는 Spring JPA, QueryDSL, 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;이름은 싸피레이스로, 차량 운행 시뮬레이터를 기반으로 하는 경주 대결이며 JobFair 한 달의 기간 동안 진행되어 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학기를 준비할 시간이 없다는 것이다. 그만큼 어렵고.. 시간이 많이 든다.. 1학기때 진행한 당구 게이미피케이션인 일타싸피와는 규모가 다른 수준이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif-converter.gif&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5vOGc/btsIipioK4R/Ogc1QE5MTCZw5Enk10wPU1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5vOGc/btsIipioK4R/Ogc1QE5MTCZw5Enk10wPU1/img.gif&quot; data-alt=&quot;SSAFY 레이스 진행 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5vOGc/btsIipioK4R/Ogc1QE5MTCZw5Enk10wPU1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b5vOGc/btsIipioK4R/Ogc1QE5MTCZw5Enk10wPU1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;320&quot; data-filename=&quot;ezgif.com-video-to-gif-converter.gif&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSAFY 레이스 진행 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 1학기때부터 항상 하던 말이 있었는데, 바로 &quot;&lt;b&gt;난 싸피레이스 하려고 싸피 왔어&quot;였다.&lt;/b&gt; 1학기 스타트 캠프 과정에서 9기 교육생들의 싸피레이스 경진대회 모습을 보고 너무 재밌어 보여서 한 말이었는데, 뭔가 밈처럼 계속 말했달까..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 보니 싸피레이스에 진심으로 참여하게 되었고, 개인적인 공부는 미루어 두게 되었다. 그랬으면 안 됐는데  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YviDN/btsIiaFMXpB/qdW4OdEgRuLGWC6PU2EAS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YviDN/btsIiaFMXpB/qdW4OdEgRuLGWC6PU2EAS1/img.png&quot; data-alt=&quot;베이직 맵 1등, 스피드 맵 2등&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YviDN/btsIiaFMXpB/qdW4OdEgRuLGWC6PU2EAS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYviDN%2FbtsIiaFMXpB%2FqdW4OdEgRuLGWC6PU2EAS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;300&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;베이직 맵 1등, 스피드 맵 2등&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로는 베이직 맵 1등과, 스피드 맵 2등을 달성하긴 했지만 코드가 완벽하지는 못해서 최종 싸피 맵에는 진출하지 못했다. 그래도 나름 열심히 했고, 좋은 성과를 거두어서 재밌는 추억으로 남을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 우리 반은 이 JobFair 기간을 활용하여 놀다 오기도 하였다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY가 가장 좋은 점이 같은 목표를 갖는 다양한 친구들을 만날 수 있다는 점인데, 더 친해지고 더 가까워질 수 있는 기회였다. 나는 이 여행에서 레크리에이션도 준비해 가서 정말 많은 추억을 쌓고 올 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1503&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JFux7/btsIirHhiql/uKlhLFmizMcDRDNkpskHPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JFux7/btsIirHhiql/uKlhLFmizMcDRDNkpskHPK/img.png&quot; data-alt=&quot;SSAFY 17반 최고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JFux7/btsIirHhiql/uKlhLFmizMcDRDNkpskHPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJFux7%2FbtsIirHhiql%2FuKlhLFmizMcDRDNkpskHPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;700&quot; data-origin-width=&quot;1503&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSAFY 17반 최고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 보니, JobFair 기간 동안 놀기만 한 거 아니냐 생각할 수 있는데, 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학기를 앞두고 있다면 이 기간을 잘 활용해서 2학기 준비를 잘하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 이렇게 보이는 것 말고도, 개인적으로 강의도 듣고 할 건 다 하면서 놀았으니..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2학기 공통 프로젝트&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2학기가 시작하면 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;그리고, 싸피에서 사용하는 MatterMost를 활용해서 팀 빌딩을 한다. 이때, 처음 보는 사람들한테 자신을 소개해야 하다 보니 장표를 만들어 간단하게 자신을 소개하는 시간을 갖는다. 자신이 갖고 있는 역량과 기술들을 나열하고, 1분 내외로 자신을 소개하며 팀을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 아래와 같이 나를 소개하는 장표를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFEIZy/btsIg1XBabd/HQ2SpKcaH1dAHq7kFdZ4w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFEIZy/btsIg1XBabd/HQ2SpKcaH1dAHq7kFdZ4w1/img.png&quot; data-alt=&quot;내 자기소개 장표&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFEIZy/btsIg1XBabd/HQ2SpKcaH1dAHq7kFdZ4w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFEIZy%2FbtsIg1XBabd%2FHQ2SpKcaH1dAHq7kFdZ4w1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내 자기소개 장표&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그를 하다 보니 지금 블로그처럼 장표를 꾸며서 나를 소개했다. 그리고 SSAFY에서 얻은 성과들도 나열하여 내가 어떤 사람인지를 나타내려 했다. 하지만, 아무리 나를 잘 표현하고 나타내려 해도 나는 백엔드 개발자다. 싸피의 과정이 임베디드와 Python, Java로 진행되다 보니 아무래도 사람들은 프런트를 구하려 하고, 프런트를 구하는 것은 하늘의 별따기와 같았다. 대부분은 &lt;span style=&quot;color: #333333; text-align: start;&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;나는 다행히도 한 팀에게 연락이 왔었는데, 이미 1학기에 올라온 친구와 같이 하기로 한 상황이라 FE, BE 비율이 맞지 않았다. 아쉽지만, 이 팀과는 같이 하지 못했고, 친구와 함께 프런트 개발자로 자기소개를 올린 교육생에게 개인적인 연락을 돌려 팀 구성을 완료했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;로고gif.gif&quot; data-origin-width=&quot;217&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpzlhg/btsIjdV9Edg/9YHcyugExo3VXQNBLU7CV0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpzlhg/btsIjdV9Edg/9YHcyugExo3VXQNBLU7CV0/img.gif&quot; data-alt=&quot;공통 프로젝트 로고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpzlhg/btsIjdV9Edg/9YHcyugExo3VXQNBLU7CV0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bpzlhg/btsIjdV9Edg/9YHcyugExo3VXQNBLU7CV0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;217&quot; height=&quot;182&quot; data-filename=&quot;로고gif.gif&quot; data-origin-width=&quot;217&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공통 프로젝트 로고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공통 프로젝트&lt;/b&gt;는 &lt;b&gt;비전공자/전공자가 한 팀으로 구성되어 웹 기반의 기술을 공통적으로 학습하여 원하는 웹 서비스를 구현하는 프로젝트&lt;/b&gt;이다. 트랙으로는 WebRTC를 쓰는 웹 기술 트랙, SNS 기반의 웹 디자인 트랙, 그리고 IOT 트랙이 있었다. 우리 팀은 이 중 SNS 기반의 웹 디자인 트랙을 선택했다. 하지만, 트랙 자체는 프로젝트의 큰 틀만 정해줄 뿐, 굳이 따르지 않아도 되는 경우가 많다. 따라서 우리 팀은 거의 자율 주제로 진행을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하면서 총 7주의 시간이 주어지는데, 이 기간 안에 기획과 설계, 개발, 배포까지 모든 것을 해내야 한다. 우리 팀은 이 중에서 기획 단계에서 엄청 힘들었는데, 원하는 주제를 맞추기도, 적절한 규모로 조절하기도 생각보다 쉽지 않았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;915&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZsUxu/btsIigTvtjm/ygFTi8PUfnoYxfR90bvBL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZsUxu/btsIigTvtjm/ygFTi8PUfnoYxfR90bvBL1/img.png&quot; data-alt=&quot;공통 프로젝트 아이데이션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZsUxu/btsIigTvtjm/ygFTi8PUfnoYxfR90bvBL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZsUxu%2FbtsIigTvtjm%2FygFTi8PUfnoYxfR90bvBL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;915&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;915&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공통 프로젝트 아이데이션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행된 아이데이션 횟수만 봐도... 지금 봐도 내용들이 상당하다. 우리 팀은 기획에만 거의 2주라는 시간을 써 가며 기획에 매진했다. 그리고 결국 나온 것이 &quot;&lt;b&gt;초등학생들을 위한 금융 교육 시스템 BID&quot;였다.&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1014&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2jMAx/btsIiOI4Dr5/kZEZxK32ru1FomPRoLJuD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2jMAx/btsIiOI4Dr5/kZEZxK32ru1FomPRoLJuD1/img.png&quot; data-alt=&quot;탕후루는 비싸다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2jMAx/btsIiOI4Dr5/kZEZxK32ru1FomPRoLJuD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2jMAx%2FbtsIiOI4Dr5%2FkZEZxK32ru1FomPRoLJuD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;1014&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1014&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;탕후루는 비싸다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 대해서 간략하게 설명하자면, 요즘 초등학생들 사이에서는 &quot;마라탕후루&quot;라는 유행어가 있다고 한다. 글을 쓰는 지금에서야 &quot;선배! 마라탕 사주세요!&quot;라는 마라탕후루 챌린지가 유행이지만, 프로젝트를 진행할 당시에는 그런 건 없었다. 마라탕도 값이 꽤 나가고, 탕후루도 포도 한 알에 600원이면 값이 많이 나가는데, 초등학생들은 이 가격이 어느 정도인지 알고 사 먹는 걸까? 하는 취지에서 시작한 프로젝트다.&lt;/p&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과 React를 사용한 웹 서비스이고, &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;기존 학급에서 사용되던 학급 쿠폰을 학생들이 게임 화폐로 직접 구입하며 자연스럽게 소비 습관을 기를 수 있는 게이미피케이션 형식의 서비스이다. 비밀 경매 방식으로 학급 쿠폰을 사는 방식이며, 비밀 경매이기 때문에 학생들이 스스로 물건의 가치를 생각해 볼 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zt1fc/btsIhQVaJVw/4qblJqcskDlqVfWtbGiJV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zt1fc/btsIhQVaJVw/4qblJqcskDlqVfWtbGiJV1/img.png&quot; data-alt=&quot;기능 명세서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zt1fc/btsIhQVaJVw/4qblJqcskDlqVfWtbGiJV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZt1fc%2FbtsIhQVaJVw%2F4qblJqcskDlqVfWtbGiJV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;416&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기능 명세서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제가 정해지고 우리는 빠른 개발에 들어갔지만, 위와 같은 기능명세서가 5장이 더 나왔다... 모두가 같은 목표로 프로젝트를 완성도 있게 완벽하게 만들고자 하니 규모가 엄청 커지게 되었다. 우리는 짧은 시간 안에 이 것을 다 개발할 수 있을까 생각도 했지만, SSAFY에서의 처음 진행하는 프로젝트라 다들 엄청 몰두해서 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3079&quot; data-origin-height=&quot;1465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjqbYB/btsIjiiNO8l/VgDQZbdko89EKiSwRKNanK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjqbYB/btsIjiiNO8l/VgDQZbdko89EKiSwRKNanK/img.png&quot; data-alt=&quot;API 명세서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjqbYB/btsIjiiNO8l/VgDQZbdko89EKiSwRKNanK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjqbYB%2FbtsIjiiNO8l%2FVgDQZbdko89EKiSwRKNanK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3079&quot; height=&quot;1465&quot; data-origin-width=&quot;3079&quot; data-origin-height=&quot;1465&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;API 명세서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 내가 맡은 API 명세 부분이다. 심지어 나는 인프라도 하고 있었다. 인프라를 진행하며 Docker와 Jenkins를 활용해 Spring 멀티모듈로 된 프로젝트를 배포하고, React 배포도 하고, Nginx로 SSL/TLS 인증과 리버스 프락시도 진행했다. 이 모든 과정은 내 포스팅에 잘 작성되어 있으니, 궁금한 분들은 포스팅을 참고하기 바란다...&lt;/p&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를 만들다 보니, 하루에 남아나는 시간이 없었다. 밤이 지나가는지도 모르고 코딩을 했고, 마지막 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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;관리자메인.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AoMHS/btsIjxtkeSM/D4OOrDSmtvWzWk0InCtNH1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AoMHS/btsIjxtkeSM/D4OOrDSmtvWzWk0InCtNH1/img.gif&quot; data-alt=&quot;선생님이 보는 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AoMHS/btsIjxtkeSM/D4OOrDSmtvWzWk0InCtNH1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/AoMHS/btsIjxtkeSM/D4OOrDSmtvWzWk0InCtNH1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;395&quot; data-filename=&quot;관리자메인.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;선생님이 보는 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 위와 같은 결과물이 완성되었다. 하지만, 성공적으로 프로젝트를 마무리하지는 못하였다. 너무나 많은 기능들을 넣고, 너무나 시간이 지체되는 바람에, 발표 당일까지도 우리 팀은 열심히 코딩을 해야만 했으니까..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발표 당일에는 심지어 로컬 환경에서 정상적으로 돌아가던 프로젝트가 배포환경에서 돌아가지 않는 문제가 발생했다. 후에 알고 나니, Cookie 설정에서 HttpOnly를 걸어두었는데, 우리 팀의 코드가 이 쿠키에서 정보를 꺼내오는 부분이 있었다. 로컬 환경에서는 꺼내지던 정보가, 배포 환경에서는 HttpOnly 설정 때문에 꺼낼 수 없는 환경이 되어버린 것이다. 문제의 원인도 몰랐던 우리 팀은 열심히 디버깅을 해보았지만, 결국 정상적으로 작동이 안 돼서, 로컬 환경으로 발표를 진행했다. 싸피에서의 내 첫 프로젝트는 이렇게 끝이 났고, 힘들었던 만큼 우리 팀은 더 돈독해졌다.&lt;/p&gt;
&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;figure id=&quot;og_1719763699110&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 - olrlobt/BID: SSAFY 공통 프로젝트 : 초등학생을 위한 비밀 경매 기반 금융 교육 시스템&quot; data-og-description=&quot;SSAFY 공통 프로젝트 : 초등학생을 위한 비밀 경매 기반 금융 교육 시스템. Contribute to olrlobt/BID development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/olrlobt/BID?tab=readme-ov-file&quot; data-og-url=&quot;https://github.com/olrlobt/BID&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zt1KC/hyWrWtFbUs/bvEW7CvLQ1R04Hj2OyWl91/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236&quot;&gt;&lt;a href=&quot;https://github.com/olrlobt/BID?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/olrlobt/BID?tab=readme-ov-file&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zt1KC/hyWrWtFbUs/bvEW7CvLQ1R04Hj2OyWl91/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236');&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 - olrlobt/BID: SSAFY 공통 프로젝트 : 초등학생을 위한 비밀 경매 기반 금융 교육 시스템&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSAFY 공통 프로젝트 : 초등학생을 위한 비밀 경매 기반 금융 교육 시스템. Contribute to olrlobt/BID development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2학기 특화 프로젝트&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특화 프로젝트&lt;/b&gt;는&lt;b&gt; 4차 산업 혁명 분야 중 본인이 흥미 있는 특화 기술을 익히고 신기술 프로젝트를 진행한다&lt;/b&gt;. 도메인으로는 크게 AI, 빅데이터, 블록체인, 핀테크, 메타버스 등이 있었으며, 해당 도메인 안에서도 세분화되어 나누어진다. 나는 이 중 빅데이터 추천 도메인으로 진행을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특화 프로젝트는 공통 프로젝트와는 다르게, 팀 빌딩을 알아서 진행한다. 하지만 몇 가지 제약이 존재하는데, 팀에 비전공/전공자가 한 명 이상 포함되어 있어야 하고, 공통 팀 모두가 같이 올라갈 수는 없었다. 나는 싸피 2학기에 오면서 많은 다양한 사람들과 프로젝트를 경험해보고자 하는 생각이 있었어서, 1학기 반 친구들도 아닌, 아주 모르는 사람들과 프로젝트를 진행했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 아는 사람들과 같이 하고 싶었던 마음도 있었지만, 특화 프로젝트부터는 기업연계 프로젝트가 함께 진행된다. 성적과 종합평가 우수자들을 선정하여, 삼성을 포함한 다양한 기업과 연계해서 진행하게 되는데, 이번 싸피 10기에서는 삼성 DA사업부와 기업연계 프로젝트를 진행한 것으로 알고 있다. 이 기업연계 프로젝트의 대상자 발표가 상당히 늦었고, 나는 생각보다 해 놓은 게 많았어서 내심 기대를 하고 있어 같은 반 친구들과 진행하지 못했다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같이 하자 했는데, 쏙 빠져나가면 이런 민폐가 또 있을까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 기업연계는 안 됐고, 특화 팀을 잘 만들어서 빅데이터 추천 도메인으로 프로젝트를 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;타로카드추천.jpeg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddTp13/btsIjegp0U9/oGjFhxtAHoWpQuaTx7BpIk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddTp13/btsIjegp0U9/oGjFhxtAHoWpQuaTx7BpIk/img.jpg&quot; data-alt=&quot;일기에서 타로카드 추천&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddTp13/btsIjegp0U9/oGjFhxtAHoWpQuaTx7BpIk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddTp13%2FbtsIjegp0U9%2FoGjFhxtAHoWpQuaTx7BpIk%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;707&quot; height=&quot;398&quot; data-filename=&quot;타로카드추천.jpeg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일기에서 타로카드 추천&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빅데이터와 추천 시스템에 대해 아무것도 아는 게 없었던 나는, 프로젝트 진행에 있어서 상당히 곤란했다. 프로젝트 주제를 정하기부터 만만치 않았는데, 어떤 기술을 써야 할지, 구현이 되는 기능인지 아무것도 알 수 없었다. 일단은 도전해 보기엔 사실 7주라는 기간은 너무나도 짧다. 따라서, 여러 레퍼런스들을 참고해서 아이디어를 도출해 내었고, 이를 통해 &quot;&lt;b&gt;타로카드 기반 감성 다이어리&lt;/b&gt;&quot;가 주제로 선정되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;일기추천.jpeg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxGtKp/btsIhckg9hT/Q7kOBSanuckif52fwYZ3pK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxGtKp/btsIhckg9hT/Q7kOBSanuckif52fwYZ3pK/img.jpg&quot; data-alt=&quot;일기에서 다른 일기 추천&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxGtKp/btsIhckg9hT/Q7kOBSanuckif52fwYZ3pK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxGtKp%2FbtsIhckg9hT%2FQ7kOBSanuckif52fwYZ3pK%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;707&quot; height=&quot;398&quot; data-filename=&quot;일기추천.jpeg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일기에서 다른 일기 추천&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제를 선정하고, 설계를 하면서도 상당히 당황스러웠다. 여러 추천 알고리즘에 무슨 차이가 있는 지도 공부해야 했고, Python 도 잘 모르는 내가 이 것을 구현할 수 있을지.. 상당히 막막했다. 그래도 좋은 팀원들을 만난 덕에, 나름대로 수월하게 진행되었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;767&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLAVty/btsIgL1DewE/jjYbBf9LLaInEemJmZl0p1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLAVty/btsIgL1DewE/jjYbBf9LLaInEemJmZl0p1/img.png&quot; data-alt=&quot;특화 프로젝트 ERD&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLAVty/btsIgL1DewE/jjYbBf9LLaInEemJmZl0p1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLAVty%2FbtsIgL1DewE%2FjjYbBf9LLaInEemJmZl0p1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;767&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;767&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;특화 프로젝트 ERD&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트는 공통에 비하면, 정말 초라할 정도의 설계 산출물들이 나왔는데, ERD는 테이블이 4개뿐이다. 이후에 추가되긴 했지만 그래도 적은 규모였으며, 기능명세서와 API 명세서는 프로젝트 전체가 공통 프로젝트에서 내가 맡은 부분보다 작았다. 상당히 당황스러웠지만, 많은 로직들이 임베딩 모델을 처리하고 학습시키며, ANNOY 추천 알고리즘을 사용하는 과정으로 빠져 있었기 때문에 그러려니 하고 넘겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 프로젝트 진행 과정은 상당히 수월했고, 여유가 넘쳤다. 코치님들과 컨설턴트님들이 하시는 말씀이. 공통 프로젝트에서 다들 힘을 무척이나 빼고 와서, 특화 프로젝트에서는 다들 여유롭게 진행한다고들 한다. 우리 팀도 역시 그런 분위기였고, 또한 프로젝트가 진행되는 기간이 공채 기간과 겹치다 보니 다들 프로젝트에 크게 신경 쓰지 않는 분위기였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;사연추천.gif&quot; data-origin-width=&quot;389&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m5eDA/btsIiSq7kJC/ptYARR61olkKuMonHkyEfk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m5eDA/btsIiSq7kJC/ptYARR61olkKuMonHkyEfk/img.gif&quot; data-alt=&quot;타로카드를 추천받고, 다른일기를 추천 받는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m5eDA/btsIiSq7kJC/ptYARR61olkKuMonHkyEfk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/m5eDA/btsIiSq7kJC/ptYARR61olkKuMonHkyEfk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;216&quot; height=&quot;468&quot; data-filename=&quot;사연추천.gif&quot; data-origin-width=&quot;389&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;타로카드를 추천받고, 다른일기를 추천 받는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제는 상당히 괜찮아서, 조금 더 열심히 했다면 수상까지 노려볼 수 있었겠다는 생각이 들어 지금 생각해도 아쉽다. 하지만, 다들 취업이 우선인 것이 당연한 것이고, 나는 개인적으로 많은 것을 했다고 생각해서 후회는 되지 않는다. 이 프로젝트에 사용한 추천 알고리즘인 ANNOY 알고리즘에 대해서는 아래 포스팅을 통해 자세히 설명을 해 놓았다. 알고리즘 자체가 상당히 재밌어서, 추천시스템을 사용한다면 참고해 보면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1719766199732&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;[추천 알고리즘] Spotify ANNOY (Approximate Nearest Neighbors Oh Yeah) 알고리즘이란?&quot; data-og-description=&quot;추천 알고리즘 이번에 진행한 프로젝트의 도메인은 '빅데이터 추천'이었다. 나는 추천 시스템에 관한 지식이 전혀 없는 상태였기에 여러 가지 학습한 내용을 정리하고, 내가 사용한 추천 알고리&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/87&quot; data-og-url=&quot;https://olrlobt.tistory.com/87&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbDLgJ/hyWvLjHgVo/kltzULwI2p0yEkKAw6M7W0/img.png?width=750&amp;amp;height=750&amp;amp;face=0_0_750_750,https://scrap.kakaocdn.net/dn/d2aZZy/hyWrPnK02K/o5m4DYIxtJSx5Hq51Ta5CK/img.png?width=750&amp;amp;height=750&amp;amp;face=0_0_750_750,https://scrap.kakaocdn.net/dn/ckPsZW/hyWrUij8ts/NQdF6ac8yWIG28XkHtcgR0/img.png?width=4084&amp;amp;height=1550&amp;amp;face=0_0_4084_1550&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/87&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/87&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbDLgJ/hyWvLjHgVo/kltzULwI2p0yEkKAw6M7W0/img.png?width=750&amp;amp;height=750&amp;amp;face=0_0_750_750,https://scrap.kakaocdn.net/dn/d2aZZy/hyWrPnK02K/o5m4DYIxtJSx5Hq51Ta5CK/img.png?width=750&amp;amp;height=750&amp;amp;face=0_0_750_750,https://scrap.kakaocdn.net/dn/ckPsZW/hyWrUij8ts/NQdF6ac8yWIG28XkHtcgR0/img.png?width=4084&amp;amp;height=1550&amp;amp;face=0_0_4084_1550');&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;[추천 알고리즘] Spotify ANNOY (Approximate Nearest Neighbors Oh Yeah) 알고리즘이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;추천 알고리즘 이번에 진행한 프로젝트의 도메인은 '빅데이터 추천'이었다. 나는 추천 시스템에 관한 지식이 전혀 없는 상태였기에 여러 가지 학습한 내용을 정리하고, 내가 사용한 추천 알고리&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;figure id=&quot;og_1719766609483&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 - olrlobt/Letter-for-me: SSAFY 특화 프로젝트 : 당신의 하루를 위로하는 타로카드 기반 다이어리&quot; data-og-description=&quot;SSAFY 특화 프로젝트 : 당신의 하루를 위로하는 타로카드 기반 다이어리. Contribute to olrlobt/Letter-for-me development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/olrlobt/Letter-for-me&quot; data-og-url=&quot;https://github.com/olrlobt/Letter-for-me&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bMu2fi/hyWrPBkjqo/paJ7Rhd3kNSLf6lACCuG3k/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236&quot;&gt;&lt;a href=&quot;https://github.com/olrlobt/Letter-for-me&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/olrlobt/Letter-for-me&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bMu2fi/hyWrPBkjqo/paJ7Rhd3kNSLf6lACCuG3k/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236');&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 - olrlobt/Letter-for-me: SSAFY 특화 프로젝트 : 당신의 하루를 위로하는 타로카드 기반 다이어리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSAFY 특화 프로젝트 : 당신의 하루를 위로하는 타로카드 기반 다이어리. Contribute to olrlobt/Letter-for-me development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개인 프로젝트&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 특화 프로젝트를 진행하며, 개인 프로젝트를 같이 진행했다. 특화 프로젝트는 앞선 언급처럼 규모가 작았고, 공통 프로젝트에서 밤새 코딩했던 것처럼 할 필요도 없었고, 시간도 남았고.. 무엇보다 해보고 싶은 게 있어서 개인 프로젝트를 함께 진행했다. 그렇다고 특화 프로젝트에 소홀히 하지 않았다. 내가 맡은 역할을 넘어서 다른 부분도 참여했으니.. 예를 들어, 추천 시스템 이외에도 API도 만들고, Fast 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;그래서 개인 프로젝트로 뭘 했는지 설명하자면, &quot;&lt;b&gt;깃허브 리드미에 블로그를 효과적으로 노출시키기&lt;/b&gt;&quot; 프로젝트이다. 조금 간단히 말하자면 &quot;&lt;b&gt;블로그 위젯&lt;/b&gt;&quot;이라고 할 수 있다. 이 프로젝트를 진행한 이유는 간단하다. 나를 포함한 많은 개발자들이 개인 기술 블로그를 운영하고 있고, 이를 깃허브 리드미에 걸어두곤 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 문제는, 잘 안 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/54Rek/btsIhVa2Y4y/3M5eKcEIocXUAeFzQusmGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/54Rek/btsIhVa2Y4y/3M5eKcEIocXUAeFzQusmGk/img.png&quot; data-alt=&quot;숨은 블로그 찾기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/54Rek/btsIhVa2Y4y/3M5eKcEIocXUAeFzQusmGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F54Rek%2FbtsIhVa2Y4y%2F3M5eKcEIocXUAeFzQusmGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;729&quot; height=&quot;285&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;숨은 블로그 찾기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 원래는 맨 왼쪽 사진과 같은 형태였다. 안 보이지 않은가? 클릭할 생각조차 들지 않는다. 그래서 나는 이 프로젝트를 계획했고, 어떤 식으로 구현을 하면 좋을지 많은 고민을 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 문득 생각난 것이 깃허브에 있는 수많은 위젯들이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드 (1).png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/967KF/btsIhvcPLm5/rWjnIk6dzTJIfBgFmyvxo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/967KF/btsIhvcPLm5/rWjnIk6dzTJIfBgFmyvxo1/img.png&quot; data-alt=&quot;수 많은 깃허브 위젯들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/967KF/btsIhvcPLm5/rWjnIk6dzTJIfBgFmyvxo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F967KF%2FbtsIhvcPLm5%2FrWjnIk6dzTJIfBgFmyvxo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;213&quot; data-filename=&quot;다운로드 (1).png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;213&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수 많은 깃허브 위젯들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얘네들은 어떻게 만들어진 걸까...? 갑자기 궁금해졌고, 다짜고짜 깃허브를 뒤져가며 어떤 식으로 만들어지고 있는지 연구했다. 구조는 생각보다 단순했는데, Markdown 환경에서 API를 호출하여 이미지를 호스팅 해주는 형식이었다. 그래서 나는 이 정보를 기반으로 간단히 Markdown 환경에 블로그를 크롤링해 와서 이미지를 호스팅 해주는 프로젝트를 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;1801&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R7DXe/btsIgZ6FAuI/gl3iJ3bXQK6vTKEXVNb6e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R7DXe/btsIgZ6FAuI/gl3iJ3bXQK6vTKEXVNb6e0/img.png&quot; data-alt=&quot;깃허브 리드미 : 블로그 위젯 사용 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R7DXe/btsIgZ6FAuI/gl3iJ3bXQK6vTKEXVNb6e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR7DXe%2FbtsIgZ6FAuI%2Fgl3iJ3bXQK6vTKEXVNb6e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;603&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;1801&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;깃허브 리드미 : 블로그 위젯 사용 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤가? 잘 보이지 않나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 느끼기엔 클릭해보고 싶은 생각도 든다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1534&quot; data-origin-height=&quot;1211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wAAQy/btsIjLkLgF6/CoeGDz266QdmCEzfvLGD50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wAAQy/btsIjLkLgF6/CoeGDz266QdmCEzfvLGD50/img.png&quot; data-alt=&quot;SSAFY 관통 프로젝트 README.md에 적용한 개인 프로젝트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wAAQy/btsIjLkLgF6/CoeGDz266QdmCEzfvLGD50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwAAQy%2FbtsIjLkLgF6%2FCoeGDz266QdmCEzfvLGD50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;1211&quot; data-origin-width=&quot;1534&quot; data-origin-height=&quot;1211&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSAFY 관통 프로젝트 README.md에 적용한 개인 프로젝트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위처럼 활용도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하고, 진행한 내용들을 포스팅을 작성을 해 두었다면, 프로젝트 README에 효과적으로 내 포스팅을 노출할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (14).png&quot; data-origin-width=&quot;1449&quot; data-origin-height=&quot;1240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFxSU7/btsIhf2c9kp/XNKJ5mMcyZQUBgfNgSf2P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFxSU7/btsIhf2c9kp/XNKJ5mMcyZQUBgfNgSf2P1/img.png&quot; data-alt=&quot;SSAFY MatterMost 개인프로젝트 활용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFxSU7/btsIhf2c9kp/XNKJ5mMcyZQUBgfNgSf2P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFxSU7%2FbtsIhf2c9kp%2FXNKJ5mMcyZQUBgfNgSf2P1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;574&quot; height=&quot;1240&quot; data-filename=&quot;Untitled (14).png&quot; data-origin-width=&quot;1449&quot; data-origin-height=&quot;1240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSAFY MatterMost 개인프로젝트 활용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 중요한 건, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Markdown을 지원하는 거의 모든 환경에서 사용이 가능하다는 것이다. 위처럼 SSAFY에서 사용하는 MatterMost에서도 링크를 공유할 일이 많은데, 이럴 때 위 사진처럼 활용하면 생각보다 용이하다.&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;&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1719768137361&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;많은 개발자들의 고민 나는 깃허브 리드미에 내 블로그를 홍보하기 위해 링크를 해 놓곤 한다. 하지만, 이 전의 내 깃허브 리드미에는 아래와 같이 a 태그를 이용한 조촐한 이미지 링크만 띄워 &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/84&quot; data-og-url=&quot;https://olrlobt.tistory.com/84&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qFTz9/hyWrTjqDeB/MTAYK63sN51cSeu1Kxvym1/img.png?width=800&amp;amp;height=832&amp;amp;face=0_0_800_832,https://scrap.kakaocdn.net/dn/jif9i/hyWrQUweC4/S3nBxDd9LQU4Q1LYpeny7k/img.png?width=800&amp;amp;height=832&amp;amp;face=0_0_800_832,https://scrap.kakaocdn.net/dn/bcn4MN/hyWrOI9M9c/h3NMK2vqQyAofLbTzBaul0/img.png?width=1862&amp;amp;height=415&amp;amp;face=0_0_1862_415&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qFTz9/hyWrTjqDeB/MTAYK63sN51cSeu1Kxvym1/img.png?width=800&amp;amp;height=832&amp;amp;face=0_0_800_832,https://scrap.kakaocdn.net/dn/jif9i/hyWrQUweC4/S3nBxDd9LQU4Q1LYpeny7k/img.png?width=800&amp;amp;height=832&amp;amp;face=0_0_800_832,https://scrap.kakaocdn.net/dn/bcn4MN/hyWrOI9M9c/h3NMK2vqQyAofLbTzBaul0/img.png?width=1862&amp;amp;height=415&amp;amp;face=0_0_1862_415');&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;많은 개발자들의 고민 나는 깃허브 리드미에 내 블로그를 홍보하기 위해 링크를 해 놓곤 한다. 하지만, 이 전의 내 깃허브 리드미에는 아래와 같이 a 태그를 이용한 조촐한 이미지 링크만 띄워&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1719768177723&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 - olrlobt/github-tistory-posting: Github Readme에 블로그 포스팅을 표기하는 서비스&quot; data-og-description=&quot;Github Readme에 블로그 포스팅을 표기하는 서비스. Contribute to olrlobt/github-tistory-posting development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/olrlobt/github-tistory-posting&quot; data-og-url=&quot;https://github.com/olrlobt/github-tistory-posting&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bf2BfO/hyWrYSyXfU/d3rjWoehlGK2FmlZ56JK7k/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236&quot;&gt;&lt;a href=&quot;https://github.com/olrlobt/github-tistory-posting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/olrlobt/github-tistory-posting&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bf2BfO/hyWrYSyXfU/d3rjWoehlGK2FmlZ56JK7k/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236');&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 - olrlobt/github-tistory-posting: Github Readme에 블로그 포스팅을 표기하는 서비스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Github Readme에 블로그 포스팅을 표기하는 서비스. Contribute to olrlobt/github-tistory-posting development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2학기 자율 프로젝트&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 싸피 2학기 마지막 과정인 자율 프로젝트이다. &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;기업연계 프로젝트에 참여하는 기업은 삼성 전자, 삼성 SDS, AWS korea, Yes24, 포스코 등 다양한 기업들이 참여하고, 기업의 요구 명세서에 따라 프로젝트를 진행하게 된다.&lt;/p&gt;
&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;letter-spacing: 0px;&quot;&gt;우리 팀은 운이 좋게 기업연계 프로젝트를 할 수 있었고, 한국의 헬스케어 기업인 루닛(Lunit) 기업과 연계 프로젝트를 진행하였다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 루닛 기업을 선택한 이유는, 우리 팀원들이 해당 기업에 관심이 많았고, 프로젝트 주제가 가장 신박하고 재밌어서 선택하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (15).png&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Cfe9/btsIgYUa5xL/Sdw1f0E0suTnd4Ty7Kiw51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Cfe9/btsIgYUa5xL/Sdw1f0E0suTnd4Ty7Kiw51/img.png&quot; data-alt=&quot;기업연계 프로젝트 로고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Cfe9/btsIgYUa5xL/Sdw1f0E0suTnd4Ty7Kiw51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Cfe9%2FbtsIgYUa5xL%2FSdw1f0E0suTnd4Ty7Kiw51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;180&quot; data-filename=&quot;Untitled (15).png&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기업연계 프로젝트 로고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 프로젝트의 주제는 &quot;&lt;b&gt;피처 플래깅(Feature Flagging) 솔루션 개발&lt;/b&gt;&quot;이었다. 피처플래깅이 뭔데? 검색을 해도 나오는 게 맞는 건지 알 수 없었고, 추측만 무성할 뿐이었다. 지금에서야 정확히 뭔지 알고 말하지만, 당시에는 멘토님과 팀미팅을 진행하기 전까지 우리가 생각하는 게 맞겠지?라는 생각뿐이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;피처 플래깅&lt;/b&gt;은 &lt;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;글로만 설명하니 어려운데, 간단하게 if 조건문이 있다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1719769369313&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(유저 성이 LEE 라면){
	새로운 기능;
}&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;이런 조건 문 설정이 가능하고, 이 기능을 배포 환경에서 &quot;LEE&quot;가 아닌, &quot;CHOI&quot;로 바꾸는 것 또한 가능하다. 재배포 없이 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 주제 자체도 너무 신박하고 재밌었으며, 요구되는 산출물 또한 말이 안 되게 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;산출물로는 Web, IOS, Android 대시보드가 요구되었고, Java, Python, JavaScript 세 가지 언어를 지원해야 했다. 세 가지 언어를 지원해야 된다는 말은 Java, Python, JavaScript 세 가지 언어로 된 SDK를 만들어야 한다는 것이다. 생각만 해도 할 것들이 너무 많아 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이 프로젝트에서 JAVA SDK 개발 역할을 맡아 수행했다. SDK를 어떻게 만드는지 알 수 없었을뿐더러, 이 SDK가 Android에서도 사용될 수 있도록 Spring으로 개발을 해서는 안 됐다. 또한, 이 SDK를 Maven 저장소에 배포하여, 사용자 접근성 또한 높여야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (16).png&quot; data-origin-width=&quot;1702&quot; data-origin-height=&quot;1041&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXRZ30/btsIhTRQBWs/i52CHfie4JKKCm6vC0uIaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXRZ30/btsIhTRQBWs/i52CHfie4JKKCm6vC0uIaK/img.png&quot; data-alt=&quot;Maven Central Repository Sonatype 배포&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXRZ30/btsIhTRQBWs/i52CHfie4JKKCm6vC0uIaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXRZ30%2FbtsIhTRQBWs%2Fi52CHfie4JKKCm6vC0uIaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;611&quot; height=&quot;1041&quot; data-filename=&quot;Untitled (16).png&quot; data-origin-width=&quot;1702&quot; data-origin-height=&quot;1041&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Maven Central Repository Sonatype 배포&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수많은 과제들을 안은 채로 프로젝트를 수행했고, 나는 성공적으로 SDK를 개발하여 Mvn Repository에 배포하는 경험까지 했다. 이 과정에서는 심지어, 공식 Maven Central Repository에서 배포 과정을 수정하는 중이었어서, 공식 홈페이지에서 안내하는 비공식 배포 과정으로 배포를 진행했다. 여기에 배포하기 위해서 GPG인증을 거치는 과정까지 새로운 경험을 많이 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (17).png&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nL4OH/btsIhSlaqBS/q3kOdweUbDsnXEGP9Y2pK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nL4OH/btsIhSlaqBS/q3kOdweUbDsnXEGP9Y2pK0/img.png&quot; data-alt=&quot;Mvn Repository LightSwitch SDK&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nL4OH/btsIhSlaqBS/q3kOdweUbDsnXEGP9Y2pK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnL4OH%2FbtsIhSlaqBS%2Fq3kOdweUbDsnXEGP9Y2pK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;693&quot; data-filename=&quot;Untitled (17).png&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mvn Repository LightSwitch SDK&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 우리가 많이 사용하는 MVN Repository에도 올라 가 있는 상태이고, 자세한 배포 과정이 궁금하다면 아래 포스팅을 참고하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1719770673418&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;[INFRA] Maven Central Repository에 Gradle Artifacts 배포하기&quot; data-og-description=&quot;Maven Central RepositoryMaven Central Repository는 Maven 프로젝트를 위한 공개 아티팩트(Artifact) 저장소이다. 전 세계 개발자들이 개발에 활용할 수 있도록 수많은 라이브러리와 프레임워크, 플러그인을 중&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/90&quot; data-og-url=&quot;https://olrlobt.tistory.com/90&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btmBGF/hyWrNDtCjD/ne2gpL3ySTXD6OzSbr4BTK/img.png?width=410&amp;amp;height=372&amp;amp;face=0_0_410_372,https://scrap.kakaocdn.net/dn/bcxDhO/hyWvLqtEZ6/pMWMCkamtrCJ1oy0td509K/img.png?width=410&amp;amp;height=372&amp;amp;face=0_0_410_372,https://scrap.kakaocdn.net/dn/oOTqW/hyWrVO3y4U/EchUomap0rck5y1fmwKj80/img.png?width=3099&amp;amp;height=774&amp;amp;face=0_0_3099_774&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btmBGF/hyWrNDtCjD/ne2gpL3ySTXD6OzSbr4BTK/img.png?width=410&amp;amp;height=372&amp;amp;face=0_0_410_372,https://scrap.kakaocdn.net/dn/bcxDhO/hyWvLqtEZ6/pMWMCkamtrCJ1oy0td509K/img.png?width=410&amp;amp;height=372&amp;amp;face=0_0_410_372,https://scrap.kakaocdn.net/dn/oOTqW/hyWrVO3y4U/EchUomap0rck5y1fmwKj80/img.png?width=3099&amp;amp;height=774&amp;amp;face=0_0_3099_774');&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;[INFRA] Maven Central Repository에 Gradle Artifacts 배포하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Maven Central RepositoryMaven Central Repository는 Maven 프로젝트를 위한 공개 아티팩트(Artifact) 저장소이다. 전 세계 개발자들이 개발에 활용할 수 있도록 수많은 라이브러리와 프레임워크, 플러그인을 중&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;여하튼 이렇게 Java SDK까지 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;맡은 역할을 다 수행했다. 그리고 공통 프로젝트에서 인프라를 맡으며, 한 가지 해 보고 싶었던 것이 있는데, 바로 Blue/Green 무중단 배포이다. 공통 프로젝트를 진행하며, 프로젝트가 빌드될 때마다 소요되는 5분 남짓의 시간 동안, API를 호출하지 못하는 문제가 발생했었는데, 이를 해결해보고 싶어서 팀에게 내가 하게 해달라고 부탁했다. 그리고 개인 프로젝트에서 수많은 테스트를 거쳐, 해당 자율 프로젝트에 적용까지 성공했다.&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;letter-spacing: 0px;&quot;&gt;여기까지 다 수행하고, 팀에게 더 도움이 되고자 써보지도 않은 React로 Tag 기능을 만들었다. 그리고 더 나아가 백엔드에 기여하여 Kotlin으로 Spring AOP를 사용하여 플래그의 변동사항을 기록하는 기능도 만들었다. 이번 프로젝트도 개인적으로 시간을 많이 투자해 가며 성과를 내기 위해 노력했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;결과적으로 훌륭한 팀원들이 많았어서 수상까지 하기는 했지만, 이 프로젝트 또한 아쉬움이 많이 남는다. 디자인 쪽에 특화되어 있는 팀원이 없어서, 디자인 부분이 많이 아쉬웠고, 팀원들과 소통을 안 하는 분이 계셔서 진행에 상당한 곤란함을 느꼈다. 그래도 이런 과정을 거치 고나니 더 성장한 것 같기도 하고, 마지막 프로젝트에 수상까지 해서 한편으로는 뿌듯하기도 하였다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&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;이 프로젝트는 아직 README가 정리가 안 되었다. 안 한 건 아니고, 업로드를 깜빡했다. 최대한 빠른 시간 내에 리드미를 작성하여 수정하도록 하겠다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1719771411003&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 - olrlobt/LightSwitch&quot; data-og-description=&quot;Contribute to olrlobt/LightSwitch development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/olrlobt/LightSwitch&quot; data-og-url=&quot;https://github.com/olrlobt/LightSwitch&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bPJK2I/hyWrUWU5wM/ftLKCDlzB7KmVbzrq7jzW0/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236&quot;&gt;&lt;a href=&quot;https://github.com/olrlobt/LightSwitch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/olrlobt/LightSwitch&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bPJK2I/hyWrUWU5wM/ftLKCDlzB7KmVbzrq7jzW0/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236');&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 - olrlobt/LightSwitch&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to olrlobt/LightSwitch development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2학기 JobFair&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;자율 프로젝트까지 끝나고 나면, 1주일 간의 포트폴리오 위크가 진행된다. 교육생들 포트폴리오를 어떻게 작성하면 좋을지, 그리고 서로의 포트폴리오를 보고 피드백을 해 주는 시간을 갖는다. 이 시간들을 통해, 내 포트폴리오의 부족한 점을 보완할 수 있고, 남에게 보이는 내 포트폴리오가 어떻게 보이는지 자세히 들을 수 있다.&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;letter-spacing: 0px;&quot;&gt;포트폴리오 위크까지 끝나고 나면, 1학기 JobFair와 똑같은 여러 가지 취업 지원 활동들을 한다. 온라인으로 취업 특강을 해주거나, 취업 박람회를 통해 취업 면접 기회와 취업 상담 기회를 제공해 준다. 또한, 모의 면접을 통해 전문가와 일대일 혹은 일대다 면접 상황을 경험해 볼 수 있다. 나는 이 모의면접이 상당히 자신이 없었는데, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;실제로 모의면접에서는 내가 사전에 제출한 포트폴리오를 활용하여 모의면접을 진행한다. 이때, 전문가 분께서 내 포트폴리오는 같이 일해보고 싶을 정도로 괜찮다고 칭찬을 해 주신 것이 기억에 남는다. 근데, 영 면접은 탈탈 털려서 준비 좀 해야겠다는 말씀을 해 주셨다.  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;나도 내가 부족한 것을 알고 있고, 이 모의 면접에서 느낀 것들을 기회로 면접 준비를 다시 해 볼 계획이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2학기 JobFair에서는 1학기 때와는 다르게 많은 친구들이 취업 박람회를 활용하여 취업에 도전한다. 실제로 면접 보러 간다는 횟수가 엄청 늘었고, 이를 통해 합격한 친구들도 꽤 있다. 나도 여러 군데 지원을 넣고는 있는데 정말이지 쉽지 않다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 id=&quot; 어땠어&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 어땠어&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;여기까지가 내 싸피 1년의 과정의 끝이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;정말 많이 배웠고 많이 성장했고, 많은 것들을 얻어갈 수 있었다. 1년이라는 짧지 않은 시간이었지만, 스터디 운영부터, 여러 상장들과, JAVA SDK를 개발하는 특이한 경험까지.. 다 싸피라서 할 수 있었던 경험이지 않나 싶다.&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;letter-spacing: 0px;&quot;&gt;그리고 이제 친구들을 볼 수 없다는 사실이 정말이지 아쉽다. 시간은 정말 빠르고, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;많은 친구들이 취업에 성공했다. 그리고 나를 포함한 많은 친구들이 또 열심히 준비하고 있다. 다들 잘 됐으면 좋겠고, 좋은 소식 들고 와서 취업턱이나 쐈으면 좋겠다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;만약 이 글을 보고 있는 분이, SSAFY 지원을 고민하고 있다면, 나는 당연히 다니라고 하고 싶다. 그만큼 좋았고 재밌었던 추억으로 가득하다. 그리고 요즘 면접 나가는 친구들 이야기 들어보면, 다대다 환경에서 반은 싸피라고 ,,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싸피 안 나온 사람들은 어떻게 취업을 하고 있는 거지..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;어쨋든 다들 취업하는 그 날까지 화이팅이다 !! &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt; &lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;33.webp&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WYzjc/btsI3Wuxlvk/C46QaVxzthCpde6Q9oMi0K/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WYzjc/btsI3Wuxlvk/C46QaVxzthCpde6Q9oMi0K/img.webp&quot; data-alt=&quot;SSAFY 10기 수료식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WYzjc/btsI3Wuxlvk/C46QaVxzthCpde6Q9oMi0K/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWYzjc%2FbtsI3Wuxlvk%2FC46QaVxzthCpde6Q9oMi0K%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;460&quot; height=&quot;4032&quot; data-filename=&quot;33.webp&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSAFY 10기 수료식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>else/회고</category>
      <category>hellossafy</category>
      <category>ssafy</category>
      <category>SSAFY수료식</category>
      <category>삼성청년SW아카데미</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/91</guid>
      <comments>https://olrlobt.tistory.com/91#entry91comment</comments>
      <pubDate>Mon, 1 Jul 2024 03:51:04 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] Maven Central Repository에 Gradle Artifacts 배포하기</title>
      <link>https://olrlobt.tistory.com/90</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Maven Central Repository&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Maven Central Repository는 Maven 프로젝트를 위한 공개 아티팩트(Artifact) 저장소이다. 전 세계 개발자들이 개발에 활용할 수 있도록 수많은 라이브러리와 프레임워크, 플러그인을 중앙에 모아두고 공유하는 공간이다. 우리 같은 개발자들은 흔히 mvnrepository를 통하여 쉽게 라이브러리들을 찾고는 하는데, 이 m&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;vnrepository는 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt; Maven Central Repository나 다른 저장소들의 아티팩트를 인덱싱하는 웹사이트이다.&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;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;쉽게 말해&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;m&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;vnrepository은 검색 엔진이고, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;Maven Central Repository은 실제 파일이 올라가는 저장소로 생각하면 된다.&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;Maven Central 공식 홈페이지&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715004328211&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;Maven Central&quot; data-og-description=&quot;Official search by the maintainers of Maven Central Repository.&quot; data-og-host=&quot;central.sonatype.com&quot; data-og-source-url=&quot;https://central.sonatype.com/&quot; data-og-url=&quot;https://central.sonatype.com/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://central.sonatype.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://central.sonatype.com/&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;Maven Central&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Official search by the maintainers of Maven Central Repository.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;central.sonatype.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;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Maven Central Repository를 검색하면 배포를 돕는 여러 가지 포스팅들이 나오지만, &lt;b&gt;2024년 3월 12일 기준으로 기존 OSSRH를 사용하던 방식에서 중앙 포털을 통해 배포하는 방식으로 변경&lt;/b&gt;되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 OSSRH를 통하여 Nexus repository로 업로드하는 방식을 사용하던 유저는 기존 방식을 그대로 사용할 수 있지만, 신규 유저라면 Jira 티켓을 발급할 수 없고, Nexus repository에 로그인할 수도 없다.&lt;/p&gt;
&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;Maven Central 공식 문서에서 제공해 주는 중앙 포털 배포 방식&lt;/b&gt;을 따라야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1325&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpLuSw/btsHbguG66U/VXLERuyfa5yYTRg8BFvkjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpLuSw/btsHbguG66U/VXLERuyfa5yYTRg8BFvkjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpLuSw/btsHbguG66U/VXLERuyfa5yYTRg8BFvkjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpLuSw%2FbtsHbguG66U%2FVXLERuyfa5yYTRg8BFvkjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;2024년 3월 12일 이후로는 Maven Central 중앙 포털을 통해 배퐇해야 한다.&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;240&quot; data-origin-width=&quot;1325&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;Maven Central 공식 문서&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715004564088&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;Register to Publish Via the Central Portal&quot; data-og-description=&quot;Register to Publish Via the Central Portal Registration Note From March 12th, 2024, all registration will be via the Central Portal. For information about legacy registration, please see the relevant documentation. For support with switching to publish via&quot; data-og-host=&quot;central.sonatype.org&quot; data-og-source-url=&quot;https://central.sonatype.org/register/central-portal/&quot; data-og-url=&quot;https://central.sonatype.org/register/central-portal/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://central.sonatype.org/register/central-portal/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://central.sonatype.org/register/central-portal/&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;Register to Publish Via the Central Portal&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Register to Publish Via the Central Portal Registration Note From March 12th, 2024, all registration will be via the Central Portal. For information about legacy registration, please see the relevant documentation. For support with switching to publish via&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;central.sonatype.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Namespace 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Maven Central Repository에 접속하여, 우측 상단의 Sign In으로 가입을 진행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3099&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uYl37/btsHeeWfgPd/KDGEPVWjuoSHIK6FYdHWh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uYl37/btsHeeWfgPd/KDGEPVWjuoSHIK6FYdHWh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uYl37/btsHeeWfgPd/KDGEPVWjuoSHIK6FYdHWh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuYl37%2FbtsHeeWfgPd%2FKDGEPVWjuoSHIK6FYdHWh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Maven Central Repository 메인화면&quot; loading=&quot;lazy&quot; width=&quot;3099&quot; height=&quot;774&quot; data-origin-width=&quot;3099&quot; data-origin-height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가입 후에는 상단 바의 Publish를 통하여 네임스페이스 생성 페이지로 이동한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2166&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SIdTM/btsHcqpyzxQ/pa1p7A4ClHKzM0fMGzBjIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SIdTM/btsHcqpyzxQ/pa1p7A4ClHKzM0fMGzBjIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SIdTM/btsHcqpyzxQ/pa1p7A4ClHKzM0fMGzBjIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSIdTM%2FbtsHcqpyzxQ%2Fpa1p7A4ClHKzM0fMGzBjIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Maven Central Repository Namespace&quot; loading=&quot;lazy&quot; width=&quot;2166&quot; height=&quot;784&quot; data-origin-width=&quot;2166&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;네임스페이스는 Maven 생태계에서 컴포넌트를 유일하게 식별하기 위한 Group Id이다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Maven Central에 호스팅 되는 모든 다른 게시자의 컴포넌트들과 이 네임스페이스를 통해 식별하게 된다.&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;우측 상단의 Add Namespace로 만들 수 있는데, DNS의 경우 Java 패키지 명명 규칙과 유사하게 도메인 이름을 역방향으로 작성하면 된다. 예를 들어 olrlobt.tistory.com이라면 com.tistory.olrlobt로 작성하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 도메인이 없다면 Github를 사용해도 되는데, 이 경우 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;io.github. {사용자 이름} 형식을 사용한다.&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNqYfD/btsHbRnGizR/yjr5yOFdb1kOQSlMemJaRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNqYfD/btsHbRnGizR/yjr5yOFdb1kOQSlMemJaRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNqYfD/btsHbRnGizR/yjr5yOFdb1kOQSlMemJaRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNqYfD%2FbtsHbRnGizR%2Fyjr5yOFdb1kOQSlMemJaRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Maven Central Repository Add Namespace modal&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;509&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이번에 배포할 도메인의 주소가 lightswitch.kr이었기 때문에 kr.lightswitch로 생성하려 했었다. 하지만, kr이 지역 도메인이어서 그런가 인식이 되지 않았고, 어쩔 수 없이 kr.lightswitch.www로 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2149&quot; data-origin-height=&quot;691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ECHPG/btsHcRNZTBw/64Z8dxu2YALdQyKtXe7gU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ECHPG/btsHcRNZTBw/64Z8dxu2YALdQyKtXe7gU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ECHPG/btsHcRNZTBw/64Z8dxu2YALdQyKtXe7gU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FECHPG%2FbtsHcRNZTBw%2F64Z8dxu2YALdQyKtXe7gU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Maven Central Repository create namespace&quot; loading=&quot;lazy&quot; width=&quot;2149&quot; height=&quot;691&quot; data-origin-width=&quot;2149&quot; data-origin-height=&quot;691&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성을 완료하면 네임스페이스의 소유권을 확인하라는 메시지가 나오게 된다. 이때, 확인이 완료되지 않고 무턱대고 Verify Namespace를 눌렀다가는 응답이 캐싱되어 버려 확인이 완료되어도 캐시가 만료될 때까지 진행을 하지 못하니 주의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace가 Github 저장소로 설정이 되어있다면, Verification 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;DNS를 사용하는 경우에는 TXT 레코드를 설정하여 인증을 진행해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 가비아를 이용하여 도메인을 설정해 놓았기 때문에, 가비아에 접속하여 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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2132&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OJ8IR/btsHcatJfeD/jCImUVXej5y2jmxY5lBUMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OJ8IR/btsHcatJfeD/jCImUVXej5y2jmxY5lBUMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OJ8IR/btsHcatJfeD/jCImUVXej5y2jmxY5lBUMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOJ8IR%2FbtsHcatJfeD%2FjCImUVXej5y2jmxY5lBUMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Gabia TXT레코드 추가&quot; loading=&quot;lazy&quot; width=&quot;2132&quot; height=&quot;802&quot; data-origin-width=&quot;2132&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 TXT레코드에 인증코드를 설정해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 다음 Ubuntu에서 인증코드가 잘 나오는지 아래의 명령어로 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1715007121753&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ host -t txt www.lightswitch.kr&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wPbG5/btsHecqz3BT/4N7o6wV33Ddy4Wqn7Tr7Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wPbG5/btsHecqz3BT/4N7o6wV33Ddy4Wqn7Tr7Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wPbG5/btsHecqz3BT/4N7o6wV33Ddy4Wqn7Tr7Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwPbG5%2FbtsHecqz3BT%2F4N7o6wV33Ddy4Wqn7Tr7Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Ubuntu DNS 검증 명령어&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;39&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;39&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 정상적으로 나온다면, 홈페이지에서 인증을 진행하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 새로고침을 진행하면 아래와 같이 인증이 완료되었다고 표시된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2146&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkUR7W/btsHb7qd0rc/40BdcM3iBmmpPZzGIsjdok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkUR7W/btsHb7qd0rc/40BdcM3iBmmpPZzGIsjdok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkUR7W/btsHb7qd0rc/40BdcM3iBmmpPZzGIsjdok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkUR7W%2FbtsHb7qd0rc%2F40BdcM3iBmmpPZzGIsjdok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Maven Central Repository Namespace verified&quot; loading=&quot;lazy&quot; width=&quot;2146&quot; height=&quot;636&quot; data-origin-width=&quot;2146&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Deployments&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Namespace 인증을 완료했으니, Maven Central Repository에 배포해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maven Central Repository 중앙 저장소에 배포하기 위해서는 배포된 아티팩트의 품질을 최소 수준으로 보장하기 위해 여러 가지 요구사항을 맞춰 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 요구사항에는 아래와 같은 것들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-md-component=&quot;toc&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Javadoc 및 소스 제공&lt;/li&gt;
&lt;li&gt;파일 체크섬 제공&lt;/li&gt;
&lt;li&gt;GPG/PGP로 파일 서명&lt;/li&gt;
&lt;li&gt;충분한 메타데이터&lt;/li&gt;
&lt;li&gt;올바른 좌표&lt;/li&gt;
&lt;li&gt;프로젝트 이름, 설명 및 URL&lt;/li&gt;
&lt;li&gt;라이선스 정보&lt;/li&gt;
&lt;li&gt;개발자 정보&lt;/li&gt;
&lt;li&gt;SCM정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 정보들은 간단하게 공식 문서에서 제공하는 플러그인으로 요구사항만 맞추어 작성해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만,,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;1141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCjBnE/btsHbfo7D4A/dO2yICNhCO0PMS06FlaNYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCjBnE/btsHbfo7D4A/dO2yICNhCO0PMS06FlaNYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCjBnE/btsHbfo7D4A/dO2yICNhCO0PMS06FlaNYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCjBnE%2FbtsHbfo7D4A%2FdO2yICNhCO0PMS06FlaNYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;there is no official Gradle plugin for publishing to Maven Central via the Central Publishing Portal&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;1141&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;1141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 공식적으로 Gradle 플러그인은 지원하지 않고 있고, 이에 따라 여러 비공식 플러그인들을 연결해 주고 있었다. 대부분은 kotlin으로 작성된 플러그인이라 내 java 프로젝트에서 사용하기에는 무리가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 나는 여러 가지를 시도해 본 결과, 아래의 플러그인을 이용하기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1715008218462&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 - vanniktech/gradle-maven-publish-plugin: A Gradle plugin that publishes your Android and Kotlin libraries, including sou&quot; data-og-description=&quot;A Gradle plugin that publishes your Android and Kotlin libraries, including sources and javadoc, to Maven Central or any other Nexus instance. - vanniktech/gradle-maven-publish-plugin&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/vanniktech/gradle-maven-publish-plugin/?tab=readme-ov-file&quot; data-og-url=&quot;https://github.com/vanniktech/gradle-maven-publish-plugin&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dGUhtL/hyVZn6m6YK/IPJRDQWTFP1ib0AFwVkLwK/img.png?width=1200&amp;amp;height=600&amp;amp;face=1010_103_1051_148&quot;&gt;&lt;a href=&quot;https://github.com/vanniktech/gradle-maven-publish-plugin/?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/vanniktech/gradle-maven-publish-plugin/?tab=readme-ov-file&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dGUhtL/hyVZn6m6YK/IPJRDQWTFP1ib0AFwVkLwK/img.png?width=1200&amp;amp;height=600&amp;amp;face=1010_103_1051_148');&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 - vanniktech/gradle-maven-publish-plugin: A Gradle plugin that publishes your Android and Kotlin libraries, including sou&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A Gradle plugin that publishes your Android and Kotlin libraries, including sources and javadoc, to Maven Central or any other Nexus instance. - vanniktech/gradle-maven-publish-plugin&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;GPG 서명&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙 저장소에 아티팩트를 게시하기 위한 요구 사항&lt;span&gt;&amp;nbsp;&lt;/span&gt;중 하나는 PGP로 서명되어야 한다는 것이다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;GPG(GNU Privacy Guard)는 데이터 암호화와 전자 서명에 사용되는 공개 키 암호화 소프트웨어로, 개인정보 보호 및 인증을 위해 사용된다. GPG로 프로젝트를 서명함으로써, 프로젝트를 사용하는 사용자가 신뢰된 프로젝트를 사용할 수 있게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; GnuPG바이너리를 다운로드 &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnupg.org/download/index.html#sec-1-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gnupg.org/download/index.html#sec-1-2&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 위 사이트에서 GnuPG바이너리를 다운로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 gpg --version 명령어를 통해 설치가 잘 진행되었는지 확인하자.&lt;/p&gt;
&lt;pre id=&quot;code_1715008991812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ gpg --version
gpg (GnuPG) 2.2.19
libgcrypt 1.8.5
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &amp;lt;https://gnu.org/licenses/gpl.html&amp;gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /home/mylocaluser/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;키 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 발급된 개인키/공개키가 없다면 아래 명령어로 키를 발급받을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1715009030099&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ gpg --gen-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;이 과정에서 이름, 이메일, 비밀번호를 요구하는데, 필요에 맞게 작성해 주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUvQOy/btsHbkKBwRD/XqZ8jbNKPUFkaGNRSgS5Ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUvQOy/btsHbkKBwRD/XqZ8jbNKPUFkaGNRSgS5Ek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUvQOy/btsHbkKBwRD/XqZ8jbNKPUFkaGNRSgS5Ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUvQOy%2FbtsHbkKBwRD%2FXqZ8jbNKPUFkaGNRSgS5Ek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;GPG 키 생성 명령어&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;202&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;생성된 키 확인&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1715009089294&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ gpg --list-keys
/home/mylocaluser/.gnupg/pubring.kbx
---------------------------------
pub   rsa3072 2021-06-23 [SC] [expires: 2023-06-23]
      CA925CD6C9E8D064FF05B4728190C4130ABA0F98    // 끝 8자리 = key Id
uid           [ultimate] Central Repo Test &amp;lt;central@example.com&amp;gt;
sub   rsa3072 2021-06-23 [E] [expires: 2023-06-23]&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;위 명령어를 통해 키가 생성된 것을 확인할 수 있다. 여기서 키의 맨 뒤 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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;공개 키 전송&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPG로 서명한 프로젝트를 다른 사람이 확인하기 위해서는 공개 키가 필요하다. 따라서 공개 키를 공캐 키 서버에 배포해야 하는데, Maven Central Repository에서 사용하는 공개 키 서버는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;keyserver.ubuntu.com&lt;/li&gt;
&lt;li&gt;keys.openpgp.org&lt;/li&gt;
&lt;li&gt;pgp.mit.edu&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_1715010399700&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 공개 키 전송
gpg --keyserver keyserver.ubuntu.com --send-keys 0ABA0F98&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Gradle 플러그인 스크립트 추가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maven Central repository 패키지를 배포할 때, 프로세스를 자동화하기 위해 build.gradle에 앞전에 언급했던 플러그인을 사용할 것이다. 이를 위해, build.gradle에 mavenPublishing 블록을 추가해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;플러그인 추가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, build.gradle 설정에 플러그인을 추가해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 언급한 비공식 배포 플러그인이며, 이를 통해 간단하게 배포할 수&amp;nbsp; 있는 과정을 지원한다.&lt;/p&gt;
&lt;pre id=&quot;code_1715010711231&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
  id &quot;com.vanniktech.maven.publish&quot; version &quot;0.28.0&quot;
  id 'signing' // GPG 서명을 위한 플러그인
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Config 추가 (선택)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config를 통해 게시할 항목을 구성한다. 기본 구성으로는 소스 및 javadoc jar가 포함되어 있으며, 이를 수정하고 싶으면 아래와 같은 명령어를 통해 수정할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1715010743204&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.vanniktech.maven.publish.JavaLibrary
import com.vanniktech.maven.publish.JavadocJar

mavenPublishing {
  // the first parameter configures the -javadoc artifact, possible values:
  // - `JavadocJar.None()` don't publish this artifact
  // - `JavadocJar.Empty()` publish an emprt jar
  // - `JavadocJar.Javadoc()` to publish standard javadocs
  // the second whether to publish a sources jar
  configure(new JavaLibrary(new JavadocJar.Javadoc(), 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Maven Central repository 게시 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어 중 하나를 선택한다. 기존 유저라면 DEFAUTL, S01 방법으로 사용하면 되고, 우리는 신규 유저기 때문에 중앙 배포 방식을 사용하므로 CENTRAL_PORTAL을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 해당 플러그인에서는 환경변수를 이용하여 signAllPublications() 메서드로 GPG 서명을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 자바 9 버전 이상이라면 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;useGpgCmd()를 이용하여 서명을 진행하면 되기 때문에, 생략하여도 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1715011539553&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.vanniktech.maven.publish.SonatypeHost

mavenPublishing {
  // publishToMavenCentral(SonatypeHost.DEFAULT)
  // or when publishing to https://s01.oss.sonatype.org
  // publishToMavenCentral(SonatypeHost.S01)
  // or when publishing to https://central.sonatype.com/
  publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)

  //signAllPublications()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;POM 구성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POM 구성에는 Maven Cetral Repository 배포를 위한 구성요소들이 들어간다. &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;URL 및 사용된 라이선스와 같은 프로젝트에 대한 일부 일반 정보들이 포함되며, &lt;/span&gt;여기서 작성되는 것들은 문서를 위해 작성되는 것들이다. 알맞게 작성해야 배포된 라이브러리를 사용하는 사용자들이 라이선스를 확인하고 사용하거나, 버그가 있을 때 문서를 찾아보고 제보하기 수월할 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 coordinates에 들어가는 것들이 Maven Central Repository의 폴더 구성을 하므로, 네임스페이스, 라이브러리 이름, 버전 순으로 작성해 주어야 한다. 이때, 중앙 배포 방식은 SNAPSHOT 배포를 지원하지 않는 것에 유의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715011955782&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mavenPublishing {
   coordinates(&quot;kr.lightswitch.www&quot;, &quot;lightswitch&quot;, &quot;1.0.0&quot;)

    pom {
        name = &quot;lightswitch&quot;
        description = &quot;lightswitch for lunit&quot;
        inceptionYear = &quot;2024&quot;
        url = &quot;www.lightswitch.kr&quot;
        licenses {
            license {
                name = &quot;The Apache License, Version 2.0&quot;
                url = &quot;https://www.apache.org/licenses/LICENSE-2.0.txt&quot;
                distribution = &quot;https://www.apache.org/licenses/LICENSE-2.0.txt&quot;
            }
        }
        developers {
            developer {
                id = &quot;olrlobt&quot;
                name = &quot;Seungheon Lee&quot;
                url = &quot;https://github.com/olrlobt&quot;
            }
        }
        scm {
            url = &quot;https://github.com/olrlobt&quot;
            connection = &quot;scm:git:git://github.com/olrlobt&quot;
            developerConnection = &quot;scm:git:ssh://git@github.com/olrlobt&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Secret gradle.properties&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/. gradle/gradle.properties에 아래와 같은 변수들을 작성해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 signAllPublications()를 사용한다면, gpg에 대한 정보를 작성해 주어야 하며, 그렇지 않다면 생략한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1715012274079&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mavenCentralUsername=username
mavenCentralPassword=the_password

//signing.keyId=12345678
//signing.password=some_password
//signing.secretKeyRingFile=/Users/yourusername/.gnupg/secring.gpg&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;build.gradle 전체&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1715012738226&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.vanniktech.maven.publish.SonatypeHost
import com.vanniktech.maven.publish.JavaLibrary
import com.vanniktech.maven.publish.JavadocJar

plugins {
    id 'java'
    id &quot;com.vanniktech.maven.publish&quot; version &quot;0.28.0&quot;
    id 'signing'
}

group = 'com.lightswitch'
version = '1.0.0'

repositories {
    mavenCentral()
}

signing {
    useGpgCmd()
    sign publishing.publications
}


mavenPublishing {
//    configure(new JavaLibrary(new JavadocJar.Empty(), true))
    publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)

//    signAllPublications()

    coordinates(&quot;kr.lightswitch.www&quot;, &quot;lightswitch&quot;, &quot;1.0.0&quot;)

    pom {
        name = &quot;lightswitch&quot;
        description = &quot;lightswitch for lunit&quot;
        inceptionYear = &quot;2024&quot;
        url = &quot;www.lightswitch.kr&quot;
        licenses {
            license {
                name = &quot;The Apache License, Version 2.0&quot;
                url = &quot;https://www.apache.org/licenses/LICENSE-2.0.txt&quot;
                distribution = &quot;https://www.apache.org/licenses/LICENSE-2.0.txt&quot;
            }
        }
        developers {
            developer {
                id = &quot;olrlobt&quot;
                name = &quot;Seungheon Lee&quot;
                url = &quot;https://github.com/olrlobt&quot;
            }
        }
        scm {
            url = &quot;https://github.com/olrlobt&quot;
            connection = &quot;scm:git:git://github.com/olrlobt&quot;
            developerConnection = &quot;scm:git:ssh://git@github.com/olrlobt&quot;
        }
    }
}

dependencies {
    compileOnly 'javax.servlet:javax.servlet-api:4.0.1'
    implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1'
    testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.25.3'
    ... 생략
}

... 생략&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;빌드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Maven Central Repository 배포를 위한 설정은 모두 마쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 아래 명령어를 이용하여 배포를 진행하자.&lt;/p&gt;
&lt;pre id=&quot;code_1715012878912&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./gradlew publishAllPublicationsToMavenCentralRepository&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k6ixC/btsHb5MKKQ6/OwYp9c3gOkMkLdhRjvQGXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6ixC/btsHb5MKKQ6/OwYp9c3gOkMkLdhRjvQGXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6ixC/btsHb5MKKQ6/OwYp9c3gOkMkLdhRjvQGXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6ixC%2FbtsHb5MKKQ6%2FOwYp9c3gOkMkLdhRjvQGXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Maven Central Repository 배포 명령어&quot; loading=&quot;lazy&quot; width=&quot;1348&quot; height=&quot;165&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드가 성공적으로 진행되었다면, Maven Central Repository의 Deployments에서 확인이 가능하다.&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;2376&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pLNZN/btsHeasKbb1/Ub5jHR4EcSRSoE3R3W9Wjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pLNZN/btsHeasKbb1/Ub5jHR4EcSRSoE3R3W9Wjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pLNZN/btsHeasKbb1/Ub5jHR4EcSRSoE3R3W9Wjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpLNZN%2FbtsHeasKbb1%2FUb5jHR4EcSRSoE3R3W9Wjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Maven Central Repository의 Deployments&quot; loading=&quot;lazy&quot; width=&quot;2376&quot; height=&quot;922&quot; data-origin-width=&quot;2376&quot; data-origin-height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;2407&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x99e9/btsHcRHfEZn/dC8ihldRTia9YPco7kxuCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x99e9/btsHcRHfEZn/dC8ihldRTia9YPco7kxuCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x99e9/btsHcRHfEZn/dC8ihldRTia9YPco7kxuCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx99e9%2FbtsHcRHfEZn%2FdC8ihldRTia9YPco7kxuCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Maven Central Repository의 Deployments 배포 완료&quot; loading=&quot;lazy&quot; width=&quot;2407&quot; height=&quot;948&quot; data-origin-width=&quot;2407&quot; data-origin-height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;repo1.maven 확인&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 올라간 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1355&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/catf1G/btsHe5rcskI/M8tLO2sIfC4TQ4p5oiXSk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/catf1G/btsHe5rcskI/M8tLO2sIfC4TQ4p5oiXSk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/catf1G/btsHe5rcskI/M8tLO2sIfC4TQ4p5oiXSk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcatf1G%2FbtsHe5rcskI%2FM8tLO2sIfC4TQ4p5oiXSk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;repo1.maven.org 배포 완료&quot; loading=&quot;lazy&quot; width=&quot;1355&quot; height=&quot;609&quot; data-origin-width=&quot;1355&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;build.gradle 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 build.gradle에서 아래처럼 간편하게 내 라이브러리를 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1715013197722&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    implementation 'kr.lightswitch.www:lightswitch:1.0.0'
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/90</guid>
      <comments>https://olrlobt.tistory.com/90#entry90comment</comments>
      <pubDate>Tue, 7 May 2024 01:38:26 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] 가비아 도메인 구매 후 EC2 서버에 적용하기</title>
      <link>https://olrlobt.tistory.com/89</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;가비아 도메인 구매하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2로 서버를 열어서 사용하다 보면, EC2에서 기본으로 제공하는 주소가 너무 길어서 불편함을 느낄 수밖에 없다. 특히 내 개인 토인 프로젝트의 경우, API 호출만 사용하는데 도메인 주소가 길다 보니 상당히 복잡해지고 불편했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1714929741572&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### EC2에서 기본으로 제공하는 주소 형식
ec2-000-000-000-00.ap-northeast-2.compute.amazonaws.com&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1714927080284&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### API를 호출하면 엄청 길어진다.
https://ec2-000-000-000-00.ap-northeast-2.compute.amazonaws.com/api/fix?url=https://olrlobt.tistory.com/

### 현재 쓰고 있는 API 호출
https://blogwidget.com/api/fix?url=https://olrlobt.tistory.com/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 도메인을 구매하여 적용하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1714927261061&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;www.gabia.com&quot; data-og-source-url=&quot;https://www.gabia.com/?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_term=%EA%B0%80%EB%B9%84%EC%95%84&amp;amp;utm_campaign=%EA%B0%80%EB%B9%84%EC%95%84&quot; data-og-url=&quot;https://www.gabia.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/egBXrN/hyVZhkBnA8/RgjQFk12roAs7XAKszprKk/img.jpg?width=1200&amp;amp;height=1000&amp;amp;face=0_0_1200_1000&quot;&gt;&lt;a href=&quot;https://www.gabia.com/?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_term=%EA%B0%80%EB%B9%84%EC%95%84&amp;amp;utm_campaign=%EA%B0%80%EB%B9%84%EC%95%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.gabia.com/?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_term=%EA%B0%80%EB%B9%84%EC%95%84&amp;amp;utm_campaign=%EA%B0%80%EB%B9%84%EC%95%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/egBXrN/hyVZhkBnA8/RgjQFk12roAs7XAKszprKk/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;웹을 넘어 클라우드로. 가비아&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;그룹웨어부터 멀티클라우드까지 하나의 클라우드 허브&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.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;가비아는 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;IaaS형 클라우드, SaaS 그룹웨어 설루션 등 비즈니스에 필요한 IT 인프라 전반을 제공하는 경기도 판교 소재 클라우드 전문 기업이다. 국내 도메인 점유율 1위 기업으로, 처음 도메인을 등록해 보는 나로서 선택을 하지 않을 이유가 없었다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;도메인 구매하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 원하는 도메인을 검색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 도메인의 .com, .org, .kr 같은 최상위 도메인(TLD, Top-Level Domain)은 가격을 결정하는 중요한 요소이다. 특히 인지도가 높은.com, .org, .io 같은 경우 높은 가격이 책정되는데, 인지도와 신뢰성이 높기 때문이다. 만약 내 사이트가 blogwidget.store라면 뭔가 바이러스가 걸릴 것만 같지 않나? 따라서 나는&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;blogwidget.com을 구매하기로 했다.&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1321&quot; data-origin-height=&quot;923&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Duc4h/btsHcR72h8n/mnYjaA856xjK5kehfGKluk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Duc4h/btsHcR72h8n/mnYjaA856xjK5kehfGKluk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Duc4h/btsHcR72h8n/mnYjaA856xjK5kehfGKluk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDuc4h%2FbtsHcR72h8n%2FmnYjaA856xjK5kehfGKluk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;가비아 도메인 검색&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;923&quot; data-origin-width=&quot;1321&quot; data-origin-height=&quot;923&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;1951&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRqGAY/btsHbisjMVc/90UKQgoKrYk5OyDCvd0ck1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRqGAY/btsHbisjMVc/90UKQgoKrYk5OyDCvd0ck1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRqGAY/btsHbisjMVc/90UKQgoKrYk5OyDCvd0ck1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRqGAY%2FbtsHbisjMVc%2F90UKQgoKrYk5OyDCvd0ck1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;가비아 도메인 구매&quot; loading=&quot;lazy&quot; width=&quot;614&quot; height=&quot;696&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;1951&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;901&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byE0a7/btsHbSs6pTr/9aLgyJcDR68eoVhVroRnA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byE0a7/btsHbSs6pTr/9aLgyJcDR68eoVhVroRnA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byE0a7/btsHbSs6pTr/9aLgyJcDR68eoVhVroRnA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyE0a7%2FbtsHbSs6pTr%2F9aLgyJcDR68eoVhVroRnA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;가비아 도메인 구매 완료&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;327&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;901&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Route 53 설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Amazon Route 53은 AWS(Amazon Web Services)에서 제공하는 클라우드 기반 DNS(Domain Name System) 웹 서비스이다. 이를 통해 사용자는 인터넷 도메인 이름을 웹 서버나 다른 AWS 리소스의 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;우리는 Route 53에 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;먼저 Route 53 대시보드에 접속해서 좌측 메뉴의 &quot;호스팅 영역&quot;에서 호스팅 영역을 생성해 주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2398&quot; data-origin-height=&quot;1302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7UVpd/btsHa1YDJa1/JUDxk7c1PpIHknqx4gV3HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7UVpd/btsHa1YDJa1/JUDxk7c1PpIHknqx4gV3HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7UVpd/btsHa1YDJa1/JUDxk7c1PpIHknqx4gV3HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7UVpd%2FbtsHa1YDJa1%2FJUDxk7c1PpIHknqx4gV3HK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Route53 대시보드 - 호스팅 영역&quot; loading=&quot;lazy&quot; width=&quot;2398&quot; height=&quot;1302&quot; data-origin-width=&quot;2398&quot; data-origin-height=&quot;1302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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: #0d0d0d; text-align: start;&quot;&gt;호스팅 영역(Hosted Zone)은 특정 도메인과 해당 도메인에 대한 DNS 설정을 관리하는 컨테이너 역할을 하는 개념이다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;호스팅 영역을 통해 Amazon Route 53이 도메인 이름에 대한 트래픽을 어떻게 라우팅 할지 결정한다.&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1273&quot; data-origin-height=&quot;1410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH8y52/btsHcrhsQSF/WWS1D5tFqBYenS7BMpW6Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH8y52/btsHcrhsQSF/WWS1D5tFqBYenS7BMpW6Nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH8y52/btsHcrhsQSF/WWS1D5tFqBYenS7BMpW6Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH8y52%2FbtsHcrhsQSF%2FWWS1D5tFqBYenS7BMpW6Nk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Route53 - 호스팅 영역 생성&quot; loading=&quot;lazy&quot; width=&quot;491&quot; height=&quot;1410&quot; data-origin-width=&quot;1273&quot; data-origin-height=&quot;1410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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: #0d0d0d; text-align: start;&quot;&gt;Amazon Route 53의 호스팅 영역을 생성하면 DNS 트래픽을 특정 서버나 리소스로 라우팅 하기 위해 각 도메인에 대한 &lt;b&gt;DNS 레코드&lt;/b&gt;를 추가할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;1209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oZ7ML/btsHbkKnPK1/Lz7QKKWJKPN9EWW6q4tlKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oZ7ML/btsHbkKnPK1/Lz7QKKWJKPN9EWW6q4tlKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oZ7ML/btsHbkKnPK1/Lz7QKKWJKPN9EWW6q4tlKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoZ7ML%2FbtsHbkKnPK1%2FLz7QKKWJKPN9EWW6q4tlKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Route53 - 호스팅 영역 생성 완료&quot; loading=&quot;lazy&quot; width=&quot;2272&quot; height=&quot;1209&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;1209&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;호스팅을 생성하면, 기본적으로 2개의 레코드가 생성된다.&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;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NS 레코드 (Name Server Record)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 도메인 이름의 호스팅 영역을 관리하는 네임서버, 해당 값은 가비아 설정에 사용된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SOA 레코드 (Start of Authority Record)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;해당 호스팅 영역의 시작 지점으로 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;DNS의 모든 호스팅 영역에는 단 하나의 SOA 레코드가 있다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;1473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvpTHC/btsHbieK2kB/pdjJKzooqv4sxfbtQV2bT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvpTHC/btsHbieK2kB/pdjJKzooqv4sxfbtQV2bT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvpTHC/btsHbieK2kB/pdjJKzooqv4sxfbtQV2bT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvpTHC%2FbtsHbieK2kB%2FpdjJKzooqv4sxfbtQV2bT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Route53 - 레코드 생성&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;1473&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;1473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;레코드 유형은 A레코드로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A 레코드&lt;/b&gt;: example.com을 192.168.1.1이라는 IP 주소로 매핑하는 역할을 한다. 따라서 값에 EC2 퍼블릭 IPv4 주소를 입력해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2254&quot; data-origin-height=&quot;1316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIblkQ/btsHdfgx0DK/Y4skbrkqyZHQP1fSPpedL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIblkQ/btsHdfgx0DK/Y4skbrkqyZHQP1fSPpedL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIblkQ/btsHdfgx0DK/Y4skbrkqyZHQP1fSPpedL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIblkQ%2FbtsHdfgx0DK%2FY4skbrkqyZHQP1fSPpedL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Route53 - 레코드 생성 완료&quot; loading=&quot;lazy&quot; width=&quot;2254&quot; height=&quot;1316&quot; data-origin-width=&quot;2254&quot; data-origin-height=&quot;1316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;가비아 설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 가비아에서 도메인을 구매하고 아무 설정을 안 했기 때문에, 도메인에 접속하면 가비아 도메인에 설정된 NS 서버로 IP 매핑이 일어나게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 가비아 홈페이지를 통해 NS서버 설정을 해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 가비아에 접속하여 상단의 서비스 관리를 통해 도메인 관리로 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s3nSC/btsHb8Cux16/Z4fW62nTE7MLo8Z234ZLEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s3nSC/btsHb8Cux16/Z4fW62nTE7MLo8Z234ZLEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s3nSC/btsHb8Cux16/Z4fW62nTE7MLo8Z234ZLEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs3nSC%2FbtsHb8Cux16%2FZ4fW62nTE7MLo8Z234ZLEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;가비아 설정 - 서비스 관리&quot; loading=&quot;lazy&quot; width=&quot;1952&quot; height=&quot;939&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 도메인의 네임서버 설정이 가능한데, 기본적으로 가비아 설정해 놓은 서버로 설정이 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1930&quot; data-origin-height=&quot;1348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWrPA9/btsHa4Vnbcn/WYLrAxdfxCLk8joEWiK3p1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWrPA9/btsHa4Vnbcn/WYLrAxdfxCLk8joEWiK3p1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWrPA9/btsHa4Vnbcn/WYLrAxdfxCLk8joEWiK3p1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWrPA9%2FbtsHa4Vnbcn%2FWYLrAxdfxCLk8joEWiK3p1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;가비아 설정 - 서비스 도메인 설정&quot; loading=&quot;lazy&quot; width=&quot;1930&quot; height=&quot;1348&quot; data-origin-width=&quot;1930&quot; data-origin-height=&quot;1348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;그럼 아래와 같이 네임서버 설정이 가능한데, 아까 Route 53에서 확인한 NS 서버의 4개의 값을 여기에 작성하면 된다.&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;2352&quot; data-origin-height=&quot;1285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vAUPV/btsHcTdJlf7/ggckP3AwbK5WvKckRxd9J1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vAUPV/btsHcTdJlf7/ggckP3AwbK5WvKckRxd9J1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vAUPV/btsHcTdJlf7/ggckP3AwbK5WvKckRxd9J1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvAUPV%2FbtsHcTdJlf7%2FggckP3AwbK5WvKckRxd9J1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;가비아 설정 - 서비스 네임서버 설정&quot; loading=&quot;lazy&quot; width=&quot;2352&quot; height=&quot;1285&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;1285&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;2157&quot; data-origin-height=&quot;663&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DKRhO/btsHa3PF07S/YwzyIjiBYcJOtx5INmet6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DKRhO/btsHa3PF07S/YwzyIjiBYcJOtx5INmet6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DKRhO/btsHa3PF07S/YwzyIjiBYcJOtx5INmet6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDKRhO%2FbtsHa3PF07S%2FYwzyIjiBYcJOtx5INmet6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;가비아 설정 - 서비스 네임서버 설정 완료&quot; loading=&quot;lazy&quot; width=&quot;2157&quot; height=&quot;663&quot; data-origin-width=&quot;2157&quot; data-origin-height=&quot;663&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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: #0d0d0d; text-align: left;&quot;&gt;해당 도메인으로 모든 DNS 쿼리는 Route 53으로 전달된다. 그리고 Route 53에 설정한 A 레코드에 의해 EC2의 IP 주소로 사용자의 트래픽이 라우팅 되며, 정상적으로 서비스를 이용할 수 있게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SSL/TLS 설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연하게도 SSH/TLS 설정도 다시 해 주어야 한다. 나는 Nginx를 사용하였기 때문에 아래와 같은 명령어로 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1714932105542&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker-compose run --rm certbot certonly --webroot --webroot-path=/var/www/certbot -d blogwidget.com&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;이때, 아직 www.blogwidget.com에 대한 설정은 진행하지 않았기 때문에 아래와 같은 문제를 만날 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euUWwM/btsHbmIigYg/bxFw3ykGYZaZHX0WL8J7F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euUWwM/btsHbmIigYg/bxFw3ykGYZaZHX0WL8J7F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euUWwM/btsHbmIigYg/bxFw3ykGYZaZHX0WL8J7F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuUWwM%2FbtsHbmIigYg%2FbxFw3ykGYZaZHX0WL8J7F1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;SSL/TLS 설정 오류&quot; loading=&quot;lazy&quot; width=&quot;1199&quot; height=&quot;358&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, www가 붙은 도메인에 대한 것은 아직 진행하지 않고 SSL/TLS 설정을 진행하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/broyhf/btsHedCNf7V/Q2T5o3E9ETONk3fuibhBcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/broyhf/btsHedCNf7V/Q2T5o3E9ETONk3fuibhBcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/broyhf/btsHedCNf7V/Q2T5o3E9ETONk3fuibhBcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbroyhf%2FbtsHedCNf7V%2FQ2T5o3E9ETONk3fuibhBcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;SSL/TLS 설정&quot; loading=&quot;lazy&quot; width=&quot;1199&quot; height=&quot;302&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 nginx.conf를 아래와 같이 수정해 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1714933042456&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name blogwidget.com www.blogwidget.com; 
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name blogwidget.com www.blogwidget.com;
    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/blogwidget.com/fullchain.pem; 
    ssl_certificate_key /etc/letsencrypt/live/blogwidget.com/privkey.pem; 
    

    location / {
        proxy_pass  http://blogwidget.com:8080;
        proxy_set_header    Host                $http_host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;하위 도메인 설정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;www가 붙지 않는다는 문제가 있어서, 추가로 하위 도메인도 설정을 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정을 정말 간단하게, 앞서 진행했던 과정에서 레코드만 www를 설정하여 추가해 주었고, SSL/TLS 설정을 위와 같이 다시 진행해 주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;659&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJpDE/btsHbnAoswC/KBc28IA15TgywG1D3iXrok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJpDE/btsHbnAoswC/KBc28IA15TgywG1D3iXrok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJpDE/btsHbnAoswC/KBc28IA15TgywG1D3iXrok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJpDE%2FbtsHbnAoswC%2FKBc28IA15TgywG1D3iXrok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;하위 도메인 설정&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;659&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;659&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (68).png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nth4s/btsHbloZFTe/w9qU7ZVVPcmMcKEkDDKVX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nth4s/btsHbloZFTe/w9qU7ZVVPcmMcKEkDDKVX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nth4s/btsHbloZFTe/w9qU7ZVVPcmMcKEkDDKVX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNth4s%2FbtsHbloZFTe%2Fw9qU7ZVVPcmMcKEkDDKVX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;도메인 적용 완료&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;545&quot; data-filename=&quot;Untitled (68).png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;545&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/89</guid>
      <comments>https://olrlobt.tistory.com/89#entry89comment</comments>
      <pubDate>Mon, 6 May 2024 03:10:50 +0900</pubDate>
    </item>
    <item>
      <title>[CS] Local cahe, Remote cache의 차이점</title>
      <link>https://olrlobt.tistory.com/88</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cache&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;캐시(Cache)는 컴퓨터 시스템에서 고속 데이터 저장소 역할을 하며, 자주 사용되는 데이터나 명령어를 일시적으로 저장하여 빠른 접근을 가능하게 한다. 캐시는 주로 데이터 접근 시간을 줄이고, 전체 시스템의 효율성을 높이기 위해 사용된다. 이는 프로세서가 데이터를 필요로 할 때마다 주기억장치(예: 하드 드라이브, SSD)에서 데이터를 가져오는 데 걸리는 시간을 단축시키는 데 도움을 준다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;성능 향상&lt;/b&gt;: 캐시는 접근 속도가 빠른 메모리 유형이므로, 데이터를 캐시에서 빠르게 읽어 성능을 크게 향상시킨다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효율적인 리소스 사용&lt;/b&gt;: 자주 사용되는 데이터를 캐시에 저장함으로써, CPU나 서버는 더 중요한 작업에 더 많은 자원을 할애할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대역폭 사용 감소&lt;/b&gt;: 네트워크 기반의 캐시 사용은 네트워크 트래픽과 관련된 비용을 줄이고, 네트워크 대역폭의 부하를 감소시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Local Cache&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Local Cache는 서버마다 각 캐시 서버를 두고 따로 저장하는 전략이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8ezem/btsGZmVsHor/kyLsk2zCIMc4UmR7r51wQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8ezem/btsGZmVsHor/kyLsk2zCIMc4UmR7r51wQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8ezem/btsGZmVsHor/kyLsk2zCIMc4UmR7r51wQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8ezem%2FbtsGZmVsHor%2FkyLsk2zCIMc4UmR7r51wQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Local cache&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;484&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점 :&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;고속 접근&lt;/b&gt;: 데이터를 사용자의 장치나 서버 내부에 저장하기 때문에, 매우 빠른 데이터 접근 속도를 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 의존성 감소&lt;/b&gt;: 네트워크 연결이 필요 없거나 연결 상태가 좋지 않은 환경에서도 데이터 접근이 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개별 최적화&lt;/b&gt;: 사용자 또는 특정 애플리케이션에 맞춘 개별적인 데이터 관리와 최적화가 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점 :&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 일관성&lt;/b&gt; : 각 서버 간의 데이터가 불일치하는 문제가 발생할 수 있다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;다른 서버에서 데이터가 변경되어도 해당 변경 사항이 다른 서버의 캐시에 자동으로 반영되지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스케일링 제한 &lt;/b&gt;: 해당 서버나 장치의 리소스에 제한되어 있기 때문에, 시스템이 확장될 때 캐시 크기 또는 성능이 그 한계에 도달할 수 있다. 이는 전체 시스템의 확장성에 제약을 줄 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리소스 사용의 비효율&lt;/b&gt; : 각 서버가 동일한 데이터의 복사본을 로컬 캐시로 유지하게 되면, 중복 데이터 저장으로 인한 메모리 사용이 비효율적일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Remote Cache&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Remote Cache는 로컬 시스템 외부, 즉 원격 서버나 클라우드 기반 인프라에 데이터를 저장하는 캐시 유형이다. 이 캐시는 중앙에서 데이터를 관리하며, 여러 클라이언트 또는 애플리케이션이 공통된 데이터에 접근할 수 있도록 한다.&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EkbN3/btsGZnfJ7Hh/AgwNAMjsgGjLjucHy1LHK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EkbN3/btsGZnfJ7Hh/AgwNAMjsgGjLjucHy1LHK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EkbN3/btsGZnfJ7Hh/AgwNAMjsgGjLjucHy1LHK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEkbN3%2FbtsGZnfJ7Hh%2FAgwNAMjsgGjLjucHy1LHK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;remote cache&quot; loading=&quot;lazy&quot; width=&quot;573&quot; height=&quot;268&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점 :&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;중앙집중적 관리&lt;/b&gt;: 데이터를 원격 서버에 저장하고 관리함으로써 데이터 일관성과 보안을 향상시킬 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장성&lt;/b&gt;: 클라우드 기반의 캐시는 높은 확장성을 제공하며, 사용자 수나 데이터 양의 증가에 유연하게 대응할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가용성 및 내구성&lt;/b&gt;: 데이터를 여러 위치에 복제함으로써 높은 가용성을 제공하고, 장애 발생 시 데이터 복구가 용이하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;단점 :&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;네트워크 지연&lt;/b&gt; : Remote cache는 네트워크를 통해 데이터에 접근하기 때문에, 네트워크 지연이 성능에 큰 영향을 미칠 수 있다. 특히 대량의 데이터를 자주 접근하는 애플리케이션의 경우, 이러한 지연이 눈에 띄게 성능 저하를 일으킬 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;중앙 집중적 장애 지점: &lt;/b&gt;모든 데이터가 중앙 서버에 위치하는 Remote cache는, 해당 서버에 문제가 발생했을 때 전체 시스템에 영향을 줄 수 있는 단일 장애 지점이 될 수 있다. 이는 시스템의 가용성과 신뢰성을 저하시킬 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안 문제: &lt;/b&gt;데이터가 네트워크를 통해 전송되기 때문에, Remote cache는 데이터 유출 또는 무단 접근과 같은 보안 위험에 더 취약할 수 있다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;각 캐시 유형의 이러한 단점을 고려하여 적절한 캐싱 전략을 선택하고, 필요에 따라 캐시의 구성을 조정하는 것이 중요하다. 예를 들어, 데이터 일관성이 중요한 경우, 적절한 동기화 메커니즘을 추가하거나, 시스템의 확장성을 고려하여 Remote cache의 활용을 검토할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/88</guid>
      <comments>https://olrlobt.tistory.com/88#entry88comment</comments>
      <pubDate>Sun, 28 Apr 2024 00:40:27 +0900</pubDate>
    </item>
    <item>
      <title>[추천 알고리즘] Spotify ANNOY (Approximate Nearest Neighbors Oh Yeah) 알고리즘이란?</title>
      <link>https://olrlobt.tistory.com/87</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;추천 알고리즘&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 진행한 프로젝트의 도메인은 '빅데이터 추천'이었다. 나는 추천 시스템에 관한 지식이 전혀 없는 상태였기에 여러 가지 학습한 내용을 정리하고, 내가 사용한 추천 알고리즘에 대해 작성하려 한다.&lt;/p&gt;
&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;사용자가 작성한 일기(글)를 기반으로 비슷한 내용의 다른 일기를 K개 추천&lt;/b&gt;하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 어떻게 코드로 사용자가 작성한 일기와 비슷한 일기들을 찾을 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 숫자와 숫자의 유사도를 비교한다고 생각해 보자. 우리는 숫자와 숫자의 유사도를 비교할 때, 단순히 비교하는 숫자들의 거리차이가 가까울수록 유사도가 높다고 말한다. 이는 단어나 글에서도 똑같이 적용될 수 있는데, 단어를 컴퓨터가 이해할 수 있도록 숫자 배열로 표현한 임베딩 벡터값을 이용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단어와 단어 사이의 유사도를 찾는 것에 임베딩 벡터를 이용하는 방법은 자연어 처리(NLP) 분야에서 매우 널리 사용되는 기법이다. 그리고 마찬가지로, 글과 글 사이의 유사도를 찾는 것에도 똑같이 임베딩 벡터를 이용할 수 있다. 단어를 임베딩 벡터로 변환하기 위해서는 대표적으로 구글에서 개발한 Word2 Vec 모델이 있고, 글을 임베딩 벡터로 변환하기 위해서는 대표적으로 Doc2 Vec 모델이 있다. 하지만, Doc2 Vec 모델은 학습 과정에 따라 반의어의 임베딩 벡터 간의 거리가 가까워지는 단점이 있다. 따라서 나는 Bert모델을 이용하여 임베딩 벡터 값을 구해 반의어를 구분할 수 있게 하였고, 이 중에서도 KoSBert모델을 이용하여 한국어에서 임베딩 벡터 값이 더 정확히 나오도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cosine Similarity&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 앞에서 설명한 방법으로, 사용자가 작성한 일기의 임베딩 벡터와 다른 일기의 임베딩 벡터 사이의 거리를 구하면, 두 글의 유사도를 측정할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코사인 유사도(Cosine Similarity)는 두 벡터 간의 유사도를 측정하는 방법 중 하나로, 벡터의 크기가 아닌 방향에 초점을 맞추어 계산하는 방법이다. 이 역시 벡터 간 유사도를 비교할 때 널리 사용되는 기법이며, 이 전 프로젝트 중에서 사용해 본 경험이 있어서 코사인 유사도로 유사도를 측정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;KNN (K-Nearest Neighbors)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 앞서 설명한 방법으로, 사용자가 작성한 일기를 벡터로 변환하고 코사인 유사도를 이용해서 유사도를 구하면 되겠다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 KNN 알고리즘이라고 하는데, KNN 알고리즘은 주어진 벡터에서 가장 가까운 벡터 K개의 이웃을 찾는 알고리즘이다. 하지만 이 알고리즘의 경우, 모든 일기 벡터에 대해 거리를 계산하는 연산이 수행되기 때문에 비교할 일기의 수가 100만 개라면, 100만 번의 연산이 필요하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직관적이기 때문에 매우 정확하다는 특징이 있지만, 데이터가 많아질수록 응답 시간이 느려진다는 단점이 너무 치명적이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ANN (Approximate Nearest Neighbors)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이런 응답시간이 느린 것을 개선하기 위해서 최근접 이웃 탐색 ANN 알고리즘을 사용하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;ANN 알고리즘은 대규모 데이터셋에서 KNN 알고리즘의 계산 부담을 줄이기 위해 개발된 방법으로, 완벽한 정확도를 포기하고 근사치를 찾아냄으로써 훨씬 빠르게 결과를 도출하는 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;ANN 알고리즘의 정확도는 ANN에서 사용하는 알고리즘의 종류의 따라, 또한, 파라미터를 어떻게 설정하는지에 따라 달라질 수 있다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;대표적으로는 ANNOY 알고리즘과 HNSW 알고리즘이 있으며, 두 방식을 비교해 보기로 하였다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4084&quot; data-origin-height=&quot;1550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eEkHXM/btsGNcrDvF6/1vdvooU3FEqzJnMEv0FTD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eEkHXM/btsGNcrDvF6/1vdvooU3FEqzJnMEv0FTD1/img.png&quot; data-alt=&quot;Word2Vec을 이용한 Brute Force 방식보다, Annoy 알고리즘의 성능이 우수함&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eEkHXM/btsGNcrDvF6/1vdvooU3FEqzJnMEv0FTD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeEkHXM%2FbtsGNcrDvF6%2F1vdvooU3FEqzJnMEv0FTD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;Word2Vec을 이용한 Brute Force 방식보다&amp;amp;#44; Annoy 알고리즘의 성능이 우수함&quot; loading=&quot;lazy&quot; width=&quot;4084&quot; height=&quot;1550&quot; data-origin-width=&quot;4084&quot; data-origin-height=&quot;1550&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Word2Vec을 이용한 Brute Force 방식보다, Annoy 알고리즘의 성능이 우수함&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;ANNOY (&lt;/span&gt;&lt;/span&gt;Approximate Nearest Neighbors Oh-Yeah&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ANNOY 알고리즘은 ANN을 대표하는 알고리즘 중 하나로, Spotify에서 개발한 알고리즘이다. 이 역시 ANN 알고리즘의 특징인 완전한 정확도를 포기하고, 매우 빠른 성능을 제공하는 알고리즘으로 추천 시스템에서 많이 활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 고차원 공간에서 최근접 이웃 검색을 위해 설계되었으며, 트리 구조를 사용해서 빠른 속도를 제공한다. 정확도를 높이기 위해서 여러 개의 트리를 구성하는 방법을 사용하며, 이 과정을 병렬처리 함으로 성능을 챙겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup-25-2048.webp&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bssDzq/btsGNGls2La/jYlphefjQIOM0EZXCCak4K/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bssDzq/btsGNGls2La/jYlphefjQIOM0EZXCCak4K/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bssDzq/btsGNGls2La/jYlphefjQIOM0EZXCCak4K/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbssDzq%2FbtsGNGls2La%2FjYlphefjQIOM0EZXCCak4K%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;ANNOY 알고리즘 성능&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;1536&quot; data-filename=&quot;approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup-25-2048.webp&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ANNOY 알고리즘 작동 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ANNOY의 작동 방식을 내 프로젝트의 상황에 적용해서 설명하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항을 다시 한번 가정하자면, 사용자 일기 데이터가 주어지면 이와 비슷한 다른 일기들을 100개 찾아내야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bROVFJ/btsGOuLB5wC/JFpWwM3jWREnRdkbdaz7Z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bROVFJ/btsGOuLB5wC/JFpWwM3jWREnRdkbdaz7Z0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bROVFJ/btsGOuLB5wC/JFpWwM3jWREnRdkbdaz7Z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbROVFJ%2FbtsGOuLB5wC%2FJFpWwM3jWREnRdkbdaz7Z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;ANNOY 알고리즘 작동방식 1 - 초기 벡터 표현&quot; loading=&quot;lazy&quot; width=&quot;457&quot; height=&quot;891&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 모든 일기 데이터의 임베딩 벡터들 사이의 거리는 한 평면 위에 위 그림과 같이 표현될 수 있다. 이 평면에서의 점들은 각각 일기 데이터를 표현하며, 각각 벡터 사이의 거리는 유사도를 의미한다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSvpKW/btsGL24vccs/Zd0lttfQOQ1o73WN0NK30K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSvpKW/btsGL24vccs/Zd0lttfQOQ1o73WN0NK30K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSvpKW/btsGL24vccs/Zd0lttfQOQ1o73WN0NK30K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSvpKW%2FbtsGL24vccs%2FZd0lttfQOQ1o73WN0NK30K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;ANNOY 알고리즘 작동방식 2 - 초평면 분할&quot; loading=&quot;lazy&quot; width=&quot;487&quot; height=&quot;891&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;(Hyperplane)으로 나눈다. 그림을 자세히 보면, 회색 선으로 연결된 두 점이 선택되었고, 이를 두 공간으로 분리한 것을 알 수 있다.&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lRnsc/btsGNPCzbAM/yrf8u6DZrrz5XFTMke0ZR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lRnsc/btsGNPCzbAM/yrf8u6DZrrz5XFTMke0ZR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lRnsc/btsGNPCzbAM/yrf8u6DZrrz5XFTMke0ZR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlRnsc%2FbtsGNPCzbAM%2Fyrf8u6DZrrz5XFTMke0ZR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;ANNOY 알고리즘 작동방식 3 - 초평면 분할2&quot; loading=&quot;lazy&quot; width=&quot;510&quot; height=&quot;890&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 분리된 각각의 공간 안에서, 또 임의의 두 점을 선택하여 공간을 두 초평면으로 나눈다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1471&quot; data-origin-height=&quot;569&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qX3eS/btsGLtg3P96/kDLc22ZQ1hXjQqKxbF1V2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qX3eS/btsGLtg3P96/kDLc22ZQ1hXjQqKxbF1V2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qX3eS/btsGLtg3P96/kDLc22ZQ1hXjQqKxbF1V2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqX3eS%2FbtsGLtg3P96%2FkDLc22ZQ1hXjQqKxbF1V2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;ANNOY 알고리즘 작동방식 4 - 초평면 분할 최종&quot; loading=&quot;lazy&quot; width=&quot;1471&quot; height=&quot;569&quot; data-origin-width=&quot;1471&quot; data-origin-height=&quot;569&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나누어진 공간을, 또 임의의 두 점을 선택하여 나눈다. 이때, 각 공간 안의 벡터들의 수가 설정한 K값. 여기서는 100개 미만이 될 때까지 이 과정을 반복하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 최종적으로는 오른쪽 그림과 같은 공간이 완성되는데, 이때 같은 색으로 칠해진 공간 안의 벡터는 유사한 벡터로 간주한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 입력받은 일기 데이터가 빨간 공간 안에 속한다면, 빨간 공간 안에 있는 모든 벡터들을 반환하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup-33-2048.webp&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7XCWK/btsGMULxVmk/7l3BjHXT0NTEO65BzjRWc1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7XCWK/btsGMULxVmk/7l3BjHXT0NTEO65BzjRWc1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7XCWK/btsGMULxVmk/7l3BjHXT0NTEO65BzjRWc1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7XCWK%2FbtsGMULxVmk%2F7l3BjHXT0NTEO65BzjRWc1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;ANNOY 알고리즘 작동방식 5 - 트리 구성&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;1536&quot; data-filename=&quot;approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup-33-2048.webp&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 각 공간을 트리의 노드로 저장하면 위와 같은 그림으로 형성되게 되는 것이고, 각가지의 마지막 노드는 최종 그림에서 한 공간을 의미한다. 즉, 마지막 노드의 수가 K미만인 트리로 형성되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 ANNOY에서 알고리즘에 사용할 Tree를 구성하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 여기서 한 가지 의문이 생긴다. ANNOY에서는 정확도가 아주 조금 떨어진다고 했는데, 앞선 공간 형태의 그림을 보면 벡터 사이의 거리가 너무 멀거나, 한 공간 안에 주어진 K만큼의 데이터가 없을 수도 있지 않나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup-44-2048.webp&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjj3ZP/btsGNhl32Ih/nK4EZLzk7E2jtrQLk24O61/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjj3ZP/btsGNhl32Ih/nK4EZLzk7E2jtrQLk24O61/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjj3ZP/btsGNhl32Ih/nK4EZLzk7E2jtrQLk24O61/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjj3ZP%2FbtsGNhl32Ih%2FnK4EZLzk7E2jtrQLk24O61%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;ANNOY 알고리즘 작동방식 6 - 다중 트리 구성&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;1536&quot; data-filename=&quot;approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup-44-2048.webp&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ANNOY에서는 이를 해결하기 위해 여러 개의 Tree를 구축하는 방법을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 Tree를 구축하는 과정은 거의 랜덤적으로 수행되기 때문에 겹치지 않으며, 동일한 방식으로 수행하면 각각 다른 결과가 나오게 된다. 위 사진처럼, 빨간 X표시의 벡터와 유사한 벡터는 각각 색칠된 공간 안에 있다는 뜻이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 여러 개로 구축된 Tree 안에서, 입력된 임베딩 벡터와 같은 공간에 있는 모든 벡터 데이터를 뽑아내서 합치는 작업을 한다. 이때, 중복된 데이터는 제거하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup-50-2048.webp&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pnxM2/btsGL1q0iri/3ZmoiP4sh2qZuZNP7PZZ61/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pnxM2/btsGL1q0iri/3ZmoiP4sh2qZuZNP7PZZ61/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pnxM2/btsGL1q0iri/3ZmoiP4sh2qZuZNP7PZZ61/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpnxM2%2FbtsGL1q0iri%2F3ZmoiP4sh2qZuZNP7PZZ61%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;ANNOY 알고리즘 작동방식 7 - 최종 시각 자료&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;1536&quot; data-filename=&quot;approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup-50-2048.webp&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ANNOY 단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, ANNOY에는 치명적인 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로, 구축된 Tree 구조에서 데이터를 추가하거나 삭제하고 싶다면 Tree를 다시 구축해 주어야 한다는 것이다. 이 단점은 정말 치명적인데, 만약 우리 프로젝트에서 사용자가 작성한 일기가 1시간 안에 다른 사용자에게 추천되기를 원한다면, 1시간 안에 Tree를 다시 구축해주어야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1713535284142&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 - spotify/annoy: Approximate Nearest Neighbors in C++/Python optimized for memory usage and loading/saving to disk&quot; data-og-description=&quot;Approximate Nearest Neighbors in C++/Python optimized for memory usage and loading/saving to disk - spotify/annoy&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/spotify/annoy&quot; data-og-url=&quot;https://github.com/spotify/annoy&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/d1Qpq8/hyVPQH4Zv9/lMtleRMqqGav7MqyzZSDWk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/spotify/annoy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/spotify/annoy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/d1Qpq8/hyVPQH4Zv9/lMtleRMqqGav7MqyzZSDWk/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 - spotify/annoy: Approximate Nearest Neighbors in C++/Python optimized for memory usage and loading/saving to disk&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Approximate Nearest Neighbors in C++/Python optimized for memory usage and loading/saving to disk - spotify/annoy&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;figure id=&quot;og_1713535278245&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;Approximate nearest neighbor methods and vector models &amp;ndash; NYC ML meetup&quot; data-og-description=&quot;Approximate nearest neighbor methods and vector models &amp;ndash; NYC ML meetup - Download as a PDF or view online for free&quot; data-og-host=&quot;www.slideshare.net&quot; data-og-source-url=&quot;https://www.slideshare.net/erikbern/approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup&quot; data-og-url=&quot;https://www.slideshare.net/erikbern/approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cbrjgV/hyVPNxQbKC/CtdIVkhaXwm23QK2CewvEk/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480,https://scrap.kakaocdn.net/dn/SYk0E/hyVPVigWp6/vlwukKdkjzwrAKeU3a6NSK/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480,https://scrap.kakaocdn.net/dn/by4x84/hyVSUIPAJe/05VzbGrIVDkgKyVg1cql6k/img.jpg?width=320&amp;amp;height=240&amp;amp;face=0_0_320_240&quot;&gt;&lt;a href=&quot;https://www.slideshare.net/erikbern/approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.slideshare.net/erikbern/approximate-nearest-neighbor-methods-and-vector-models-nyc-ml-meetup&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cbrjgV/hyVPNxQbKC/CtdIVkhaXwm23QK2CewvEk/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480,https://scrap.kakaocdn.net/dn/SYk0E/hyVPVigWp6/vlwukKdkjzwrAKeU3a6NSK/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480,https://scrap.kakaocdn.net/dn/by4x84/hyVSUIPAJe/05VzbGrIVDkgKyVg1cql6k/img.jpg?width=320&amp;amp;height=240&amp;amp;face=0_0_320_240');&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;Approximate nearest neighbor methods and vector models &amp;ndash; NYC ML meetup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Approximate nearest neighbor methods and vector models &amp;ndash; NYC ML meetup - Download as a PDF or view online for free&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.slideshare.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HNSW(Hierarchical Navigable Small World)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 ANNOY는 데이터를 추가하거나 삭제하기에 너무 불리하기 때문에 HNSW 알고리즘을 추가적으로 조사해 보았다. HNSW 알고리즘은 근사 최근접 이웃 탐색에 사용되는 그래프 기반 알고리즘으로, 이 역시 고차원 데이터에서 뛰어난 성능을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;케빈 베이컨의 6단계 법칙&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HNSW 알고리즘은 재밌는 게, &lt;b&gt;'케빈 베이컨의 6단계 법칙'&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;케빈 베이컨의 6단계 법칙이란, 간단히 말해 세상 모든 사람들을 건너 건너 6단계만 건넌다면 모두 아는 사람들이라는 간단한 이론이다. 예를 들어, 나무위키에서는 케빈 베이컨의 6단계 법칙을 아래와 같이 예를 들고 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #212529; text-align: start;&quot; data-v-01e16484=&quot;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot; data-v-01e16484=&quot;&quot;&gt;
&lt;div data-v-01e16484=&quot;&quot;&gt;평범한 정치외교학과 대학생&lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot; data-v-01e16484=&quot;&quot;&gt;
&lt;div data-v-01e16484=&quot;&quot;&gt;학회에서 만난 서울대 정치외교학과 친구&lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot; data-v-01e16484=&quot;&quot;&gt;
&lt;div data-v-01e16484=&quot;&quot;&gt;서울대 학생의 지도교수인 서울대 정치외교학과 교수&lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot; data-v-01e16484=&quot;&quot;&gt;
&lt;div data-v-01e16484=&quot;&quot;&gt;서울대 정치외교학과 교수와 친한 대한민국 외교부 장관&lt;/div&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot; data-v-01e16484=&quot;&quot;&gt;
&lt;div data-v-01e16484=&quot;&quot;&gt;대한민국 외교부 장관과 친분이 있는 미국 대통령&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Small world network&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;screenshot12.png&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AjH8l/btsGNdqp8mr/ksAp3XEEwNw9BNeQwYKqSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AjH8l/btsGNdqp8mr/ksAp3XEEwNw9BNeQwYKqSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AjH8l/btsGNdqp8mr/ksAp3XEEwNw9BNeQwYKqSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAjH8l%2FbtsGNdqp8mr%2FksAp3XEEwNw9BNeQwYKqSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;HNSW 알고리즘 - small world&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;212&quot; data-filename=&quot;screenshot12.png&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Small world network는 전체 네트워크가 거대하더라도 전체가 서로 가깝게 연결될 수 있다는 이론이다. 예를 들어 맨 왼쪽 그림과 같이 근접한 이웃끼리 연결된 네트워크가 있다고 하자. 이 경우에 몇 개의 선만 랜덤 하게 연결을 해 주면, 평균적으로 다른 네트워크로 이동하는 경로가 줄어들게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;e3833bc4ba83fd5871cd3d8a67529772e618c2a1-512x323.webp&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;681&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/McLNC/btsGNAyVFiu/kIQjpI0ykj0hl8YRIbBPkK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/McLNC/btsGNAyVFiu/kIQjpI0ykj0hl8YRIbBPkK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/McLNC/btsGNAyVFiu/kIQjpI0ykj0hl8YRIbBPkK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMcLNC%2FbtsGNAyVFiu%2FkIQjpI0ykj0hl8YRIbBPkK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;HNSW 알고리즘 - small world 활용&quot; loading=&quot;lazy&quot; width=&quot;455&quot; height=&quot;681&quot; data-filename=&quot;e3833bc4ba83fd5871cd3d8a67529772e618c2a1-512x323.webp&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;681&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;그렇다면 위 그림에서 0 노드에서 A 노드로 가기까지, 상당히 많은 노드들을 거쳐서 돌아가야 하는 것을 볼 수 있다. 하지만 여기서 Small world network의 방식대로 랜덤 한 선을 그어준다면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;681&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3oy6Z/btsGMR2mrIp/1xqwulFh7KtV9AF0lYjPKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3oy6Z/btsGMR2mrIp/1xqwulFh7KtV9AF0lYjPKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3oy6Z/btsGMR2mrIp/1xqwulFh7KtV9AF0lYjPKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3oy6Z%2FbtsGMR2mrIp%2F1xqwulFh7KtV9AF0lYjPKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;HNSW 알고리즘 - small world 활용2&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;681&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;681&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;HNSW에서는 이렇게 랜덤 하게 연결하는 방식을 Layer를 통해 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HNSW 계층&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ec6d93a3a16daebd5225da448cb4181643f86b2b-396x512.webp&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwWRiG/btsGMTFSJwx/4MnLRKHzKPVjrs08yiotU1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwWRiG/btsGMTFSJwx/4MnLRKHzKPVjrs08yiotU1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwWRiG/btsGMTFSJwx/4MnLRKHzKPVjrs08yiotU1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwWRiG%2FbtsGMTFSJwx%2F4MnLRKHzKPVjrs08yiotU1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;HNSW 알고리즘 - 계층 구조 동작방식&quot; loading=&quot;lazy&quot; width=&quot;403&quot; height=&quot;1071&quot; data-filename=&quot;ec6d93a3a16daebd5225da448cb4181643f86b2b-396x512.webp&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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: #0d0d0d; text-align: start;&quot;&gt;HNSW 알고리즘은 위 사진처럼 여러 계층(layer)의 그래프로 구성된다. 맨 아래의 계층에는 모든 벡터 데이터들이 표시되고, 한 단계 위의 계층에는 모든 데이터가 확률적으로 올라가게 된다. 예를 들어 확률 p가 1/2라면, 모든 데이터들의 1/2 정도가 상위 계층에 표기되는 것이다.&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: #0d0d0d; text-align: start;&quot;&gt;이때, 상위 계층에 살아남은 노드들을 연결하고, 이 과정을 Small world network에서 랜덤 한 연결을 하는 과정이라고 이해하면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HNSW 알고리즘은 이런 계층 구조를 이용하여, 단 몇 번 만에 유사한 벡터들을 빠르고 정확하게 도출할 수 있게 한다. 또한 ANNOY 알고리즘과 다르게, 한 번 구성된 계층 안에서 데이터의 추가와 삭제가 발생해도, 동적 업데이트를 지원하도록 설계되었기 때문에 기존 검색 성능에 미치는 영향을 최소화하면서도 계층적 구조를 효과적으로 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HNSW 단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HNSW를 살펴보다 보면 성능적으로도 우수하고, 데이터의 추가 삭제도 쉽다는 것이 상당히 매력적으로 느껴진다. 하지만 HNSW에도 치명적인 단점이 있는데, 바로 파라미터 튜닝이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HNSW에는 파라미터 튜닝이 필수적으로 필요하고, 이 파라미터 튜닝 정도에 따라, 성능과 정확성 면에서 많이 차이가 나게 된다. 설정해야 하는 파라미터 값들은 아래와 같으며, 모든 값들은 서비스에 따라 다르기 때문에 직접 시도를 해 보면서 조정하는 것이 가장 좋다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;maxLayer&lt;/b&gt; (최대 계층 수): HNSW 그래프의 계층 수. 계층 수가 많으면 더 많은 수준의 추상화를 제공하며, 검색 속도를 향상할 수 있지만, 인덱스 구축 시간과 메모리 사용량이 증가할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;M&lt;/b&gt; (각 노드의 이웃 수): 이 파라미터는 각 노드가 유지할 이웃의 수. M이 크면 더 많은 이웃 정보를 유지하여 검색 정확도가 높아질 수 있으나, 메모리 사용량과 인덱스 구축 시간이 증가한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;efConstruction&lt;/b&gt; (구축 중 탐색 크기): 이 값은 인덱싱 동안 탐색할 노드의 수. 더 큰 값은 인덱스 구축 시 더 나은 품질을 제공하지만, 구축 시간이 길어질 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;efSearch&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 진행하는 프로젝트 기능의 요구사항은 데이터 추가와 삭제가 쉽게 일어나는 구조로 되어 있다. 따라서 HNSW로 진행하는 것이 정확도와 기술적인 측면에서 적합하다고 생각된다. 하지만, 프로젝트 자체가 짧은 6주라는 시간 안에 많은 것을 구현해야 하고, 이미 기획과 설계에서 많은 시간을 쓴 상태였다. 따라서 파라미터 튜닝을 할 시간을 확보하지 못했고, 파라미터 튜닝에 따라 성능과 정확성 차이가 많이 나는 HNSW 대신, 바로 사용이 가능한 ANNOY를 사용하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ANNOY를 사용했을 때의 가장 큰 문제는, 새로운 데이터가 추가, 삭제됨에 따라 새롭게 Tree를 구성해야 한다는 것이다. 만약 이 과정에서 데이터가 많아진다면 Build 시간 자체가 오래 걸리게 되어, 새로운 데이터 추천이 이루어지기까지의 시간이 오래 걸리게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에 대해 여러 가지 레퍼런스를 찾아본 결과, NHN Cloud의 NHN FORWARD22에서 ANNOY 알고리즘을 사용한 경험을 발표한 자료를 찾을 수 있었다. 이 발표에서 NHN Cloud는 ANNOY 알고리즘을 사용하여 추천시스템을 만들었고, 2시간마다 Tree를 새롭게 Build 하는 작업을 통해 새로운 데이터를 추가하였다고 한다. 또한, 200만 건에서 250만 건 이상의 데이터를 처리할 때 1시간 정도의 빌드시간이 걸렸고, 이 빌드시간에 대한 이슈를 해결하기 위해 HNSW를 도입하게 되었다고 설명한다.&lt;/p&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시간 간격으로 새롭게 Tree를 구성해 주는 스케줄러를 구성함으로써, 데이터의 추가와 삭제에서의 문제를 해결했다. 또한, 우리 프로젝트도 앞선 사례를 바탕으로 서비스가 운영됨에 따라 데이터가 많이 쌓여서 Tree를 구성하는 시간이 오래 걸리게 되는 불편함을 겪게 된다면, HNSW 알고리즘을 도입하여 시스템 성능을 개선하기로 결정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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=hCqF4tDPNBw&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/x4Wfn/hyVSUB23Ax/GSpa8sMNaOFnbkMRe8E1Z0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=984_182_1108_318&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;[NHN FORWARD 22] 대충? 거의 정확하다! 벡터 검색 엔진에 ANN HNSW 알고리즘 도입기 (feat. SWIG Golang)&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/hCqF4tDPNBw&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;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/87</guid>
      <comments>https://olrlobt.tistory.com/87#entry87comment</comments>
      <pubDate>Sat, 20 Apr 2024 00:10:49 +0900</pubDate>
    </item>
    <item>
      <title>[Error] AWS tensorflow==2.11.*, tensorflow-intel==2.11.* 의존성 설치 에러</title>
      <link>https://olrlobt.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fast API를 EC2에 배포하려 Docker 이미지를 빌드하는 도중 아래와 같은 에러가 발생했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0.webp&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v25Pp/btsGp2w51ML/KbrHWPiSONUfZmrjba9mpk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v25Pp/btsGp2w51ML/KbrHWPiSONUfZmrjba9mpk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v25Pp/btsGp2w51ML/KbrHWPiSONUfZmrjba9mpk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv25Pp%2FbtsGp2w51ML%2FKbrHWPiSONUfZmrjba9mpk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;tensorflow-intel==2.11.* 버전을 못 찾는 에러&quot; loading=&quot;lazy&quot; width=&quot;1378&quot; height=&quot;312&quot; data-filename=&quot;0.webp&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정전 내가 작성한 Dockerfile은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1711813542786&quot; class=&quot;dockerfile&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;FROM python:3.9

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [&quot;uvicorn&quot;, &quot;main:app&quot;, &quot;--host&quot;, &quot;0.0.0.0&quot;, &quot;--port&quot;, &quot;8000&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 data-ke-size=&quot;size16&quot;&gt;TensorFlow는 Python 3.6~3.9에서 지원하기 때문에, 파이썬을 다운그레이드해 주었고, Fast API 프로젝트의 의존성을 requirements.txt로 작성하여 Dockerfile에서 pip install을 해 주는 과정이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 requirements.txt 안에 의존성에는 tensorflow 관련 의존성들의 버전이 설정되어 있었는데, 이 부분을 설치해 주는 과정에서 버전에 따른 오류가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lby36/btsGreiIyNe/qAt8AQSPmR28man2y8kL3k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lby36/btsGreiIyNe/qAt8AQSPmR28man2y8kL3k/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lby36/btsGreiIyNe/qAt8AQSPmR28man2y8kL3k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLby36%2FbtsGreiIyNe%2FqAt8AQSPmR28man2y8kL3k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;requirements.txt안에 있는 tensorflow 의존성&quot; loading=&quot;lazy&quot; width=&quot;443&quot; height=&quot;289&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;Thensorflow를 Dockerfile 내에서 환경과 버전에 맞게 직접 설치를 해주는 방법으로 해결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Tensorflow 공식 사이트에서 사용 버전과 환경에 맞는 Pacakage Location을 찾는다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.tensorflow.org/install/pip#software_requirements&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.tensorflow.org/install/pip#software_requirements&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711813813619&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;pip로 TensorFlow 설치&quot; data-og-description=&quot;이 페이지는 Cloud Translation API를 통해 번역되었습니다. pip로 TensorFlow 설치 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 가이드는 TensorFlow의 최신 안정 &quot; data-og-host=&quot;www.tensorflow.org&quot; data-og-source-url=&quot;https://www.tensorflow.org/install/pip#software_requirements&quot; data-og-url=&quot;https://www.tensorflow.org/install/pip?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nSR5f/hyVGFTmFyc/aCeoFfmvHujrciRNpivmU0/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675&quot;&gt;&lt;a href=&quot;https://www.tensorflow.org/install/pip#software_requirements&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.tensorflow.org/install/pip#software_requirements&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nSR5f/hyVGFTmFyc/aCeoFfmvHujrciRNpivmU0/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675');&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;pip로 TensorFlow 설치&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 페이지는 Cloud Translation API를 통해 번역되었습니다. pip로 TensorFlow 설치 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 가이드는 TensorFlow의 최신 안정&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.tensorflow.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;847&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FBdbe/btsGqJQ6g3K/EMOAh5NRhRXvUXpiESWtCK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FBdbe/btsGqJQ6g3K/EMOAh5NRhRXvUXpiESWtCK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FBdbe/btsGqJQ6g3K/EMOAh5NRhRXvUXpiESWtCK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFBdbe%2FbtsGqJQ6g3K%2FEMOAh5NRhRXvUXpiESWtCK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;tensorflow공식 사이트 pacakge location&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;386&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;847&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 Python 3.9 버전의 CPU-only를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Dockerfile 수정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1711813869451&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM python:3.9

WORKDIR /app

# 필요한 경우 wget 설치
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y wget &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*

# TensorFlow .whl 파일 다운로드 및 설치
RUN wget https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow_cpu-2.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
RUN pip install --no-cache-dir tensorflow_cpu-2.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

# 필요한 경우 나머지 의존성 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [&quot;uvicorn&quot;, &quot;main:app&quot;, &quot;--host&quot;, &quot;0.0.0.0&quot;, &quot;--port&quot;, &quot;8000&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. requirements.txt 안에 의존성에는 tensorflow 관련 의존성 삭제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 다운로드를 해 주었기 때문에 별도로 tensorflow의 의존성을 설치할 필요 없다.&lt;/p&gt;
&lt;p data-ke-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>Error</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/86</guid>
      <comments>https://olrlobt.tistory.com/86#entry86comment</comments>
      <pubDate>Sun, 31 Mar 2024 00:56:47 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] Jenkins 설치하기</title>
      <link>https://olrlobt.tistory.com/85</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CI/CD&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;지속적 통합(Continuous Integration, CI)과 지속적 배포(Continuous Deployment, CD)를 의미한다. CI/CD는&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&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;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;지속적 통합 (CI)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지속적 통합(CI)은 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;자동화된 빌드 및 테스트가 수행된 후, 개발자가 코드 변경 사항을 중앙 리포지토리에 정기적으로 병합하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;DevOps&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;소프트웨어 개발 방식이다. &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&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;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;지속적 배포 (CD)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지속적 배포(CD)는 소프트웨어를 자동으로 고객에게 배포하는 과정이다. CI 과정을 통해 생성된 소프트웨어 빌드 결과물을 자동화된 배포 파이프라인을 통해 테스트, 스테이징, 프로덕션 환경으로 이동된다. CD는 고객에게 더 빠르게 새로운 기능을 제공하고, 소프트웨어 배포 과정에서 발생할 수 있는 인적 오류를 줄이는 데 목적이 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CD는 종종 지속적인 전달(Continuous Delivery)과 지속적인 배포(Continuous Deployment) 두 가지 의미로 혼합하여 쓰인다.&amp;nbsp; 지속적인 전달은 소프트웨어가 언제든지 배포할 수 있게 준비하는 과정을 자동화한 것으로 프로덕션 환경으로의 배포는 사람이 수동으로 결정하고 실행한다. 그리고 지속적인 배포는 지속적인 전달에서 한 단계 더 나아간 것으로, 프로덕션 환경으로의 배포까지 자동화된 것을 의미한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Jenkins&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;젠킨스는 &lt;b&gt;지속적 통합(Continuous Integration, CI)과 지속적 배포(Continuous Deployment, CD)를 위한 오픈 소스 자동화 서버&lt;/b&gt;이다. 코드 변경 사항을 자동으로 빌드, 테스트하고 배포하는 과정을 자동화해 주고, 이를 통해 개발자들은 더 빠르게 피드백을 받을 수 있어서 개발 주기를 단축시키는 중요한 역할을 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;젠킨스는 1,000개 이상의 플러그인을 지원하여 Github, GitLab, Maven 등의 다양한 개발, 테스트, 배포 도구와의 통합을 지원하는 생태계를 가지고 있다. 특히 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;Docker, Kubernetes 같은 컨테이너 오케스트레이션 도구와의 통합을 통해 복잡한 빌드 및 배포 프로세스를 자동화하여 관리할 수 있으며,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;JUnit&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; , &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;Selenium, SonarQube등 다양한 테스트 및 코드 품질 관리 도구와의 통합도 지원하여, 코드 품질 지표를 모니터링하고 코드 품질을 향상할&amp;nbsp;수 있다.&lt;/span&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: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한, 젠킨스의 마스터-슬레이브 아키텍처는 빌드 프로세스의 확장성과 유연성을 제공한다. 예를 들어, 보안상의 이유로 특정 작업을 특정 PC에서만 수행해야 하는 경우가 있을 수 있는데, 이러한 상황에서 마스터 서버에 설치된 젠킨스는 슬레이브 노드에 작업을 할당하여 실행하도록 명령할 수 있다. 이러한 방식을 통해 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;리소스를 효율적으로 분배하여 빌드 시간을 단축시키고, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;전체적인 빌드 프로세스의 확장성과 관리 용이성을 높일 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Jenkins 설치&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;이 포스팅에서는 Jenkins를 Docker 이미지로 실행하는 방법을 소개한다.&lt;/span&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: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Jenkins는 Java 기반의 애플리케이션이기 때문에, Jenkins를 네이티브로 설치하기 위해서는 JDK 설치가 선행되어야 한다. 하지만, Jenkins를 도커 컨테이너로 실행할 경우, Jenkins 이미지에 이미 Java가 포함되어 있기 때문에 별도의 설치를 진행할 필요가 없다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708652302444&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run -d -p 8080:8080 -v jenkins_home:/var/jenkins_home --name jenkins jenkins/jenkins:lts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;-d :&lt;/b&gt; 백그라운드에서 컨테이너를 실행한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;-p 8080:8080 :&lt;/b&gt; 호스트의 8080 포트와 컨테이너의 8080 포트를 연결한다. Jenkins 웹 인터페이스에 접속하는 데 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;-v jenkins_home:/var/jenkins_home :&lt;/b&gt; Jenkins 데이터를 호스트의 지속적인 볼륨에 저장하여 컨테이너를 재시작하거나 업데이트해도 정보가 유지되도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--name jenkins :&lt;/b&gt; 컨테이너에 jenkins라는 이름을 할당한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;jenkins/jenkins:lts : &lt;/b&gt;&amp;nbsp;Jenkins의 공식 롱텀 서포트(LTS) 버전을 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 LTS는 &lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: left;&quot;&gt;Long Term Support의 약자로 장기간 지원받는 버전이라는 뜻이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Dockerhub에 있는 jenkins/jenkins의 설명을 보면 JDK17을 사용하는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 내가 올리는 Spring boot 프로젝트 역시 JDK17을 사용하는데, 이 버전과 Jenkins 버전을 굳이 굳이 맞출 필요는 없다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__0.webp&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY0Xxz/btsGqxch2mL/bWNGKXKlFWSik0rNCxCDHK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY0Xxz/btsGqxch2mL/bWNGKXKlFWSik0rNCxCDHK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY0Xxz/btsGqxch2mL/bWNGKXKlFWSik0rNCxCDHK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY0Xxz%2FbtsGqxch2mL%2FbWNGKXKlFWSik0rNCxCDHK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Dockehub Jenkins설명&quot; loading=&quot;lazy&quot; width=&quot;546&quot; height=&quot;1158&quot; data-filename=&quot;AnyConv.com__0.webp&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 아래와 같은 명령어로 Jenkins를 도커에 올려주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708690762943&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run -d --env JENKINS_OPTS=--httpPort=8080 
-v /etc/localtime:/etc/localtime:ro -e TZ=Asia/Seoul -p 9000:8080 
-v /home/ubuntu/jenkins:/var/jenkins_home 
-v /var/run/docker.sock:/var/run/docker.sock
--name jenkins -u root jenkins/jenkins:lts&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;나는 EC2 서버에 Jenkins를 올렸기 때문에 볼륨에&lt;b&gt; /home/ubuntu/jenkins&lt;/b&gt; 폴더를 연결해 주었고, 8080포트는 사용 중이기 때문에 9000으로 포트를 지정해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-v /etc/localtime:/etc/localtime:ro :&amp;nbsp;&lt;/b&gt; 해당 설정을 EC2의 시간과 Jenkins의 시간 설정을 동기화시켰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-e TZ=Asia/Seoul :&amp;nbsp;&lt;/b&gt;그리고 타임존을 설정하여 서울 시간으로 명시적으로 설정해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 설정은 같은 설정으로 보이지만, 시간 동기화와 타임존 설정이라는 다른 목적을 갖고 있으며, 일반적으로 함께 사용될 때 가장 정확한 시간 처리를 보장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-v /var/run/docker.sock:/var/run/docker.sock :&amp;nbsp;&lt;/b&gt;그리고 EC2 도커 소켓 파일을 젠킨스 컨테이너 내부로 마운트 하여, 컨테이너 내부에서 EC2의 Docker를 제어할 수 있도록 해 주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__1.webp&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wJavj/btsGq1qoE5z/mBrK3pWDecCkxWrsAgJUb0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wJavj/btsGq1qoE5z/mBrK3pWDecCkxWrsAgJUb0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wJavj/btsGq1qoE5z/mBrK3pWDecCkxWrsAgJUb0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwJavj%2FbtsGq1qoE5z%2FmBrK3pWDecCkxWrsAgJUb0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 설치 완료&amp;amp;#44; 초기 비밀번호&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;380&quot; data-filename=&quot;AnyConv.com__1.webp&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설치를 진행하면 이미지가 없기 때문에 자동으로 pull 과정을 거쳐서 젠킨스가 실행되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 나오는 하단의 비밀번호는 Jenkins의 초기 설정 비밀번호로 처음 Jenkins 초기 설정 마법사에 로그인하여 관리자 계정을 만들 때 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 초기설정 비밀번호는 Jenkins 파일 시스템 내에서 다시 확인이 가능하며, 아래의 명령어를 통해 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708692038314&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker exec &amp;lt;jenkins_container_name&amp;gt; cat /var/jenkins_home/secrets/initialAdminPassword&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_1708692148970&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker ps&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__2.webp&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0T73r/btsGrebXSXP/i2sWW4JUDQK4TKceYaFmlk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0T73r/btsGrebXSXP/i2sWW4JUDQK4TKceYaFmlk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0T73r/btsGrebXSXP/i2sWW4JUDQK4TKceYaFmlk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0T73r%2FbtsGrebXSXP%2Fi2sWW4JUDQK4TKceYaFmlk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 컨테이너&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;88&quot; data-filename=&quot;AnyConv.com__2.webp&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 설치한 도메인의 설정한 포트로 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우에는 &lt;b&gt;도메인:9000&lt;/b&gt;의 URL로 젠킨스에 접근해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__3.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSiv4y/btsGq1YeVRi/N9UwdLDPkUdTVlZWS7gON0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSiv4y/btsGq1YeVRi/N9UwdLDPkUdTVlZWS7gON0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSiv4y/btsGq1YeVRi/N9UwdLDPkUdTVlZWS7gON0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSiv4y%2FbtsGq1YeVRi%2FN9UwdLDPkUdTVlZWS7gON0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 설치 마법사&amp;amp;#44; 초기 비밀번호 입력&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;637&quot; data-filename=&quot;AnyConv.com__3.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__4.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGCAh8/btsGpXig5my/7w7CfxOiAslqkwKKMykFqK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGCAh8/btsGpXig5my/7w7CfxOiAslqkwKKMykFqK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGCAh8/btsGpXig5my/7w7CfxOiAslqkwKKMykFqK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGCAh8%2FbtsGpXig5my%2F7w7CfxOiAslqkwKKMykFqK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 설치&amp;amp;#44; 초기 플러그인 설정&quot; loading=&quot;lazy&quot; width=&quot;582&quot; height=&quot;645&quot; data-filename=&quot;AnyConv.com__4.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 위와 같이 초기 설정을 할 수 있는 화면이 나오고, 만약 초기 설정의 플러그인을 직접 선택을 할 것이라면 오른쪽을, 아니라면 왼쪽을 클릭하여 초기 설정 플러그인을 설치해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__5.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cj2uPl/btsGspKSpKQ/tGAdylLqtFWsJbvzyOo1WK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cj2uPl/btsGspKSpKQ/tGAdylLqtFWsJbvzyOo1WK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cj2uPl/btsGspKSpKQ/tGAdylLqtFWsJbvzyOo1WK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcj2uPl%2FbtsGspKSpKQ%2FtGAdylLqtFWsJbvzyOo1WK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 설치&amp;amp;#44; 초기 플러그인 커스텀 설정&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;892&quot; data-filename=&quot;AnyConv.com__5.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 오른쪽을 클릭했다면 원하는 플러그인을 선택해 주어 초기 설정을 진행하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인은 언제든지 젠킨스 내부에서 추가 설치하거나 삭제할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__6.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT8JdG/btsGqhnhFB2/K3lz3mXAaFqiB3Y8nF5eO0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT8JdG/btsGqhnhFB2/K3lz3mXAaFqiB3Y8nF5eO0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT8JdG/btsGqhnhFB2/K3lz3mXAaFqiB3Y8nF5eO0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT8JdG%2FbtsGqhnhFB2%2FK3lz3mXAaFqiB3Y8nF5eO0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 설치&amp;amp;#44; 초기 플러그인 설치 화면&quot; loading=&quot;lazy&quot; width=&quot;533&quot; height=&quot;1595&quot; data-filename=&quot;AnyConv.com__6.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1595&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 다 끝나게 되면, 관리자를 설정하는 화면으로 전환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__7.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/olOBd/btsGpZ8fJ3N/DjFICu5kL4ivXLOGCoSk50/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/olOBd/btsGpZ8fJ3N/DjFICu5kL4ivXLOGCoSk50/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/olOBd/btsGpZ8fJ3N/DjFICu5kL4ivXLOGCoSk50/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FolOBd%2FbtsGpZ8fJ3N%2FDjFICu5kL4ivXLOGCoSk50%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 설치&amp;amp;#44; 초기 관리자 설정 화면&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;875&quot; data-filename=&quot;AnyConv.com__7.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 화면에서 Jenkins에 접속할 아이디와 비밀번호, Jenkins에서 사용할 이름과 이메일을 입력해 준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__8.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnHGjK/btsGqQJr3XE/GYnNlNyB2baOeW98XQH7b0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnHGjK/btsGqQJr3XE/GYnNlNyB2baOeW98XQH7b0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnHGjK/btsGqQJr3XE/GYnNlNyB2baOeW98XQH7b0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnHGjK%2FbtsGqQJr3XE%2FGYnNlNyB2baOeW98XQH7b0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 설치&amp;amp;#44; 초기 접속 URL 설정&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;492&quot; data-filename=&quot;AnyConv.com__8.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Jenkins에서 사용할 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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__9.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qTeEG/btsGqJjkE4C/M7tmbOC9dwXYSSN1kI1WLk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qTeEG/btsGqJjkE4C/M7tmbOC9dwXYSSN1kI1WLk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qTeEG/btsGqJjkE4C/M7tmbOC9dwXYSSN1kI1WLk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqTeEG%2FbtsGqJjkE4C%2FM7tmbOC9dwXYSSN1kI1WLk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 메인 화면&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;747&quot; data-filename=&quot;AnyConv.com__9.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;747&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;만약 다음에 접속할 때 세션이 만료되거나, 다른 PC로 접속하게 될 경우 아래와 같은 화면으로 접속할 수 있는데, 이 전에 입력한 Username과 password로 접속하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__10.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m897Y/btsGqkYy6DG/ws8fIaSdjkB1rKmWaFYGrk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m897Y/btsGqkYy6DG/ws8fIaSdjkB1rKmWaFYGrk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m897Y/btsGqkYy6DG/ws8fIaSdjkB1rKmWaFYGrk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm897Y%2FbtsGqkYy6DG%2Fws8fIaSdjkB1rKmWaFYGrk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 접속 화면&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;635&quot; data-filename=&quot;AnyConv.com__10.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Jenkins 한글설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Dashboard &amp;gt; Manage Jenkins &amp;gt; Plugins로 접속한다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__11.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;631&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GxAiE/btsGpMBjHiQ/4kdQY56ps6sKhNBxlbl2VK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GxAiE/btsGpMBjHiQ/4kdQY56ps6sKhNBxlbl2VK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GxAiE/btsGpMBjHiQ/4kdQY56ps6sKhNBxlbl2VK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGxAiE%2FbtsGpMBjHiQ%2F4kdQY56ps6sKhNBxlbl2VK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 플러그인 추가&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;324&quot; data-filename=&quot;AnyConv.com__11.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;631&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Avaliable plugins &amp;gt; Locale 검색 &amp;gt; Locale 선택 후 &amp;gt; Install&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__12.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAZ79I/btsGqxwAa30/3dAaTjvMfjZ28YtYZ94Myk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAZ79I/btsGqxwAa30/3dAaTjvMfjZ28YtYZ94Myk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAZ79I/btsGqxwAa30/3dAaTjvMfjZ28YtYZ94Myk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAZ79I%2FbtsGqxwAa30%2F3dAaTjvMfjZ28YtYZ94Myk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins Locale 플러그인 설치&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;435&quot; data-filename=&quot;AnyConv.com__12.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;435&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DashBoard &amp;gt; ManageJenkins &amp;gt; System&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__13.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;619&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdsHcJ/btsGtkJi55v/sq1TctR368TrB2TYFTbpp0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdsHcJ/btsGtkJi55v/sq1TctR368TrB2TYFTbpp0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdsHcJ/btsGtkJi55v/sq1TctR368TrB2TYFTbpp0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdsHcJ%2FbtsGtkJi55v%2Fsq1TctR368TrB2TYFTbpp0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins locale 플러그인 적용&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;619&quot; data-filename=&quot;AnyConv.com__13.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;619&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Locale &amp;gt; 'ko' 입력 &amp;gt; 체크박스 체크 &amp;gt; Apply&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__14.webp&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oclwN/btsGsVbKsUL/3NiI9eE59XPKjXpzRFfJM0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oclwN/btsGsVbKsUL/3NiI9eE59XPKjXpzRFfJM0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oclwN/btsGsVbKsUL/3NiI9eE59XPKjXpzRFfJM0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoclwN%2FbtsGsVbKsUL%2F3NiI9eE59XPKjXpzRFfJM0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins locale ko 설정&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;417&quot; data-filename=&quot;AnyConv.com__14.webp&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;한글 적용 완료 !!&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AnyConv.com__15.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9nVSO/btsGp0F2ekW/flnAiej3ZXRHKGT6Oko5P1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9nVSO/btsGp0F2ekW/flnAiej3ZXRHKGT6Oko5P1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9nVSO/btsGp0F2ekW/flnAiej3ZXRHKGT6Oko5P1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9nVSO%2FbtsGp0F2ekW%2FflnAiej3ZXRHKGT6Oko5P1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Jenkins 한글 설정&quot; loading=&quot;lazy&quot; width=&quot;533&quot; height=&quot;810&quot; data-filename=&quot;AnyConv.com__15.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/85</guid>
      <comments>https://olrlobt.tistory.com/85#entry85comment</comments>
      <pubDate>Sat, 30 Mar 2024 23:08:19 +0900</pubDate>
    </item>
    <item>
      <title>[토이 프로젝트] 블로그를 깃허브에 효과적으로 노출시키는 방법</title>
      <link>https://olrlobt.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;많은 개발자들의 고민&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 깃허브 리드미에 내 블로그를 홍보하기 위해 링크를 해 놓곤 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만,&amp;nbsp;이 전의 내 깃허브 리드미에는 아래와 같이 a 태그를 이용한 조촐한 이미지 링크만 띄워 놓거나, 좌측 프로필에 조그맣게 걸어 놓는 것이 전부였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1121&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bftp0B/btsF72X9JFH/UOj16CC6OwgSoOmGU6dcwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bftp0B/btsF72X9JFH/UOj16CC6OwgSoOmGU6dcwK/img.png&quot; data-alt=&quot;보이는가? 사실 잘 보이지 않는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bftp0B/btsF72X9JFH/UOj16CC6OwgSoOmGU6dcwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbftp0B%2FbtsF72X9JFH%2FUOj16CC6OwgSoOmGU6dcwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;336&quot; data-origin-width=&quot;1121&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;보이는가? 사실 잘 보이지 않는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눌러보고 싶다는 호기심도 생기지 않고, 홍보도 잘 안 되는 것이 불만이었는데, 이를 해결할만한 마땅한 방법이 없어서 그대로 내버려 둔 채로 오랜 시간을 보냈다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1862&quot; data-origin-height=&quot;415&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M7Z3m/btsF78YmDl0/dKT2ma3k7m0ZXB9GyhXxY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M7Z3m/btsF78YmDl0/dKT2ma3k7m0ZXB9GyhXxY1/img.png&quot; data-alt=&quot;숨은 블로그 찾기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M7Z3m/btsF78YmDl0/dKT2ma3k7m0ZXB9GyhXxY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM7Z3m%2FbtsF78YmDl0%2FdKT2ma3k7m0ZXB9GyhXxY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1862&quot; height=&quot;415&quot; data-origin-width=&quot;1862&quot; data-origin-height=&quot;415&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;숨은 블로그 찾기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자, 혹은 개발자 지망생 분들이 자신의 블로그를 홍보, 포트폴리오, 지식공유의 목적으로 깃허브 리드미에 걸어 놓은 것을 자주 볼 수 있는데, 그리 효과적인 방법은 아니라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적어도 나는 블로그에 관심이 많은 편인데도, 눈에 띄지 않아서 지나치곤 하니까..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;효과적으로 포스팅을 노출하는 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I1u5C/btsGac6iXLb/kPZRaD7TKcOJWsBB4KJSek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I1u5C/btsGac6iXLb/kPZRaD7TKcOJWsBB4KJSek/img.png&quot; data-alt=&quot;꽤나 눈에 잘 띄는 위젯들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I1u5C/btsGac6iXLb/kPZRaD7TKcOJWsBB4KJSek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI1u5C%2FbtsGac6iXLb%2FkPZRaD7TKcOJWsBB4KJSek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;291&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;291&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;꽤나 눈에 잘 띄는 위젯들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 것을 해결할 만한 방법이 없을까 고민을 하던 도중, 위와 같은 리드미 꾸미기 위젯들이 눈에 밟혔다. 이런 위젯들은 사용 기술의 분포가 얼마나 되는지, 내 백준 프로필은 어떤지, 내가 얼마나 깃허브 활동을 했는지 등등 많은 형태로 존재하고, 꽤나 효과적으로 데이터를 제공해 준다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제와 먼 얘기긴 한데, 최근에 본 가장 재밌는 위젯은 3D 잔디이다.&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;1375&quot; data-origin-height=&quot;555&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/couCTk/btsF9eqhf20/MGSaAKkgMmW4ZyVqAHNLk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/couCTk/btsF9eqhf20/MGSaAKkgMmW4ZyVqAHNLk0/img.png&quot; data-alt=&quot;옛날과.. 지금의 나..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/couCTk/btsF9eqhf20/MGSaAKkgMmW4ZyVqAHNLk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcouCTk%2FbtsF9eqhf20%2FMGSaAKkgMmW4ZyVqAHNLk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1375&quot; height=&quot;555&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;555&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;옛날과.. 지금의 나..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밋밋하고 똑같던 잔디를 3D 입체로, 심지어 1일 1 커밋만으로 잔디를 채웠다면, 검거당해 버리는 신박한 위젯이었다. 이 것을 프로필에 걸고 난 후, 큰 변화는 안 보이겠지만 실제로 더 열심히 커밋하려고 노력도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 서론은 그만하고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 위젯 형식으로 블로그 포스팅을 보여줄 수 있으면 꽤나 효과적이지 않을까 고민하게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;어떤 식으로 제작된 걸까&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnOYv2/btsF9eKxqIw/2l6KwMbsKWtMtxpQItPO3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnOYv2/btsF9eKxqIw/2l6KwMbsKWtMtxpQItPO3K/img.png&quot; data-alt=&quot;다짜고짜 타겟으로 잡은 위젯&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnOYv2/btsF9eKxqIw/2l6KwMbsKWtMtxpQItPO3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnOYv2%2FbtsF9eKxqIw%2F2l6KwMbsKWtMtxpQItPO3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;358&quot; height=&quot;200&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다짜고짜 타겟으로 잡은 위젯&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다짜고짜 이 위젯을 배포하는 주소로 들어가서 어떤 식으로 만들었는지 파헤치기 시작했다. 먼저 개발자 도구로 위젯을 살펴보니, 단순히 이미지를 띄워주고 있었고 SVG 태그를 에 css를 이용하여 애니메이션을 넣는 형식이었다. 그리고 이 이미지를 리드미에서 사용하는 방법은 아래와 같았는데,&lt;/p&gt;
&lt;pre id=&quot;code_1711553455725&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=olrlobt&amp;amp;layout=compact)](https://github.com/olrlobt/github-readme-stats)&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;이는 Markdown에서 이미지를 띄워줄 때 많이 사용하는 방식이다.&lt;/p&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에서 이미지를 만들어준다고 추측하게 되었고,&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;a href=&quot;https://github.com/anuraghazra/github-readme-stats&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/anuraghazra/github-readme-stats&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위젯을 배포하는 레퍼지토리에 들어가서 코드를 분석하기 시작했다. 물론 TypeScript로 작성된 코드에 수많은 파일들과 나는 잘 모르는 GraphQL을 이용한 점에서 꽤나 애를 먹었다.&lt;/p&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를 호출받아서 GraphQL로 Github API를 호출하여 정보를 받아내고, SVG 이미지 파일로 만들어 데이터를 집어넣어 주는 형식이라는 것까지 파악했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서, 어차피 분석하는 김에 오픈소스 컨트리뷰터가 되면 좋겠다는 생각에 사소한 이슈 하나를 수정해 보고 싶어서 도전도 했었지만, 아쉽게도 이미 해결 중인 이슈라는 답변과 함께 채택되지는 못했다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1537&quot; data-origin-height=&quot;1269&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dm3Hvj/btsF8vy2Unt/Qg54SlRKOJzZFmxZNUKExK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dm3Hvj/btsF8vy2Unt/Qg54SlRKOJzZFmxZNUKExK/img.png&quot; data-alt=&quot;첫 오픈소스 컨트리뷰터 도전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dm3Hvj/btsF8vy2Unt/Qg54SlRKOJzZFmxZNUKExK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdm3Hvj%2FbtsF8vy2Unt%2FQg54SlRKOJzZFmxZNUKExK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;485&quot; height=&quot;1269&quot; data-origin-width=&quot;1537&quot; data-origin-height=&quot;1269&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;첫 오픈소스 컨트리뷰터 도전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모르는 언어를 열심히 찾아가며 오류를 수정하고, 영어 번역기까지 써 가며 PR을 작성한 것에 비해, 사전에 해결 중인 이슈인지 확인을 안 한 것이... 후회가 된다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 좋은 경험을 했고, 위젯 서비스를 어떤 식으로 운영 중인지 대강 알 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;블로그 위젯 제작 시작&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모은 정보들을 바탕으로 본격적인 제작에 들어갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 프로젝트를 구성한 후, 이미지가 잘 나오는지 테스트를 하기 시작했다. 앞선 예시 위젯에서는 TypeScript를 사용했지만, 나는 내가 자신 있는 Spring을 사용하기로 했다. 어차피 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;그리고, 단순히 포스팅 링크로 띄워주는 것보다는 최신순으로 포스팅을 띄워주는 것을 목표로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;329&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfqc5U/btsF7Y2wVGU/cgq9IAYKaTZCrKq2ZDbYV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfqc5U/btsF7Y2wVGU/cgq9IAYKaTZCrKq2ZDbYV0/img.png&quot; data-alt=&quot;이미지가 잘 노출된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfqc5U/btsF7Y2wVGU/cgq9IAYKaTZCrKq2ZDbYV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfqc5U%2FbtsF7Y2wVGU%2Fcgq9IAYKaTZCrKq2ZDbYV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;214&quot; height=&quot;258&quot; data-origin-width=&quot;329&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지가 잘 노출된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 프로젝트를 구성하고,&amp;nbsp;BurfferedImage를 이용하여 이미지를 만들어 API 호출 테스트를 진행했다. 그 결과, Markdown 형식에서 정상적으로 띄워지는 것을 확인했다. (위의 사진은 현재 제공 중인 사진이다.) 그리고는 바로 서비스 제작에 들어갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 작동 방식은 단순히 HTML을 크롤링해 와서 Image를 만들어 byte []로 반환해 주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이런 간단한 과정에서도 여러 가지 문제에 부딪혔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Markdown은 이미지 1개씩만 API호출이 가능하다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 Markdown은 API 호출을 지원하지 않는다. 하지만, 동적으로 이미지를 생성하기 위하여, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;이미지 태그(! [대체&lt;/span&gt; 텍스트](API 호출 링크)&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;)를 이용한 API를 호출은 예외적으로 지원한다.&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: #0d0d0d; text-align: start;&quot;&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;1049&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dziPjF/btsF89Jgz6X/wyiCcAWRhI44IyWLtk8jKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dziPjF/btsF89Jgz6X/wyiCcAWRhI44IyWLtk8jKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dziPjF/btsF89Jgz6X/wyiCcAWRhI44IyWLtk8jKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdziPjF%2FbtsF89Jgz6X%2FwyiCcAWRhI44IyWLtk8jKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;1049&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;1049&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 포스팅의 경우 여러 개의 포스팅을 리스트로 뿌려 놓은 UI를 생각했었다. 하지만 이를 구현하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1711555899158&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;![이미지1](https://www.example.com/?param=olrlobt&amp;amp;num=0&amp;amp;theme=b)
![이미지2](https://www.example.com/?param=olrlobt&amp;amp;num=1&amp;amp;theme=b)
![이미지3](https://www.example.com/?param=olrlobt&amp;amp;num=2&amp;amp;theme=b)
![이미지4](https://www.example.com/?param=olrlobt&amp;amp;num=3&amp;amp;theme=b)
![이미지5](https://www.example.com/?param=olrlobt&amp;amp;num=4&amp;amp;theme=b)
![이미지6](https://www.example.com/?param=olrlobt&amp;amp;num=5&amp;amp;theme=b)
...&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;이를 해결하기 위해, 많은 시도를 했다. 여러 사례를 찾아보고, Image를 여러 개 반환한다거나, 한 이미지로 6개의 포스팅을 묶어서 보내주면 되지 않을까? 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 결국에는 띄워준 블로그 위젯을 클릭했을 때, 해당 포스팅으로 바로 가는 기능도 제공할 계획이었어서 포기했다. 결국 이를 해결할 수 있는 근본적인 방법은 찾지 못했고, 사용자가 원하는 개수만큼 포스팅을 노출시킬 수 있게 하는 대신, 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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Markdown 이미지엔 링크를 포함시킬 수 없다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만든 Markdown 이미지 태그에는 큰 문제점이 있었다. 바로 링크를 걸 수 없다는 것인데, 기존의 SVG 태그를 생각하면 이미지 안에 a 태그를 넣어서 링크를 걸 수 있다. 하지만 Github를 포함한 대부분의 Markdown에서는 보안상의 이유로 SVG태그 내에서의 a 태그를 지원하지 않았고, 시도를 해보아도 에러만 뱉을 뿐이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스팅에 링크를 걸지 못한다면, 사실상 무의미하진 않을까..? 많은 고민을 하다, Markdown에서 지원하는 링크 태그 형식을 떠올렸다.&lt;/p&gt;
&lt;pre id=&quot;code_1711556886296&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&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;Markdown에서는 위와 같은 형식으로 글씨에 링크를 걸 수도, 이미지에 링크를 걸 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 해당 부분에 링크를 넣어주어 해결하고자 하였다. 하지만, 내가 보낸 API와 정확히 일치하는 포스팅의 링크를 어떻게 걸게 할까 고민이 되었는데, 이 역시 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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oDQvS/btsF9dx9i7Z/96bamKlGKuyykXhXElT5i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oDQvS/btsF9dx9i7Z/96bamKlGKuyykXhXElT5i1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oDQvS/btsF9dx9i7Z/96bamKlGKuyykXhXElT5i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoDQvS%2FbtsF9dx9i7Z%2F96bamKlGKuyykXhXElT5i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;97&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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 호출을 하여, 텍스트로 URL링크를 보내주었었다. 하지만 당연하게도, 해당 텍스트를 보여주는 웹페이지로 이동만 할 뿐이었다. 따라서 나는 RedirectView를 이용하여 해당 포스팅으로 Redirect 시키는 방법을 이용하여 링크를 걸어주었고, 정상적으로 작동이 되는 것을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;블로그 위젯&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 포스팅 이미지와 포스팅 링크를 구성하여 이미지를 서빙해 주는 블로그 위젯을 만들었다. 사실 이 외에도 여러 고민과 문제들이 있지만, 한 포스팅에 풀어내는 것보다 더 정리하여 깊이 있는 글로 남기고 싶어서 여기까지만 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;1039&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca7Yuf/btsGaEnWNLM/EhlL5KOpPBqpgLlfQLGbo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca7Yuf/btsGaEnWNLM/EhlL5KOpPBqpgLlfQLGbo0/img.png&quot; data-alt=&quot;블로그 위젯 사용 미리보기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca7Yuf/btsGaEnWNLM/EhlL5KOpPBqpgLlfQLGbo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca7Yuf%2FbtsGaEnWNLM%2FEhlL5KOpPBqpgLlfQLGbo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;449&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;1039&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;블로그 위젯 사용 미리보기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 이 블로그 위젯은 배포 중에 있으며, 누구나 사용할 수 있게 깃허브에 사용법을 정리해 두었다. 앞으로도 시간을 조금씩 할애하여 더 발전시킬 계획이고, 많은 사람들이 사용했으면 하는 바람에 적극적으로 홍보할 계획이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;a href=&quot;https://github.com/olrlobt&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/olrlobt&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711557832018&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;profile&quot; data-og-title=&quot;olrlobt - Overview&quot; data-og-description=&quot;olrlobt has 22 repositories available. Follow their code on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/olrlobt&quot; data-og-url=&quot;https://github.com/olrlobt&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kZSyi/hyVGKsKLn8/X6rNR8wNHWFUYs6t3N4FS0/img.jpg?width=460&amp;amp;height=460&amp;amp;face=134_158_308_348&quot;&gt;&lt;a href=&quot;https://github.com/olrlobt&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/olrlobt&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kZSyi/hyVGKsKLn8/X6rNR8wNHWFUYs6t3N4FS0/img.jpg?width=460&amp;amp;height=460&amp;amp;face=134_158_308_348');&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;olrlobt - Overview&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt has 22 repositories available. Follow their code on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/84</guid>
      <comments>https://olrlobt.tistory.com/84#entry84comment</comments>
      <pubDate>Thu, 28 Mar 2024 01:50:58 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] AWS EC2 프리티어 인스턴스 생성하기</title>
      <link>https://olrlobt.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AWS (Amazon Web Services)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;AWS는 Amazon이 제공하는 클라우드 컴퓨팅 플랫폼 및 인프라 서비스 모음이다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;2006년에 시작된 AWS는 가상 컴퓨터, 스토리지, 데이터베이스, 네트워킹, 분석, 머신 러닝, 모바일, 개발자 도구, 관리 도구, IoT(Internet of Things), 보안 및 엔터프라이즈 애플리케이션과 같은 다양한 서비스를 제공한다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; 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;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1711305505793&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 Management Console&quot; data-og-description=&quot;AWS Support 플랜은 AWS로 성공하는 데 도움이 되는 다양한 도구, 프로그램 및 전문 지식에 대한 액세스의 조합을 제공합니다.&quot; data-og-host=&quot;aws.amazon.com&quot; data-og-source-url=&quot;https://aws.amazon.com/ko/console/&quot; data-og-url=&quot;https://aws.amazon.com/ko/console/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/B3PGH/hyVDwhXwsZ/mqpbvF039bUlnuV5QN5G0k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/wLfdd/hyVDs7G9BT/t9SWLmtNfgB4MsiMHJ0Nz1/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/console/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aws.amazon.com/ko/console/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/B3PGH/hyVDwhXwsZ/mqpbvF039bUlnuV5QN5G0k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/wLfdd/hyVDs7G9BT/t9SWLmtNfgB4MsiMHJ0Nz1/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 Management Console&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AWS Support 플랜은 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;EC2 (Elastic Compute Cloud)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt; AWS가 제공하는 가장 기본적이고 중심적인 서비스 중 하나인 EC2는 사용자가 가상 컴퓨터 인스턴스를 빌려 사용할 수 있게 해주는 서비스이다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;사용자는 운영 체제, 애플리케이션 서버, 데이터베이스 서버 등을 포함한 서버를 클라우드에서 가상으로 실행할 수 있다.&amp;nbsp;&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;AWS에서는 무료로 EC2 프리티어를 사용할 수 있는데, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;AWS EC2 프리티어는 AWS 클라우드를 처음 사용하는 고객을 대상으로 하는 프로모션으로 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;매월 일정 시간 동안 t2.micro 또는 t3.micro 인스턴스를 무료로 사용할 수 있는 옵션을 제공한다. 하지만 사실상 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;매월 750시간을 제공하기 때문에, 하나의 인스턴스를 한 달 내내 계속 실행할 수 있다. 그렇다고 무제한으로 사용할 수 있는 것은 아니고, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&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: #0d0d0d; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;또한, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;AWS 프리티어는 AWS 계정을 생성한 후 12개월 동안만 유효하며, 12개월이 지난 후에는 표준 요금이 적용된다.&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;t2.micro와 t3.micro 인스턴스는 대략 1GB의 RAM을 제공하므로, 소규모 애플리케이션이나 학습용으로만 사용하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;EC2 인스턴스 생성하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 AWS 계정을 생성하고 로그인을 해주자. 이 부분에서는 해외결제가 가능한 카드를 등록하는 부분 말고는 큰 어려움이 없을 거라 생각해서 과감히 생략하고 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;AWS Region 설정&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에는 Region이라는 개념이 있다. AWS는 전 세계에 분산하여 운영하는 위치를 뜻한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 화면 우측 상단에서 Seoul로 바꿔주자.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (29).png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;827&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kTk3u/btsF1u1H01l/yuj3kfptSQCR5HRQzU3cN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kTk3u/btsF1u1H01l/yuj3kfptSQCR5HRQzU3cN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kTk3u/btsF1u1H01l/yuj3kfptSQCR5HRQzU3cN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkTk3u%2FbtsF1u1H01l%2Fyuj3kfptSQCR5HRQzU3cN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;463&quot; height=&quot;827&quot; data-filename=&quot;Untitled (29).png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;827&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (30).png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;1014&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOlR9o/btsF4xWIfVE/fxGAWahMlsAzMu1oVy8x2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOlR9o/btsF4xWIfVE/fxGAWahMlsAzMu1oVy8x2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOlR9o/btsF4xWIfVE/fxGAWahMlsAzMu1oVy8x2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOlR9o%2FbtsF4xWIfVE%2FfxGAWahMlsAzMu1oVy8x2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;1014&quot; data-filename=&quot;Untitled (30).png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;1014&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;EC2 서비스 접속&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lVpNf/btsF3dxDiBr/29X2zsOTXcNcZPrx23cvGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lVpNf/btsF3dxDiBr/29X2zsOTXcNcZPrx23cvGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lVpNf/btsF3dxDiBr/29X2zsOTXcNcZPrx23cvGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlVpNf%2FbtsF3dxDiBr%2F29X2zsOTXcNcZPrx23cvGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;481&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단의 검색창을 이용하여 EC2를 검색하여 대시보드에 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;EC2 인스턴스 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2395&quot; data-origin-height=&quot;1472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tIXnO/btsF1qLL78w/yv3BvLwwclFKsT94raoqK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tIXnO/btsF1qLL78w/yv3BvLwwclFKsT94raoqK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tIXnO/btsF1qLL78w/yv3BvLwwclFKsT94raoqK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtIXnO%2FbtsF1qLL78w%2Fyv3BvLwwclFKsT94raoqK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2395&quot; height=&quot;1472&quot; data-origin-width=&quot;2395&quot; data-origin-height=&quot;1472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 대시보드는 위와 같이 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스 시작을 눌러 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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;AMI 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;1587&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpbP8Y/btsF1IrSrvW/dAvQ9ejcN8WVdlFKLNXcy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpbP8Y/btsF1IrSrvW/dAvQ9ejcN8WVdlFKLNXcy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpbP8Y/btsF1IrSrvW/dAvQ9ejcN8WVdlFKLNXcy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpbP8Y%2FbtsF1IrSrvW%2FdAvQ9ejcN8WVdlFKLNXcy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;739&quot; height=&quot;643&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;1587&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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: #545b64; text-align: start;&quot;&gt;AMI를 선택해 준다. AMI는 설명이 잘 나와있다시피 &lt;span style=&quot;background-color: #ffffff; color: #545b64; text-align: start;&quot;&gt;인스턴스를 시작하는 데 필요한 소프트웨어이다. 나는 Ubuntu를 선택해 주었고, 하단에 &lt;span style=&quot;background-color: #ffffff; color: #16191f; text-align: start;&quot;&gt;Amazon Machine Image(AMI)를 눌러 사용할 이미지 버전을 선택해 주자.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1845&quot; data-origin-height=&quot;1411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSmwdc/btsF1ctvUvg/zz3GIA2rEGPYn2WuBwnFk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSmwdc/btsF1ctvUvg/zz3GIA2rEGPYn2WuBwnFk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSmwdc/btsF1ctvUvg/zz3GIA2rEGPYn2WuBwnFk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSmwdc%2FbtsF1ctvUvg%2Fzz3GIA2rEGPYn2WuBwnFk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1845&quot; height=&quot;1411&quot; data-origin-width=&quot;1845&quot; data-origin-height=&quot;1411&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 적당히 작은 애플리케이션을 만들 것이기 때문에, 프리티어 지원 버전을 선택했다. 사양과 요금을 종합적으로 고려하여 선택하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;키 페어 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;AWS에서 키 페어(Key Pair)는 EC2(Elastic Compute Cloud) 인스턴스와 같은 리소스에 안전하게 접근하기 위해 사용되는 공개키 암호화의 한 형태이다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;키 페어는 공개키와 개인키로 구성되며, 두 키는 수학적으로 연관은 있지만, 개인키로부터 공개키를 유추하는 것은 사실상 불가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 만드는 키는 개인키이며, &lt;b&gt;절대 유출이 되지 않게 조심해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (34).png&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFtFq2/btsF2R2C4lm/EDMTlcgkbYRSvESogadQWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFtFq2/btsF2R2C4lm/EDMTlcgkbYRSvESogadQWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFtFq2/btsF2R2C4lm/EDMTlcgkbYRSvESogadQWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFtFq2%2FbtsF2R2C4lm%2FEDMTlcgkbYRSvESogadQWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;370&quot; data-filename=&quot;Untitled (34).png&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 키 페어 생성을 눌러 Pem 키를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 기존에 사용하던 키페어가 있다면 선택해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;917&quot; data-origin-height=&quot;913&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K6jrG/btsF16lCA9J/FELrnEntTZYhVtGMc4X5Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K6jrG/btsF16lCA9J/FELrnEntTZYhVtGMc4X5Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K6jrG/btsF16lCA9J/FELrnEntTZYhVtGMc4X5Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK6jrG%2FbtsF16lCA9J%2FFELrnEntTZYhVtGMc4X5Hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;404&quot; height=&quot;913&quot; data-origin-width=&quot;917&quot; data-origin-height=&quot;913&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키 이름 역시 구분이 가능할 이름을 적어주고, RSA 유형으로. pem키 형식으로 만들어 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;RSA의 보안성이 높고 널리 지원되고 있고,. pem키 역시 다양한 운영 체제에서 사용 가능하게 광범위하게 지원되기 때문에 많이 사용한다.&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;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (35).png&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bos7e5/btsF1CL75OW/3MgmIyy8WjdW7nXaCwwBE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bos7e5/btsF1CL75OW/3MgmIyy8WjdW7nXaCwwBE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bos7e5/btsF1CL75OW/3MgmIyy8WjdW7nXaCwwBE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbos7e5%2FbtsF1CL75OW%2F3MgmIyy8WjdW7nXaCwwBE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;145&quot; data-filename=&quot;Untitled (35).png&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절대 유출되지 않는 곳에 잘 보관해 놓고, EC2 접속에 사용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;네트워크 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;1042&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xngWb/btsF2Pqe4wm/3CBZ1sgjX0BLRWRouegUA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xngWb/btsF2Pqe4wm/3CBZ1sgjX0BLRWRouegUA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xngWb/btsF2Pqe4wm/3CBZ1sgjX0BLRWRouegUA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxngWb%2FbtsF2Pqe4wm%2F3CBZ1sgjX0BLRWRouegUA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;1042&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;1042&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;이때, SSH 트래픽 허용란에 내 IP라고 되어 있다면, pem키를 이용한 SSH 접근이 동일 IP에서만 접근 가능함에 유의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스토리지 구성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (37).png&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;735&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clwULc/btsF3TMpkiA/XKyue85TVbfRM94Sk9KKb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clwULc/btsF3TMpkiA/XKyue85TVbfRM94Sk9KKb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clwULc/btsF3TMpkiA/XKyue85TVbfRM94Sk9KKb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclwULc%2FbtsF3TMpkiA%2FXKyue85TVbfRM94Sk9KKb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;735&quot; data-filename=&quot;Untitled (37).png&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;735&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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: #0d0d0d; text-align: start;&quot;&gt;AWS 프리 티어에서는 월별 30GB의 스토리지 양을 제공하는데, 여러 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;EBS(Elastic Block Store) 스토리지에 걸쳐 분배해서 사용할 수 있다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;예를 들어, 10GB짜리 EBS 볼륨 3개를 사용하거나, 20GB와 10GB짜리 볼륨 각각 하나씩을 사용할 수도 있다. 나는 하나만 사용할 것이기 때문에 30으로 설정해 주었다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;인스턴스 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;1136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcyL2M/btsF3CxbVAg/0etQGhhVQhDjWJqiOB9ZzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcyL2M/btsF3CxbVAg/0etQGhhVQhDjWJqiOB9ZzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcyL2M/btsF3CxbVAg/0etQGhhVQhDjWJqiOB9ZzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcyL2M%2FbtsF3CxbVAg%2F0etQGhhVQhDjWJqiOB9ZzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;344&quot; height=&quot;646&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;1136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 우측 요약란에서 내용을 확인하고, 인스턴스를 시작하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (39).png&quot; data-origin-width=&quot;2415&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSnd74/btsF15G2CfE/C7i4GbJUkrKkqv5lqwPX10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSnd74/btsF15G2CfE/C7i4GbJUkrKkqv5lqwPX10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSnd74/btsF15G2CfE/C7i4GbJUkrKkqv5lqwPX10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSnd74%2FbtsF15G2CfE%2FC7i4GbJUkrKkqv5lqwPX10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2415&quot; height=&quot;494&quot; data-filename=&quot;Untitled (39).png&quot; data-origin-width=&quot;2415&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2409&quot; data-origin-height=&quot;1109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biwAYN/btsF4zAc7i8/NNW6DnHB388uKlGIkcBFN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biwAYN/btsF4zAc7i8/NNW6DnHB388uKlGIkcBFN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biwAYN/btsF4zAc7i8/NNW6DnHB388uKlGIkcBFN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiwAYN%2FbtsF4zAc7i8%2FNNW6DnHB388uKlGIkcBFN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2409&quot; height=&quot;1109&quot; data-origin-width=&quot;2409&quot; data-origin-height=&quot;1109&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 화면이 뜬다면, 정상적으로 EC2 인스턴스가 생성되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;보안 그룹 설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;보안 그룹(Security Groups)은 AWS에서 제공하는 가상 방화벽으로, EC2 인스턴스와 같은 AWS 리소스에 대한 인바운드(들어오는 트래픽)와 아웃바운드(나가는 트래픽) 트래픽을 제어하는 데 사용된다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;보안 그룹을 통해 어떤 소스(IP 주소나 IP 주소 범위)에서 포트 번호를 통해 리소스에 접근할 수 있는지, 리소스가 외부로 어떤 트래픽을 보낼 수 있는지 세밀하게 설정할 수 있다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2419&quot; data-origin-height=&quot;1745&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yhgDb/btsF1zV9PH3/u2JfRS6af8VCCxyDti2CK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yhgDb/btsF1zV9PH3/u2JfRS6af8VCCxyDti2CK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yhgDb/btsF1zV9PH3/u2JfRS6af8VCCxyDti2CK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyhgDb%2FbtsF1zV9PH3%2Fu2JfRS6af8VCCxyDti2CK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2419&quot; height=&quot;1745&quot; data-origin-width=&quot;2419&quot; data-origin-height=&quot;1745&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측 탭에서 &quot;보안 그룹&quot;을 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;보안 그룹 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2424&quot; data-origin-height=&quot;989&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMuPxh/btsF2xQWO7d/f00kXON44EVfJyOWuOU4MK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMuPxh/btsF2xQWO7d/f00kXON44EVfJyOWuOU4MK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMuPxh/btsF2xQWO7d/f00kXON44EVfJyOWuOU4MK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMuPxh%2FbtsF2xQWO7d%2Ff00kXON44EVfJyOWuOU4MK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2424&quot; height=&quot;989&quot; data-origin-width=&quot;2424&quot; data-origin-height=&quot;989&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;보안 그룹 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2430&quot; data-origin-height=&quot;1723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baWuhL/btsF4ST1XgF/YRXSiA4NpEbbiSvx7HOtGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baWuhL/btsF4ST1XgF/YRXSiA4NpEbbiSvx7HOtGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baWuhL/btsF4ST1XgF/YRXSiA4NpEbbiSvx7HOtGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaWuhL%2FbtsF4ST1XgF%2FYRXSiA4NpEbbiSvx7HOtGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2430&quot; height=&quot;1723&quot; data-origin-width=&quot;2430&quot; data-origin-height=&quot;1723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;인바운드 규칙(Inbound Rules)&lt;/b&gt;: 특정 소스로부터 들어오는 트래픽을 허용하거나 거부할 수 있다. 소스는 IP 주소, IP 주소 범위, 또는 다른 보안 그룹일 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;아웃바운드 규칙(Outbound Rules)&lt;/b&gt;: 인스턴스가 시작하는 트래픽이 특정 목적지로 나갈 수 있도록 허용한다. 기본적으로 모든 아웃바운드 트래픽이 허용되지만, 필요에 따라 이를 제한할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 그룹을 구분할 수 있는 이름을 지어주고 하단의 인바운드 규칙을 추가하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;인바운드 규칙&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (44).png&quot; data-origin-width=&quot;2225&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IC3nl/btsF4Tk4RBM/ueZRo7Z1KBkGYl77Mi5iL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IC3nl/btsF4Tk4RBM/ueZRo7Z1KBkGYl77Mi5iL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IC3nl/btsF4Tk4RBM/ueZRo7Z1KBkGYl77Mi5iL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIC3nl%2FbtsF4Tk4RBM%2FueZRo7Z1KBkGYl77Mi5iL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2225&quot; height=&quot;722&quot; data-filename=&quot;Untitled (44).png&quot; data-origin-width=&quot;2225&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인바운드 규칙은 위와 같이 설정해 주었다. 이때, SSH는 앞서 설정한 pem키로 인스턴스 접근이 가능하게 꼭 설정해 주어야 하며, 기본적으로 HTTP(80)과 HTTPS(443)을 설정해 준다. 이 외에 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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;1278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ICzAR/btsF3kXLC7R/6o5b17ssrxufIbVcKL1Cuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ICzAR/btsF3kXLC7R/6o5b17ssrxufIbVcKL1Cuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ICzAR/btsF3kXLC7R/6o5b17ssrxufIbVcKL1Cuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FICzAR%2FbtsF3kXLC7R%2F6o5b17ssrxufIbVcKL1Cuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;1278&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;1278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 생성을 완료해 주면, 위와 같이 보안그룹이 잘 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;보안 그룹 설정&lt;/b&gt;&lt;/h3&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2061&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3hWwO/btsF3clbMIT/20Zn9jiITJoJFTlYxsDnWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3hWwO/btsF3clbMIT/20Zn9jiITJoJFTlYxsDnWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3hWwO/btsF3clbMIT/20Zn9jiITJoJFTlYxsDnWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3hWwO%2FbtsF3clbMIT%2F20Zn9jiITJoJFTlYxsDnWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2061&quot; height=&quot;890&quot; data-origin-width=&quot;2061&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, EC2 인스턴스를 우클릭하여 &quot;보안 그룹 변경&quot;을 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1271&quot; data-origin-height=&quot;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAwLpt/btsF4A0fh40/iCa6cpHuNu24JSPTWKn06k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAwLpt/btsF4A0fh40/iCa6cpHuNu24JSPTWKn06k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAwLpt/btsF4A0fh40/iCa6cpHuNu24JSPTWKn06k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAwLpt%2FbtsF4A0fh40%2FiCa6cpHuNu24JSPTWKn06k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;668&quot; height=&quot;661&quot; data-origin-width=&quot;1271&quot; data-origin-height=&quot;661&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &quot;보안 그룹 선택&quot;을 눌러 이 전에 만든 보안 그룹을 선택해서 저장해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2415&quot; data-origin-height=&quot;1474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzoJFp/btsF1aCt3yg/ciQttpIvcF4Oedlg1gNmM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzoJFp/btsF1aCt3yg/ciQttpIvcF4Oedlg1gNmM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzoJFp/btsF1aCt3yg/ciQttpIvcF4Oedlg1gNmM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzoJFp%2FbtsF1aCt3yg%2FciQttpIvcF4Oedlg1gNmM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2415&quot; height=&quot;1474&quot; data-origin-width=&quot;2415&quot; data-origin-height=&quot;1474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;EC2 인스턴스 접속하기&lt;/b&gt;&lt;/h2&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2415&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btu9bT/btsF3mOO8ob/306l9hUITwdy0US3gCGDy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btu9bT/btsF3mOO8ob/306l9hUITwdy0US3gCGDy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btu9bT/btsF3mOO8ob/306l9hUITwdy0US3gCGDy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtu9bT%2FbtsF3mOO8ob%2F306l9hUITwdy0US3gCGDy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2415&quot; height=&quot;413&quot; data-origin-width=&quot;2415&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 EC2 대시보드에서, 연결 버튼을 눌러준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;1168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm8mV6/btsF1bVAAqK/lx6LJ9SJwLXcAf9QAhRnpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm8mV6/btsF1bVAAqK/lx6LJ9SJwLXcAf9QAhRnpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm8mV6/btsF1bVAAqK/lx6LJ9SJwLXcAf9QAhRnpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm8mV6%2FbtsF1bVAAqK%2Flx6LJ9SJwLXcAf9QAhRnpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;539&quot; height=&quot;1168&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;1168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;chmod 400&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt; 명령어를 사용하여. pem&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt; 키 파일에 대한 권한을 설정하는 이유는 파일의 보안을 강화하기 위함이다. &lt;/span&gt;chmod 400&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&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;또한, chmod 400&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt; 설정을 통해 파일을 읽기 전용으로 만들어, 실수로 파일을 수정하거나 삭제하는 일을 방지할 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs9Yor/btsF14H70aT/kGryTSaVAs13jLu1SdlxPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs9Yor/btsF14H70aT/kGryTSaVAs13jLu1SdlxPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs9Yor/btsF14H70aT/kGryTSaVAs13jLu1SdlxPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs9Yor%2FbtsF14H70aT%2FkGryTSaVAs13jLu1SdlxPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;725&quot; height=&quot;348&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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: #0d0d0d; text-align: left;&quot;&gt;설정이 끝나면, 앞서 복사한 명령어를 shell에 입력하여 EC2에 접속할 수 있다.&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;</description>
      <category>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/83</guid>
      <comments>https://olrlobt.tistory.com/83#entry83comment</comments>
      <pubDate>Mon, 25 Mar 2024 03:39:02 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] Docker에서 Nginx 컨테이너 실행과 https 설정하기</title>
      <link>https://olrlobt.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1710857980690&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;[INFRA] Nginx를 사용하여 HTTPS 요청 처리하기&quot; data-og-description=&quot;2024.02.19 - [Infra] - [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기 [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기 이번 프로젝트에서 인프라를 담당하면서 위와 같은 구조로 시스템 아키텍처를 &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/80&quot; data-og-url=&quot;https://olrlobt.tistory.com/80&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/f8XOd/hyVDx0Yrg9/YcKA4TDRzgDKKj4Z6zMSuK/img.png?width=800&amp;amp;height=460&amp;amp;face=0_0_800_460,https://scrap.kakaocdn.net/dn/WBpBV/hyVAzlSC1b/9oCVCqSuyQw8jDdOsrhnqk/img.png?width=800&amp;amp;height=460&amp;amp;face=0_0_800_460,https://scrap.kakaocdn.net/dn/8lLJ5/hyVAHRHde0/1U8czORXsRAFNbvwruGFNk/img.png?width=1197&amp;amp;height=254&amp;amp;face=0_0_1197_254&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/f8XOd/hyVDx0Yrg9/YcKA4TDRzgDKKj4Z6zMSuK/img.png?width=800&amp;amp;height=460&amp;amp;face=0_0_800_460,https://scrap.kakaocdn.net/dn/WBpBV/hyVAzlSC1b/9oCVCqSuyQw8jDdOsrhnqk/img.png?width=800&amp;amp;height=460&amp;amp;face=0_0_800_460,https://scrap.kakaocdn.net/dn/8lLJ5/hyVAHRHde0/1U8czORXsRAFNbvwruGFNk/img.png?width=1197&amp;amp;height=254&amp;amp;face=0_0_1197_254');&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;[INFRA] Nginx를 사용하여 HTTPS 요청 처리하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2024.02.19 - [Infra] - [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기 [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기 이번 프로젝트에서 인프라를 담당하면서 위와 같은 구조로 시스템 아키텍처를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;지난번 포스팅에서 EC2에 Nginx를 직접 설치하고, https 설정까지 마쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Docker를 이용하여 Nginx를 컨테이너로 실행하고 https를 적용하는 방법까지 알아보자.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Nginx&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Nginx는 가볍고, 고성능의 HTTP 웹 서버, 리버스 프록시, 이메일 프록시(POP3/IMAP), TCP/UDP 프록시 서버로 사용된다.&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;Nginx를 Docker 컨테이너로 실행하는 것과 Amazon Ec2 인스턴스에서 직접 설치하는 것은 각각의 장단점이 있어서 상황에 따라 선택하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EC2에 직접 설치&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Docker, docker-compose에 관한 지식이 없어도 된다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Docker 컨테이너로 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Docker의 중요 특징 중 하나인, 어느 환경에서나 동일하게 실행된다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Docker에서 Nginx 컨테이너 실행하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;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;&lt;a href=&quot;https://olrlobt.tistory.com/81&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.02.21 - [Infra] - [INFRA] Docker와 Dockerfile, Docker-compose 구성하기&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;Docker가 설치되어 있다면 아래 명령어로 간단하게 Nginx 이미지를 Docker hub에서 pull 하고, 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하지만, 이 방법은 사용하지 않는다. 그냥 이렇구나 하고 다음 챕터로 넘어가자.&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;pre id=&quot;code_1710845206156&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run --name nginx -d -p 80:80 nginx #이 부분은 건너 뛰고 아래로 넘어가자&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVTAjC/btsFTfjlrfM/HXIGtrWIoD7jLPJXVLQ5P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVTAjC/btsFTfjlrfM/HXIGtrWIoD7jLPJXVLQ5P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVTAjC/btsFTfjlrfM/HXIGtrWIoD7jLPJXVLQ5P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVTAjC%2FbtsFTfjlrfM%2FHXIGtrWIoD7jLPJXVLQ5P1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;235&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무 설정을 하지 않고 도메인에 접속하게 되면, 아래와 같이 Nginx의 Welcome 페이지를 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L24qy/btsFTd0ifvh/l5I5khL2SxKSydJ27TQKbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L24qy/btsFTd0ifvh/l5I5khL2SxKSydJ27TQKbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L24qy/btsFTd0ifvh/l5I5khL2SxKSydJ27TQKbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL24qy%2FbtsFTd0ifvh%2Fl5I5khL2SxKSydJ27TQKbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1212&quot; height=&quot;513&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이렇게 실행하게 되면 Nginx 설정에 접근하기 위하여 컨테이너에 접속해야 하는 번거로움이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Docker 컨테이너에 볼륨을 설정하여 EC2 인스턴스의 폴더와 Nginx 컨테이너 간 데이터를 공유하게 해 주자. 이렇게 설정하면 Nginx 설정이 바뀌더라도 컨테이너에 직접 접속하지 않고 쉽게 설정을 변경할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 볼륨 설정을 컨테이너 run 명령어에서 처리할 수도 있지만, 볼륨 개수가 늘어나면 한 명령어에 3줄씩 차지하는 현상이 발생한다. 따라서 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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;docker-compose.yml 작성&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1710859102286&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;version: '3'

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - &quot;80:80&quot;
      - &quot;443:443&quot;
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    depends_on:
      - certbot

  certbot:
    image: certbot/certbot
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot&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;여기서 version은 docker compose의 버전을 나타낸다. 그리고 nginx, certbot를 컨테이너로 올리고 있으며, volumes를 통해 EC2의 설정 파일들을 nginx 내부와 연동시킨다. 예를 들어, &quot;./data/nginx:/etc/nginx/conf.d&quot;로 볼륨이 설정되면, &quot;./data/nginx&quot; 폴더 안에 파일들이 nginx 컨테이너 내부의 &quot;/etc/nginx/conf.d&quot;로 복사된다고 이해하면 편할 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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 compose 작성이 끝났다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 여러 설정 파일을 관리할 폴더를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1710849661780&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ mkdir -p /home/ubuntu/data/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.yml 파일을 작성하여 EC2 안으로 옮긴다.&lt;/p&gt;
&lt;pre id=&quot;code_1710849647132&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scp -i {pem key} {복사할 파일} {호스트}@{도메인}:{복사할 위치}/{이름}
scp -i .\readme-blog-key.pem .\docker-compose.yml ubuntu@ec2:/home/ubuntu/data/docker-compose.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oAliS/btsFUcGKYHc/Tsg8Qdgpf56atr3ohLHPkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oAliS/btsFUcGKYHc/Tsg8Qdgpf56atr3ohLHPkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oAliS/btsFUcGKYHc/Tsg8Qdgpf56atr3ohLHPkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoAliS%2FbtsFUcGKYHc%2FTsg8Qdgpf56atr3ohLHPkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1180&quot; height=&quot;57&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SSL/TLS 인증서 설치&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;그다음, EC2에 접속하여&lt;span&gt; &lt;/span&gt;&lt;/span&gt;볼륨 설정을 해 둔 nginx 설정파일 경로에 conf 파일을 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1710861290727&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name blogwidget.com; #여기에는 본인의 도메인이 들어간다.
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}&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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이제 docker-compose up 명령어로 도커 컴포즈를 실행시키자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, -d 옵션을 붙여 백그라운드로 실행되게 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1710859759604&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker-compose up -d
$ docker ps&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRikst/btsFUsbtRGd/H9KajkkHcC4w3Lp57dsSkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRikst/btsFUsbtRGd/H9KajkkHcC4w3Lp57dsSkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRikst/btsFUsbtRGd/H9KajkkHcC4w3Lp57dsSkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRikst%2FbtsFUsbtRGd%2FH9KajkkHcC4w3Lp57dsSkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1198&quot; height=&quot;221&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 다음에는 docker ps 명령어로 nginx 컨테이너나 certbot 컨테이너가 죽지 않았는지 확인해 주자.&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;잘 실행되었다면, certbot을 이용하여 도메인에 대한 SSL/TLS 인증서를 생성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어를 입력하여, certbot 인증서를 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1710860148315&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker-compose run --rm certbot certonly --webroot --webroot-path=/var/www/certbot -d your_domain.com -d www.your_domain.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;--webroot : 방식을 이용하여 웹 서버의 루트 디렉터리에 특정 파일을 생성하도록 하여 도메인 소유권을 증명한다.&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: #0d0d0d; text-align: left;&quot;&gt;--webroot-path=/var/www/certbot : 인증 과정에서 생성되는 파일을 저장할 경로이다.&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;여기서, yourdomain에는 도메인 명이 들어가야 하며, EC2 서버에서 발급받은 기본 도메인으로는 진행할 수 없다. 만약 진행하게 된다면 아래와 같이 더 진행할 수 없을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5aZmH/btsFSapawal/mVlNOiBTAPwP1nrOkz64Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5aZmH/btsFSapawal/mVlNOiBTAPwP1nrOkz64Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5aZmH/btsFSapawal/mVlNOiBTAPwP1nrOkz64Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5aZmH%2FbtsFSapawal%2FmVlNOiBTAPwP1nrOkz64Dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1159&quot; height=&quot;217&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 만약 도메인이 없다면 아래 포스팅을 참고하여 도메인을 구매하고 설정하자. 500원이면 구매 가능한 도메인도 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;도메인 구매 관련 포스팅 추가 예정입니다! 잠시만 기다려 주세요!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oTflr/btsFR2LuhKW/Zko97Er7WK4xJ9kmdYTt7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oTflr/btsFR2LuhKW/Zko97Er7WK4xJ9kmdYTt7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oTflr/btsFR2LuhKW/Zko97Er7WK4xJ9kmdYTt7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoTflr%2FbtsFR2LuhKW%2FZko97Er7WK4xJ9kmdYTt7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1203&quot; height=&quot;389&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 SSL/TLS 인증서 설치가 제대로 마쳤다면, 다시 Nginx conf 설정에 Https 리버스 프록시 설정을 해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1710861436433&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name blogwidget.com; 
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name blogwidget.com;
    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/blogwidget.com/fullchain.pem; 
    ssl_certificate_key /etc/letsencrypt/live/blogwidget.com/privkey.pem; 
    

    location / {
        proxy_pass  http://blogwidget.com:8080;
        proxy_set_header    Host                $http_host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    }
}&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;설정을 잘 마쳤다면, nginx 컨테이너를 재 실행해 설정을 적용해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1710861480936&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker restart nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HTTPS 설정 완료&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인에 접속하면 정상적으로 https접근이 가능해졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (28).png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOKB0/btsFWB6aTXi/haTitjnHUjfxhYmOpOvtL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOKB0/btsFWB6aTXi/haTitjnHUjfxhYmOpOvtL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOKB0/btsFWB6aTXi/haTitjnHUjfxhYmOpOvtL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOKB0%2FbtsFWB6aTXi%2FhaTitjnHUjfxhYmOpOvtL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;566&quot; height=&quot;545&quot; data-filename=&quot;Untitled (28).png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;545&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-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>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/82</guid>
      <comments>https://olrlobt.tistory.com/82#entry82comment</comments>
      <pubDate>Wed, 20 Mar 2024 00:24:53 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] Docker와 Dockerfile, Docker-compose 구성하기</title>
      <link>https://olrlobt.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.02.20 - [Infra] - [INFRA] Nginx를 사용하여 HTTPS 요청 처리하기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708926628496&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;[INFRA] Nginx를 사용하여 HTTPS 요청 처리하기&quot; data-og-description=&quot;2024.02.19 - [Infra] - [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기 [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기 이번 프로젝트에서 인프라를 담당하면서 위와 같은 구조로 시스템 아키텍처를 &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/80&quot; data-og-url=&quot;https://olrlobt.tistory.com/80&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkGCKY/hyVqnLISDt/RIzgwzoEiVAKSnW3xGVB40/img.png?width=800&amp;amp;height=460&amp;amp;face=0_0_800_460,https://scrap.kakaocdn.net/dn/dgF8gC/hyVqoRnhjG/lck2xRLdDi5Dvz3zAsKDvK/img.png?width=800&amp;amp;height=460&amp;amp;face=0_0_800_460,https://scrap.kakaocdn.net/dn/h2MVw/hyVqjo2nXG/RUoeewKSXguygNvBL888KK/img.png?width=1197&amp;amp;height=254&amp;amp;face=0_0_1197_254&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkGCKY/hyVqnLISDt/RIzgwzoEiVAKSnW3xGVB40/img.png?width=800&amp;amp;height=460&amp;amp;face=0_0_800_460,https://scrap.kakaocdn.net/dn/dgF8gC/hyVqoRnhjG/lck2xRLdDi5Dvz3zAsKDvK/img.png?width=800&amp;amp;height=460&amp;amp;face=0_0_800_460,https://scrap.kakaocdn.net/dn/h2MVw/hyVqjo2nXG/RUoeewKSXguygNvBL888KK/img.png?width=1197&amp;amp;height=254&amp;amp;face=0_0_1197_254');&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;[INFRA] Nginx를 사용하여 HTTPS 요청 처리하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2024.02.19 - [Infra] - [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기 [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기 이번 프로젝트에서 인프라를 담당하면서 위와 같은 구조로 시스템 아키텍처를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Docker&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Docker는 소프트웨어 개발 분야에서 광범위하게 사용되는 오픈 소스 컨테이너화 플랫폼이다. &lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;도커 컨테이너는 일종의 소프트웨어를 소프트웨어의 실행에 필요한 모든 것을 포함하는 완전한 파일 시스템 안에 감싼다. 여기에는 코드, 런타임, 시스템 도구, 시스템 라이브러리 등 서버에 설치되는 무엇이든 아우른다. 이는 &lt;b&gt;실행 중인 환경에 관계없이 언제나 동일&lt;/b&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;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;Docker 이미지는 애플리케이션을 실행하는 데 필요한 모든 것을 포함하는 경량의, 독립적인, 실행 가능한 소프트웨어 패키지로, 컨테이너를 생성하는 데 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Docker 설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Docker를 설치하기 전에, Docker의 공식 GPG 키를 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;시스템의 패키지&lt;/span&gt;에 추가해야 한다. GPG 키를 시스템 패키지에 추가하게 되면, 패키지의 내용을 기반으로 한 해쉬 값이 생성되게 되는데, 만약 패키지가 수정되게 되면 시스템 패키지의 해시 값이 변하게 되면서 원본과 일치하지 않게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 방식을 이용하여 GPG 키를 통해 패키지가 신뢰할 수 있는 기관에서 생성되었음을 확인할 수 있다. 또한, GPG 서명은 패키지가 배포된 이후로 수정되지 않았음을 보증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;필요한 패키지 설치&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;HTTPS를 통해 저장소에서 패키지를 안전하게 다운로드하고, Docker의 공식 GPG 키를 추가하기 위해 필요한 패키지를 다운로드한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708411008322&quot; class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt-get update&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1708410730715&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Docker의 공식 GPG 키 추가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;Docker 저장소의 패키지가 신뢰할 수 있는 출처인지 확인하기 위해 Docker의 공식 GPG 키를 시스템에 추가한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708410780591&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;Docker의 공식 저장소를 시스템의 APT 소스 목록에 추가&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708411078285&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo add-apt-repository \
   &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable&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;h4 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;패키지 목록 업데이트 및 Docker 설치&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;새로 추가된 Docker 저장소의 패키지 정보를 포함하여 시스템의 전체 패키지 목록을 업데이트하고, Docker CE를 설치한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708411092560&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt-get update
$ sudo apt-get install docker-ce&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용자를 Docker 그룹에 추가&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708411103424&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo usermod -aG docker ${USER}&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;이 명령어를 통해 사용자는 sudo 명령어 없이 Docker 명령을 사용할 수 있게 된다.&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;h4 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Docker 서비스 시작 및 자동 시작 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708411215172&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo systemctl start docker
$ sudo systemctl enable docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Docker 버전 확인&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708410574974&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;24&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M8PAD/btsE6RpdPhd/8Vvv05sKh9CjpilwV8Tcvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M8PAD/btsE6RpdPhd/8Vvv05sKh9CjpilwV8Tcvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M8PAD/btsE6RpdPhd/8Vvv05sKh9CjpilwV8Tcvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM8PAD%2FbtsE6RpdPhd%2F8Vvv05sKh9CjpilwV8Tcvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;24&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;24&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 나온다면 설치가 잘 완료된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Dockerfile&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Dockerfile은 Docker 이미지를 생성하기 위한 명령어와 설정을 포함하는 텍스트 파일이다. 이 파일은 Docker 이미지를 빌드할 때 필요한 모든 지시사항을 담고 있다. Dockerfile을 사용하면 애플리케이션과 그 의존성을 포함하는 컨테이너화된 환경을 정의, 생성, 자동화할 수 있다.&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;Dockerfile은 첫 글자는 대문자로 &quot;Dockerfile&quot;이라는 이름의 텍스트 파일로 생성하며, 대부분 프로젝트의 루트 경로에 위치시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708432320146&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;프로젝트 루트 디렉토리/
├── src/
│   ├── main/
│   │   ├── java/
│   │   └── resources/
│   └── test/
├── build.gradle    # Gradle 빌드 스크립트
├── settings.gradle
└── Dockerfile      # Dockerfile 위치&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Dockerfile의 기본 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring의 경우 아래와 같이 Dockerfile을 구성할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708431808206&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM openjdk:17-jdk # 베이스 이미지로 OpenJDK가 포함된 이미지 사용

WORKDIR /app # 현재 디렉토리 설정

# 애플리케이션의 JAR 파일을 컨테이너에 app.jar라는 이름으로 복사
# 실제 build된 jar 파일 경로와 build.gradle을 참고하여 파일 이름을 작성해 주어야 한다
COPY build/libs/bid-admin-0.0.1-SNAPSHOT.jar app.jar

EXPOSE 8080 # 포트 8080 열기

ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;app.jar&quot;] # 애플리케이션 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;FROM:&lt;/b&gt; &lt;br /&gt;이미지의 기반이 되는 베이스 이미지를 지정한다. 예를 들어, FROM ubuntu:18.04은 Ubuntu 18.04를 베이스 이미지로 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WORKDIR:&lt;/b&gt; &lt;br /&gt;명령어를 실행할 작업 디렉터리를 설정한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RUN:&lt;/b&gt; &lt;br /&gt;이미지를 빌드하는 동안 명령어를 실행한다. 예를 들어, 패키지를 설치할 때 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CMD:&lt;/b&gt; &lt;br /&gt;컨테이너가 시작될 때 실행할 명령어를 지정한다. Dockerfile에는 하나의 CMD만 있어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;COPY:&lt;/b&gt; &lt;br /&gt;호스트 시스템의 파일이나 디렉터리를 이미지의 파일 시스템으로 복사한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ADD&lt;/b&gt;: &lt;br /&gt;COPY와 비슷하지만, 리모트 URL에서 파일을 추가하거나 압축 파일을 자동으로 압축 해제할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EXPOSE:&lt;/b&gt; &lt;br /&gt;컨테이너에서 리슨 할 포트를 지정한다. 하지만 실제 포트 오픈을 강제하지 않으며, 문서화 목적으로만 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ENTRYPOINT:&lt;/b&gt; &lt;br /&gt;컨테이너가 시작될 때 실행할 스크립트나 명령어를 지정한다. CMD와 함께 사용되어, 실행 파일과 파라미터를 분리할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ENV:&lt;/b&gt;&lt;br /&gt;환경 변수를 설정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;여기서 중요한 점은 &lt;/span&gt;COPY&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt; 명령어가 사용하는 경로이다. Gradle을 사용할 때, 빌드 결과물은 보통 &lt;/span&gt;build/libs&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;디렉터리에 위치한다. 따라서 &lt;/span&gt;Dockerfile&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;에서는 이 디렉터리 내 생성된 JAR 파일을 컨테이너 내부로 복사하도록 지정한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708432360728&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker build -t spring-boot-app .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;docker build : &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;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Docker 이미지를 빌드하기 위한 명령어이다.&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: #0d0d0d; text-align: left;&quot;&gt;-&lt;b&gt;t :&lt;/b&gt; 이미지의 이름과 태그를 지정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;여기서 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;spring-boot-app은&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt; 생성할 이미지의 이름과 태그를 지정하는 것이며, 마지막의 점(.&lt;/span&gt; )은&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;&amp;nbsp;현재 디렉터리를 가리키며, Dockerfile 위치를 의미한다.&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_1708432383270&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run -d -p 8080:8080 spring-boot-app&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker run :&lt;/b&gt; Docker&amp;nbsp; 이미지로부터 컨테이너를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-d :&lt;/b&gt; 컨테이너를 백그라운드에서 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-p 8080:8080 :&lt;/b&gt; 호스트의 8080 포트와 컨테이너의 8080 포트를 연결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 호스트의 8080 포트를 통해 컨테이너의 8080 포트로 연결되며, 해당 컨테이너에 접근이 가능해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile은 사용하는 프레임워크나, 기술에 따라 구성이 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 정적 파일로 빌드가 되는 React 같은 경우는 Dockerfile에서 Nginx를 통해 컨테이너 내부의 정적 파일을 서빙해 주어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1708473781817&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM node:14 as build-stage # Node.js 이미지를 베이스 이미지로 사용

WORKDIR /app # 작업 디렉토리 설정

COPY package*.json ./ # 애플리케이션의 package.json과 package-lock.json을 이미지로 복사

RUN npm install # 의존성 설치

COPY . . # 애플리케이션의 소스 코드를 이미지로 복사

RUN npm run build # React 애플리케이션 빌드

FROM nginx:alpine as production-stage # Nginx 이미지를 베이스 이미지로 사용

COPY --from=build-stage /app/build /usr/share/nginx/html # 빌드 단계에서 생성된 빌드 파일을 Nginx의 서빙 디렉토리로 복사

EXPOSE 80 # 기본 포트로 80을 노출

CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;] # Nginx 실행&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;위와 같이 Dockerfile을 구성하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708474930076&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker build -t react-app .
$ docker run -d -p 8080:80 react-app&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;도커 이미지를 만들어 컨테이너로 실행시켜 주면, 호스트의 8080 포트로 Nginx가 서빙해 준 React의 정적 파일에 접근이 가능해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Docker compose&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Docker Compose는 여러 Docker 컨테이너를 정의하고 실행하기 위한 도구이다. YAML 파일(&lt;/span&gt;docker-compose.yml&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;)을 사용하여 애플리케이션의 서비스, 네트워크, 볼륨 등을 구성할 수 있다. Docker Compose는 특히 멀티 컨테이너 Docker 애플리케이션 개발, 테스팅, 배포에 유용하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Docker-compose 설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어로 리눅스 환경에서 Docker compose를 설치할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708481112281&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt-get install 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;잘 설치가 되었다면 아래와 같이 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708481125093&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker-compose --version&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;27&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/da4Yhw/btsE3oaHllE/kawYWnHVCpQUJnWagvy6Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/da4Yhw/btsE3oaHllE/kawYWnHVCpQUJnWagvy6Ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/da4Yhw/btsE3oaHllE/kawYWnHVCpQUJnWagvy6Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fda4Yhw%2FbtsE3oaHllE%2FkawYWnHVCpQUJnWagvy6Ok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;442&quot; height=&quot;27&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;27&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;docker-compose.yml 파일 기본 구조&lt;/b&gt; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker compose 파일은 docker-compose.yml이라는 이름으로 프로젝트 루트 경로에 위치시킨다.&lt;/p&gt;
&lt;pre id=&quot;code_1708479851047&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;my-spring-boot-app/
├── src/
│   └── main/
│       ├── java/
│       └── resources/
├── Dockerfile
├── docker-compose.yml # docker-compose.yml 위치
├── build.gradle    # Gradle 빌드 스크립트
├── settings.gradle
└── .gitignore&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;docker-compose.yml은 아래와 같이 구성된다.&lt;/p&gt;
&lt;pre id=&quot;code_1708480047312&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3'
services:
  web:
    image: example/mywebapp:latest
    ports:
      - &quot;5000:5000&quot;
    depends_on:
      - db
  db:
    image: postgres:latest
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password&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;version :&lt;/b&gt; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;Docker Compose API의&amp;nbsp; 버전을 나타낸다. Docker Compose 버전은 도커 엔진 버전에 의존성이 있기 때문에 가능하면 최신 버전을 사용하는 것이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;&lt;b&gt;services:&lt;/b&gt; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;컨테이너화된 애플리케이션의 각 컴포넌트를 정의한다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;이 예제에서는 &lt;/span&gt;web&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;과 &lt;/span&gt;db&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&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;web&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;&lt;b&gt;:&lt;/b&gt; 사용자 정의 웹 애플리케이션 서비스다. 이 서비스는 &lt;/span&gt;example/mywebapp:latest&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;라는 이미지를 사용하여 컨테이너를 생성한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;image:&lt;/b&gt; web 서비스에 사용될 Docker 이미지를 지정한다. 여기서는 example/mywebapp의 latest 태그를 사용하는 이미지를 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ports:&lt;/b&gt; 호스트와 컨테이너 간의 포트 매핑을 정의한다. 이 설정은 호스트의 5000 포트와 컨테이너의 5000 포트를 연결한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;depends_on:&lt;/b&gt; web 서비스가 db 서비스에 의존한다는 것을 나타낸다. 이는 db 서비스가 web 서비스보다 먼저 시작되어야 함을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708481291820&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker-compose up&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 docker-compose.yml이 있는 경로에서 위의 명령어로 실행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 docker-compose.yml을 아래와 같이 구성하여,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트 React와, 백엔드 Spring boot, 웹소켓 Node Js를 한 docker-compose.yml에 작성하여 필요에 의해 한 아이템씩 빌드할 수 있도록 젠킨스 파이프라인을 구성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상세한 것은 아래 내용을 참고하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1708489788544&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3.8'
services:

  frontend:
    image: olrlobt/bid-front:latest
    container_name: bid-front
    ports:
      - &quot;3000:443&quot;
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt
    environment:
      TZ: Asia/Seoul

  bid-socket:
    image: olrlobt/bid-socket:latest
    container_name: bid-socket
    ports:
      - &quot;3001:3001&quot;
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt
    environment:
      TZ: Asia/Seoul

  bid-core:
    image: olrlobt/bid-core:latest
    container_name: bid-core
    ports:
      - &quot;8083:8083&quot;
    volumes:
      - /home/ubuntu/config/application.yml:/config/core/application.yml
      - /home/ubuntu/config/application_core.yml:/config/core/application-port.yml
    environment:
      - SPRING_CONFIG_LOCATION=file:/config/core/
      - SPRING_PROFILES_ACTIVE=port
      - TZ=Asia/Seoul

  bid-admin:
    image: olrlobt/bid-admin:latest
    container_name: bid-admin
    depends_on:
      - bid-core
    ports:
      - &quot;8081:8081&quot;
    volumes:
      - /home/ubuntu/config/application.yml:/config/admin/application.yml
      - /home/ubuntu/config/application_admin.yml:/config/admin/application-port.yml
    environment:
      - SPRING_CONFIG_LOCATION=file:/config/admin/
      - SPRING_PROFILES_ACTIVE=port
      - TZ=Asia/Seoul

  bid-student:
    image: olrlobt/bid-student:latest
    container_name: bid-student
    depends_on:
      - bid-core
    ports:
      - &quot;8082:8082&quot;
    volumes:
      - /home/ubuntu/config/application.yml:/config/student/application.yml
      - /home/ubuntu/config/application_student.yml:/config/student/application-port.yml
    environment:
      - SPRING_CONFIG_LOCATION=file:/config/student/
      - SPRING_PROFILES_ACTIVE=port
      - TZ=Asia/Seoul&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/81</guid>
      <comments>https://olrlobt.tistory.com/81#entry81comment</comments>
      <pubDate>Wed, 21 Feb 2024 14:34:10 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] Nginx를 사용하여 HTTPS 요청 처리하기</title>
      <link>https://olrlobt.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/79&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.02.19 - [Infra] - [INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708926599488&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;[INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기&quot; data-og-description=&quot;이번 프로젝트에서 인프라를 담당하면서 위와 같은 구조로 시스템 아키텍처를 구성해 보았다. 위와 같이 인프라를 구성하면서, 진행했던 과정을 기록하고 문제가 됐던 부분을 다시 정리해 보려&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/79&quot; data-og-url=&quot;https://olrlobt.tistory.com/79&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KQ4lF/hyVquxjqu5/Lmxzd8LQA4gqn5KG1MKwM0/img.png?width=800&amp;amp;height=459&amp;amp;face=0_0_800_459,https://scrap.kakaocdn.net/dn/48FvG/hyVqkVKRKc/Fd9tScrrY08lXO3SgTkjok/img.png?width=800&amp;amp;height=459&amp;amp;face=0_0_800_459,https://scrap.kakaocdn.net/dn/bfID3J/hyVqm67dhn/VA0gwtn8djwjpZCFsWSo20/img.png?width=3062&amp;amp;height=1760&amp;amp;face=0_0_3062_1760&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/79&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/79&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KQ4lF/hyVquxjqu5/Lmxzd8LQA4gqn5KG1MKwM0/img.png?width=800&amp;amp;height=459&amp;amp;face=0_0_800_459,https://scrap.kakaocdn.net/dn/48FvG/hyVqkVKRKc/Fd9tScrrY08lXO3SgTkjok/img.png?width=800&amp;amp;height=459&amp;amp;face=0_0_800_459,https://scrap.kakaocdn.net/dn/bfID3J/hyVqm67dhn/VA0gwtn8djwjpZCFsWSo20/img.png?width=3062&amp;amp;height=1760&amp;amp;face=0_0_3062_1760');&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;[INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서 인프라를 담당하면서 위와 같은 구조로 시스템 아키텍처를 구성해 보았다. 위와 같이 인프라를 구성하면서, 진행했던 과정을 기록하고 문제가 됐던 부분을 다시 정리해 보려&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;해당 포스팅은 윗글과 이어서 진행하는 글이다. 기본적인 서버 세팅은 위 포스팅을 참고하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Nginx&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Nginx는 가볍고, 고성능의 HTTP 웹 서버, 리버스 프록시, 이메일 프록시(POP3/IMAP), TCP/UDP 프록시 서버로 사용된다. 비동기 이벤트 기반의 구조를 가지고 있어, 매우 높은 동시 연결을 처리할 수 있는 것이 특징이다.&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: #0d0d0d; text-align: start;&quot;&gt; 처음에는 웹 서버로 개발되었지만, 현재는 리버스 프록시, 로드 밸런서, HTTP 캐시 등 다양한 기능을 제공하며, 웹의 성능과 보안을 향상하는 데 널리 사용된다.&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: #0d0d0d; text-align: start;&quot;&gt;나는 Nginx가 지원하는 SSL/TLS 프토토콜을 이용하여 도메인을 HTTPS로 구성하여 보안을 강화할 목적으로 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;SSL/TLS&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;SSL(Secure Sockets Layer)과 TLS(Transport Layer Security)는 인터넷상에서 데이터를 안전하게 전송하기 위해 설계된 암호화 프로토콜이다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;SSL/TLS는&lt;/span&gt;&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;웹 브라우저와 서버 간, 또는 두 서버 간의 통신에서 데이터를 암호화하여 보안을 유지하는 데 사용된다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;SSL은 이 분야의 원조 프로토콜이고, TLS는 후속 버전으로 SSL 3.0에 기반하고 있지만, 보안과 효율성 등 개선 사항과 변경사항이 많아 별도의 이름으로 구분된다. 하지만, 기술적으로 연속성이 있고 많은 사람이 SSL이라는 용어에 익숙하기 때문에 SSL이라 통칭하기도 하며 SSL/TLS처럼 붙여서 부르기도 한다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Nginx 설치&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1708285555394&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt update
$ sudo apt upgrade&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;먼저, apt를 이용하여 nginx를 설치하기 위해, apt 패키지를 업데이트해 준다.&lt;/p&gt;
&lt;p data-ke-size=&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_1708285279952&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt install nginx -y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;그리고 앞서 진행하던 EC2 ubuntu 서버에 nginx를 설치한다.&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: #0d0d0d; text-align: start;&quot;&gt;잘 설치가 진행되었다면, 아래 명령어를 통해 상태를 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708285387496&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo systemctl status nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baBGun/btsEWMQhrRC/Jub011GP7uZ9AJU1evKJIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baBGun/btsEWMQhrRC/Jub011GP7uZ9AJU1evKJIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baBGun/btsEWMQhrRC/Jub011GP7uZ9AJU1evKJIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaBGun%2FbtsEWMQhrRC%2FJub011GP7uZ9AJU1evKJIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;189&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;SSL/TLS 설정&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx의 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SSL/TLS&lt;/span&gt; 설정에는 인증서가 필요한데, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Let's Encrypt를 사용하여 무료로 쉽게 발급받을 수 있다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Let's Encrypt는 자동화된 무료 개방형 인증 기관으로 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;비용이나 복잡한 절차 없이 누구나 SSL/TLS 인증서를 발급받을 수 있게 하는 서비스이다. 무료인 대신 만료기한이 90일로 주기적으로 재발급을 해야 하고, 실제 서비스를 운영한다면 보안등급이 더 높은 유료 인증서를 사용해야 한다.&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;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Certbot은 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Let's Encrypt의 파트너 소프트웨어로, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt; SSL/TLS 인증서의 발급부터 설치, 갱신에 이르기까지 전 과정을 자동화한다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Certbot을 이용하면 사용자는 간단한 몇 가지 명령어로 HTTPS 설정을 쉽게 할 수 있으며, 90일 주기로 인증서를 재갱신하는 것도 자동으로 해 준다.&lt;/span&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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: #0d0d0d; text-align: start;&quot;&gt;Let's Encrypt 설치&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708286757330&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt-get install letsencrypt&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;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Certbot 설치&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708286766346&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt-get install certbot python3-certbot-nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;b&gt;Certbot - Nginx 연결&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708286970146&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo certbot --nginx&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_1708287097059&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 이메일 입력
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# 약관동의
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# 이메일 수신동의
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Account registered.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정은 본인에 맞게 설정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;b&gt;HTTPS 적용 도메인 설정&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHGeZr/btsEYPMfkUv/yPF1fkqeLhNVYkJmhKd8E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHGeZr/btsEYPMfkUv/yPF1fkqeLhNVYkJmhKd8E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHGeZr/btsEYPMfkUv/yPF1fkqeLhNVYkJmhKd8E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHGeZr%2FbtsEYPMfkUv%2FyPF1fkqeLhNVYkJmhKd8E0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;809&quot; height=&quot;198&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Https를 적용하고 싶은 도메인을 고를 수 있고, 모든 도메인을 선택하려면 그냥 엔터를 누르면 된다.&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;1197&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qt7dI/btsEZnIF2qO/CVNAvANG6fsRGHpr3zCop0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qt7dI/btsEZnIF2qO/CVNAvANG6fsRGHpr3zCop0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qt7dI/btsEZnIF2qO/CVNAvANG6fsRGHpr3zCop0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQt7dI%2FbtsEZnIF2qO%2FCVNAvANG6fsRGHpr3zCop0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1197&quot; height=&quot;254&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이미 발급된 인증서가 있다면, 재 발급받을 것인지 물어보는 창이 나온다. 재발급받고 싶다면 2를 눌러 재발급을 신청한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP-HTTPS 리다이렉트 설정&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnaIU4/btsE2BlFjwu/FYBFk1ndahd8JJNUg04L50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnaIU4/btsE2BlFjwu/FYBFk1ndahd8JJNUg04L50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnaIU4/btsE2BlFjwu/FYBFk1ndahd8JJNUg04L50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnaIU4%2FbtsE2BlFjwu%2FFYBFk1ndahd8JJNUg04L50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;882&quot; height=&quot;197&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;모든 HTTP 트래픽을 HTTPS로 리다이렉트 하려면 2를, 2 기존 HTTP 설정을 유지하고 싶다면 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYvTt0/btsE5Cdt3JO/BI5a1nL4jKhzBKHAHLqYq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYvTt0/btsE5Cdt3JO/BI5a1nL4jKhzBKHAHLqYq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYvTt0/btsE5Cdt3JO/BI5a1nL4jKhzBKHAHLqYq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYvTt0%2FbtsE5Cdt3JO%2FBI5a1nL4jKhzBKHAHLqYq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;148&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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: #0d0d0d; text-align: start;&quot;&gt;이런 메시지가 나오면 성공적으로 설정이 완료되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Nginx 환경설정&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSL/TLS 인증서 발급을 마쳤다면, Nginx의 설정에서 SSL/TLS 인증서를 사용하도록 변경해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx가 읽어 들이는 핵심 설정파일은 /etc/nginx/nginx.conf 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 아래와 같은 구성을 하고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708297234733&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
    worker_connections 768;
    # multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] &quot;$request&quot; '
                    '$status $body_bytes_sent &quot;$http_referer&quot; '
                    '&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;';

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}&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;nginx.conf 파일의 주요 구성은 다음과 같다.&lt;/p&gt;
&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. Global block&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx 서버 전체에 영향을 미치는 설정을 포함한다. 사용자의 수, 로그 파일의 위치, PID 파일의 경로 등을 설정할 수 있다.&lt;/p&gt;
&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. Events block&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결 처리에 관련된 설정을 포함한다. 여기서는 동시 연결 처리 방식이나 연결 수의 제한 등을 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. HTTP block&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP와 관련된 설정을 포함하며, 여러 개의 서버 블록(Server blocks)을 포함할 수 있다. 이 서버 블록에 우리가 주로 변경해야 하는 설정들이 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 nginx.conf 파일을 보면 Http 블록 가장 아래에 다음과 같은 명령어가 포함되어 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708297549773&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;&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;즉, /etc/nginx/conf.d/ 경로의. conf 확장자를 갖는 파일을 포함한다는 것이고, /etc/nginx/sites-enabled/ 의 모든 파일을 포함한다는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 우리는 nginx.conf 파일에서 직접 설정을 하지 않고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sites-enabled, sites-&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;available, conf.d&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;여기서 sites-&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;available 안의 파일은, sites-enabled 폴더에 심볼릭 링크로 연결되어 nginx 설정이 가능해진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;sites-enabled&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;/etc/nginx/sites-enabled/default 파일로, 실제로 Nginx에 의해 읽히며 활성화된 사이트의 설정을 포함한다. 일반적으로&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;sites-available에 있는 설정 파일의 심볼릭 링크를 생성해 두고, 활성화하거나 비활성화하여 실제 설정 파일을 옮기지 않고도 관리를 쉽게 할 수 있게 해 준다.&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;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;여기서 심볼릭 링크는 윈도우에서 바로 가기와 비슷한 개념이며, 원본 파일이나 디렉터리로의 포인터 역할을 한다. 즉, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;/etc/nginx/sites-enabled/default의 심볼릭 링크가 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;sites-available/default로 설정되어 있으면, 어느 한 파일이 수정되면 같이 바뀌게 된다.&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 data-ke-size=&quot;size16&quot;&gt;아래 명령어로&amp;nbsp; 심볼릭 링크가 있는지 확인이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1708295881474&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ls -l /etc/nginx/sites-enabled/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;37&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpRDvl/btsEXtpoS1q/bFQpmpfxSrPmevSPIjQpCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpRDvl/btsEXtpoS1q/bFQpmpfxSrPmevSPIjQpCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpRDvl/btsEXtpoS1q/bFQpmpfxSrPmevSPIjQpCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpRDvl%2FbtsEXtpoS1q%2FbFQpmpfxSrPmevSPIjQpCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;37&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;37&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;위와 같은 결과가 뜬다면, default라는 이름으로 해당 경로의 심볼릭 링크가 존재한다는 것을 의미한다.&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: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;만약 심볼릭 링크를 생성하거나 제거하려면 아래와 같은 명령어를 입력하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708296233812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 심볼릭 링크 제거
$ sudo rm /etc/nginx/sites-enabled/default

# 심볼릭 링크 생성
$ sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/&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;sites-enabled 폴더 안의 default 파일의 경우 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;sites-available 폴더 안의 설정파일을 심볼릭 링크로 연결해 주기만 하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;sites-available&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;/etc/nginx/sites-available/ 디렉터리 내부의 파일로, 가능한 모든 웹사이트의 서버 블록 설정 파일을 보관한다. 여기에는 활성화되지 않은 사이트의 설정도 포함할 수 있으며, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;sites-enabled의 심볼릭 링크로 활성화할 수 있다.&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: #0d0d0d; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;일반적으로 이 폴더 안에 여러 개의 설정 파일을 두고, 상황에 따라 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;sites-enabled 폴더로 심볼릭 링크를 활성화/비활성화하면서 서버의 설정을 전환한다.&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;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;/etc/nginx/sites-available/&lt;span&gt; 안의 파일은 일반적으로 아래와 같이 구성되며, &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;span&gt;해당 예시에는 SSL 설정이 포함되어 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708299950456&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri; # 모든 HTTP 요청을 HTTPS로 리다이렉트
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # SSL 인증서 경로
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # SSL 키 경로

    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    
    location / {
        root   /usr/share/nginx/html; # 해당 디렉터리를 경로로 잡음
        index  index.html;
    }
}&lt;/code&gt;&lt;/pre&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;이 Server 블록은 하나의 웹 사이트나 애플리케이션을 나타내며, server_name, listen, location 블록으로 구성된다. 또한 특정 도메인에 대한 요청을 처리하는 방식을 구성한다.&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;&lt;b&gt;listen&lt;/b&gt;은 특정 포트와 IP 주소에 대한 리스닝을 설정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;server_name&lt;/b&gt; 에는 처리할 도메인 이름을 지정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Location block&lt;/b&gt;은&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;특정 요청 URI에 대한 처리 방식을 정의한다. 정적 파일의 제공, 프록시 서버로의 요청 전달, 요청에 대한 특정 처리 등을 설정할 수 있다.&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;
&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;이제 하나하나 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1708301203984&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri; # 모든 HTTP 요청을 HTTPS로 리다이렉트
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;해당 server 블록은 80 포트 http로 오는 모든 요청을 https로 리다이렉트 해주는 역할을 한다.&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;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고, 다음 server 블록은 443 포트 https로 오는 모든 요청을 처리한다.&lt;/p&gt;
&lt;pre id=&quot;code_1708307795992&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen 443 ssl;
    server_name example.com www.example.com;

    access_log /var/log/nginx/access.log; # 성공 로그 저장 경로
    error_log /var/log/nginx/error.log; # 실패 로그 저장 경로&lt;/code&gt;&lt;/pre&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 아래 부분이 ssl 인증서를 지정해 주는 부분이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 부분 경로에는 fullchain.pem 경로와 privkey.pem 경로를 지정해 준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 아래와 같은 경로이고, example.com을 본인의 도메인으로 대체해 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1708307172451&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # SSL 인증서 경로
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # SSL 키 경로

ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_protocols TLSv1.2 TLSv1.3;  # 서버에서 지원할 TLS 프로토콜의 버전
ssl_session_cache shared:SSL:10m; # 10MB의 공유 메모리를 SSL 세션 캐시로 사용하겠다는 의미
ssl_session_timeout 10m; # 세션 유효기간 10분 설정&lt;/code&gt;&lt;/pre&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: #0d0d0d; text-align: left;&quot;&gt;&amp;nbsp;&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;
&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: #0d0d0d; text-align: left;&quot;&gt;그 후, 아래 부분은 정적 파일을 로딩해 주는 부분이다.&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: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;/ 로 들어오는 모든 URI는 해당 로케이션 블록을 통해 정적 파일이 서빙된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708390822645&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;location / {
    root   /usr/share/nginx/html; # 해당 디렉터리에
    index  index.html; # index.html 파일을 서빙한다
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;이 Location 블록의 경우에는 프록시&lt;span&gt;&amp;nbsp;&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: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;나의 경우에는 아래와 같이 설정하여 / 루트 경로로 들어오는 모든 URL을 도커에서 실행 중인 3000 포트로 서빙해 주는 식으로 사용하였다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708309504652&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;location / {
        proxy_pass https://localhost:3000; # 특정 요청을 전달하는 역할 / 이 경우 도커로
        proxy_set_header Host $host; # 원래 요청의 호스트 이름을 대상 서버에 전달
        proxy_set_header X-Real-IP $remote_addr; # 클라이언트의 실제 IP 주소를 대상 서버에 전달
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  #  요청이 전달된 클라이언트의 IP 주소 목록을 대상 서버에 전달
        proxy_set_header X-Forwarded-Proto $scheme; # 원래 요청에서 사용된 프로토콜(예: http, https)을 대상 서버에 전달
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;conf.d&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;/etc/nginx/conf.d/~~. conf&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt; 파일로, Nginx는 구동 시 이 디렉터리 내의. conf 확장자를 가진 모든 파일을 자동으로 읽어 들인다. 이 디렉터리에는 개별 설정 등을 심볼릭 링크를 관리할 필요 없이 설정을 추가하거나 제거하기 편리함을 제공한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708288754407&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;log_format  main  '$remote_addr - $remote_user [$time_local] &quot;$request&quot; '
                  '$status $body_bytes_sent &quot;$http_referer&quot; '
                  '&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;';

access_log  /var/log/nginx/access.log  main;
error_log /var/log/nginx/error.log warn;&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;예를 들어, 위와 같이 로그 파일을 저장하는 설정을 할 수 있으며, Http 블록 안에 직접적으로 들어가는 코드는 블록으로 구성할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Nginx 설정 파일 검증&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708288447744&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo nginx -t&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정을 마쳤으면 위 코드를 입력하여 정상적으로 설정이 되었는지 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;35&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceMxMn/btsEYZg5DRJ/166WOzfAH0g3efmAmBMnxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceMxMn/btsEYZg5DRJ/166WOzfAH0g3efmAmBMnxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceMxMn/btsEYZg5DRJ/166WOzfAH0g3efmAmBMnxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceMxMn%2FbtsEYZg5DRJ%2F166WOzfAH0g3efmAmBMnxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;35&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;35&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 OK 표시가 떴다면 정상적으로 설정이 된 것이고, 오류가 있다면 아래와 같이 오류가 난 부분을 알려준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;45&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kAWSY/btsE3o8CI0R/Me6EeZfTBhajZz3x5KUDuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kAWSY/btsE3o8CI0R/Me6EeZfTBhajZz3x5KUDuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kAWSY/btsE3o8CI0R/Me6EeZfTBhajZz3x5KUDuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkAWSY%2FbtsE3o8CI0R%2FMe6EeZfTBhajZz3x5KUDuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;45&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;45&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;Nginx 시작&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708389133670&quot; class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ sudo systemctl start nginx&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;Nginx 재시작&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708288458465&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo systemctl reload nginx&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;설정 파일을 수정한 이후엔, 수정 사항이 적용될 수 있도록 Nginx를 재시작해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CgiaU/btsE0hoBGYD/E4Pp9o2CHSrdJ0HkN3mhV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CgiaU/btsE0hoBGYD/E4Pp9o2CHSrdJ0HkN3mhV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CgiaU/btsE0hoBGYD/E4Pp9o2CHSrdJ0HkN3mhV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCgiaU%2FbtsE0hoBGYD%2FE4Pp9o2CHSrdJ0HkN3mhV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;296&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 도메인으로 접속을 하게 되면, 위 화면과 같이 Nginx의 기본적인 웰컴 페이지가 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Nginx의 설정에서 location 블록을 적절히 수정하여 요청을 받을 때 React의 정적 파일을 서빙해 주어 페이지를 렌더링 하거나, 요청을 다른 서버로 넘겨주어 간단하게 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/80</guid>
      <comments>https://olrlobt.tistory.com/80#entry80comment</comments>
      <pubDate>Tue, 20 Feb 2024 10:30:03 +0900</pubDate>
    </item>
    <item>
      <title>[INFRA] EC2 서버 기본 설정과, SWAP메모리 할당하기</title>
      <link>https://olrlobt.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3062&quot; data-origin-height=&quot;1760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brbNRs/btsEZB0WrVY/fuPJteu4KTlziW6ModkE91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brbNRs/btsEZB0WrVY/fuPJteu4KTlziW6ModkE91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brbNRs/btsEZB0WrVY/fuPJteu4KTlziW6ModkE91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrbNRs%2FbtsEZB0WrVY%2FfuPJteu4KTlziW6ModkE91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3062&quot; height=&quot;1760&quot; data-origin-width=&quot;3062&quot; data-origin-height=&quot;1760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;EC2&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Amazon EC2(Elastic Compute Cloud)는 Amazon Web Services(AWS)가 제공하는 클라우드 기반의 가상 서버 호스팅 서비스이다.&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: #0d0d0d; text-align: start;&quot;&gt;EC2는 사용자가 필요에 따라 컴퓨팅 파워를 쉽게 확장하거나 축소할 수 있는 유연성을 제공하여, 다양한 컴퓨팅 작업을 클라우드에서 실행할 수 있도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. EC2 서버 생성하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에 접근하여 계정을 생성한 후, EC2 서버를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 SSAFY에서 제공되는 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;/p&gt;
&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://olrlobt.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.03.25 - [Infra] - [INFRA] AWS EC2 프리티어 인스턴스 생성하기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711305583274&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;[INFRA] AWS EC2 프리티어 인스턴스 생성하기&quot; data-og-description=&quot;AWS (Amazon Web Services) AWS는 Amazon이 제공하는 클라우드 컴퓨팅 플랫폼 및 인프라 서비스 모음이다. 2006년에 시작된 AWS는 가상 컴퓨터, 스토리지, 데이터베이스, 네트워킹, 분석, 머신 러닝, 모바일, &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/83&quot; data-og-url=&quot;https://olrlobt.tistory.com/83&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cDn2Za/hyVDDBpHGk/wqPIFKYaORntbdCkTA3X50/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/79Cpl/hyVDvcjcXr/8OE6rFzrLkT7oH2oaXwwnk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/CJmEB/hyVDFTuUAT/IshQbNuETj06yttPsbpME1/img.png?width=2395&amp;amp;height=1472&amp;amp;face=0_0_2395_1472&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/83&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cDn2Za/hyVDDBpHGk/wqPIFKYaORntbdCkTA3X50/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/79Cpl/hyVDvcjcXr/8OE6rFzrLkT7oH2oaXwwnk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/CJmEB/hyVDFTuUAT/IshQbNuETj06yttPsbpME1/img.png?width=2395&amp;amp;height=1472&amp;amp;face=0_0_2395_1472');&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;[INFRA] AWS EC2 프리티어 인스턴스 생성하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AWS (Amazon Web Services) AWS는 Amazon이 제공하는 클라우드 컴퓨팅 플랫폼 및 인프라 서비스 모음이다. 2006년에 시작된 AWS는 가상 컴퓨터, 스토리지, 데이터베이스, 네트워킹, 분석, 머신 러닝, 모바일,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. EC2 서버 접근&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1708192850952&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ssh -i &amp;lt;pem키 경로&amp;gt; ubuntu@&amp;lt;도메인&amp;gt;
$ ssh -i /path/to/your-key.pem ubuntu@example.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;757&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGE0FS/btsEYYvlXtW/uJXi1rxTkFxv1HKOmhw1A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGE0FS/btsEYYvlXtW/uJXi1rxTkFxv1HKOmhw1A0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGE0FS/btsEYYvlXtW/uJXi1rxTkFxv1HKOmhw1A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGE0FS%2FbtsEYYvlXtW%2FuJXi1rxTkFxv1HKOmhw1A0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;444&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;757&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 패키지 업데이트, 업그레이드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1708265783601&quot; class=&quot;elixir&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt update
$ sudo apt upgrade&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sudo apt update&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;:&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;apt&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(Advanced Package Tool)의 로컬 시스템의 패키지 인덱스를 최신 상태로 업데이트한다. 실제 업데이트가 아닌, 업데이트 가능한지 정보를 가져오는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sudo apt upgrade&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;update로&lt;span&gt;&amp;nbsp;&lt;/span&gt;업데이트된&lt;span&gt;&amp;nbsp;&lt;/span&gt;패키지 인덱스를 바탕으로, 설치된 소프트웨어를&lt;span&gt;&amp;nbsp;&lt;/span&gt;업그레이드한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. EC2 서버 시간 변경&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1708192595659&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo timedatectl set-timezone Asia/Seoul&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;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;/p&gt;
&lt;pre id=&quot;code_1708192952455&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ date&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;38&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6gOXf/btsE2Eia6rU/crPWMLNcHLtO594CrWxtQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6gOXf/btsE2Eia6rU/crPWMLNcHLtO594CrWxtQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6gOXf/btsE2Eia6rU/crPWMLNcHLtO594CrWxtQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6gOXf%2FbtsE2Eia6rU%2FcrPWMLNcHLtO594CrWxtQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;301&quot; height=&quot;38&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;38&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. EC2 미러서버 변경&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 우분투 운영체제에서 패키지를 받으면, 패키지를 다운로드하는 서버가 &quot;http://kr.archive.ubuntu.com/ubuntu/&quot;로&lt;span style=&quot;color: #333333; text-align: left;&quot;&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;figure id=&quot;og_1708193421757&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;Mirrors : Ubuntu&quot; data-og-description=&quot;Ubuntu also includes a wide variety of software through its network of software repositories. Once your system is installed you can simply call up a list of all the existing tools out there and choose any of them for immediate installation over the interne&quot; data-og-host=&quot;launchpad.net&quot; data-og-source-url=&quot;https://launchpad.net/ubuntu/+cdmirrors&quot; data-og-url=&quot;https://launchpad.net/ubuntu/+cdmirrors&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ZGoRY/hyVjckrcDU/yE7LrTOjVLYdSvVMbN7LT1/img.png?width=192&amp;amp;height=192&amp;amp;face=0_0_192_192&quot;&gt;&lt;a href=&quot;https://launchpad.net/ubuntu/+cdmirrors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://launchpad.net/ubuntu/+cdmirrors&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ZGoRY/hyVjckrcDU/yE7LrTOjVLYdSvVMbN7LT1/img.png?width=192&amp;amp;height=192&amp;amp;face=0_0_192_192');&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;Mirrors : Ubuntu&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ubuntu also includes a wide variety of software through its network of software repositories. Once your system is installed you can simply call up a list of all the existing tools out there and choose any of them for immediate installation over the interne&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;launchpad.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 링크에서 korea를 검색하면 사용 가능한 미러 서버들의 리스트가 보이는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 가장 빠른 kakao 미러 서버로 변경하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래 명령어로 sources.list 시스템 파일을 vim 텍스트 에디터로 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1708193529570&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo vim /etc/apt/sources.list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 아래 명령어를 통해 문자열을 일괄 교체한다.&lt;/p&gt;
&lt;pre id=&quot;code_1708194052250&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;:%s/ap-northeast-2.ec2.archive.ubuntu.com/mirror.kakao.com/&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;:%s&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 전체에서 검색 및 교체를 수행하라는 명령어로, 기존에 설정된 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;ap-northeast-2.ec2.archive.ubuntu.com 서버를&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: #0d0d0d; text-align: left;&quot;&gt;mirror.kakko.com으로 교체하라는 명령어이다.&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: #0d0d0d; text-align: left;&quot;&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRuTNn/btsEZAAR5yz/SgUK3kJX9KSOB2GF1tYqi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRuTNn/btsEZAAR5yz/SgUK3kJX9KSOB2GF1tYqi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRuTNn/btsEZAAR5yz/SgUK3kJX9KSOB2GF1tYqi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRuTNn%2FbtsEZAAR5yz%2FSgUK3kJX9KSOB2GF1tYqi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;836&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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 환경의 powerShell에서는 글씨가 잘 보이지 않지만, 빨간 글씨 부분이 변경된 것을 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708194149489&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;:wq // 저장하고 나가기
:q! // 강제로 저장 안 하고 나가기&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;수정이 끝났다면, 위 명령어를 통해 vim 편집기에서 저장하고 나가자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. SWAP 메모리 할당&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 무료 인스턴스를 사용한다면, 1GB 남짓의 적은 메모리를 사용하여 느려지거나 먹통이 되는 현상이 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유료로 전환할 생각이 없는 서버라면, SWAP 메모리를 활용하여 메모리 부족 현상을 어느 정도 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SWAP 메모리는 리눅스에서 RAM이 부족할 경우, 디스크의 일정 공간을 마치 RAM처럼 사용할 수 있게 해 준다.&amp;nbsp;&lt;/p&gt;
&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_1708257352101&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ free -h&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/msOkt/btsEYDd939L/EH4ykUEaA52ZldAzRjTGc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/msOkt/btsEYDd939L/EH4ykUEaA52ZldAzRjTGc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/msOkt/btsEYDd939L/EH4ykUEaA52ZldAzRjTGc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmsOkt%2FbtsEYDd939L%2FEH4ykUEaA52ZldAzRjTGc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;64&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;table style=&quot;border-collapse: collapse; width: 65.3489%; height: 184px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50.3597%;&quot;&gt;RAM&lt;/td&gt;
&lt;td style=&quot;width: 49.4077%;&quot;&gt;권장 스왑 공간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50.3597%;&quot;&gt;~ 2GB&lt;/td&gt;
&lt;td style=&quot;width: 49.4077%;&quot;&gt;RAM의 2배 ( 32MB보다 작으면 안 됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50.3597%;&quot;&gt;2GB ~ 64GB&lt;/td&gt;
&lt;td style=&quot;width: 49.4077%;&quot;&gt;RAM의 0.5배&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50.3597%;&quot;&gt;64GB ~&lt;/td&gt;
&lt;td style=&quot;width: 49.4077%;&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;자신의 메모리 용량에 맞게 Swap파일을 생성한다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708257450378&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ sudo dd if=/dev/zero of=/swapfile bs=128M count=16&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;dd:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data duplicator의 약자로, 파일이나 파일 시스템의 변환 및 복사에 사용된다.&lt;/p&gt;
&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;if=/dev/zero:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 파일로 /dev/zero를 사용한다. /dev/zero는 무한히 0으로 채워진 데이터 스트림을 제공하는 특수 디바이스 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;of=/swapfile:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 파일로 /swapfile을 사용한다. 이는 생성될 swap 파일의 경로와 이름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;bs=128M:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록 사이즈를 128MB로 설정한다. dd 명령어가 한 번에 읽고 쓰는 데이터 양을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;count=16:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bs 명령어에서 지정한 블록 사이즈의 데이터를 16번 쓰라는 의미이다. 128x16=2048 이므로 2GB의 Swap 파일이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 16GB의 Swap 메모리를 할당할 때는 아래와 같이 작성할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708257450378&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ sudo dd if=/dev/zero of=/swapfile bs=128M count=128&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;이때, bs 블록 사이즈의 크기는 한 번에 읽고/쓰는 데이터의 양을 결정한다. 이 값이 크거나 작다고 무조건 좋은 것이 아니며, bs가 작을수록 작업당 오버헤드가 높아질 수 있고, bs가 클수록 시스템 자원 사용에 영향을 미치기 때문에, 실험을 통해 적절한 값을 선택하는 것이 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스왑 파일의 권한 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708257016304&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo chmod 600 /swapfile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스왑 파일을 루트 사용자만이 읽고 쓸 수 있도록 변경한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스왑 영역을 설정한다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708257053583&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo mkswap /swapfile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스왑 영역을 활성화한다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708257101591&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo swapon /swapfile&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_1708257199116&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo swapon -s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXWv5p/btsEZbVMqUr/n2rjhzET3fsprHjXW5UpyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXWv5p/btsEZbVMqUr/n2rjhzET3fsprHjXW5UpyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXWv5p/btsEZbVMqUr/n2rjhzET3fsprHjXW5UpyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXWv5p%2FbtsEZbVMqUr%2Fn2rjhzET3fsprHjXW5UpyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;690&quot; height=&quot;57&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;/etc/fstab 파일을 편집하여 부팅 시 스왑 파일을 활성화한다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1708257525202&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo vi /etc/fstab&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_1708257572522&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/swapfile swap swap defaults 0 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkvA5w/btsEWyxGJLU/AKKEKyG0ZLz7C0Vl438ym1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkvA5w/btsEWyxGJLU/AKKEKyG0ZLz7C0Vl438ym1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkvA5w/btsEWyxGJLU/AKKEKyG0ZLz7C0Vl438ym1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkvA5w%2FbtsEWyxGJLU%2FAKKEKyG0ZLz7C0Vl438ym1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;59&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;UFW (&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Uncomplicated Firewall&lt;/span&gt; )&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Uncomplicated Firewall은 Ubuntu 및 기타 Linux 배포판에서 사용하는 쉬운 방화벽 관리 도구이다. iptables의 복잡성을 추상화하여 사용자가 더 간단한 명령어를 통해 방화벽 규칙을 설정하고 관리할 수 있게 해 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UFW 상태 확인하기:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708267187681&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo ufw status&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AKFc6/btsE0rX3h1z/SfRR7BeSvkXfTTT5dr8l21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AKFc6/btsE0rX3h1z/SfRR7BeSvkXfTTT5dr8l21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AKFc6/btsE0rX3h1z/SfRR7BeSvkXfTTT5dr8l21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAKFc6%2FbtsE0rX3h1z%2FSfRR7BeSvkXfTTT5dr8l21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;393&quot; height=&quot;286&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽 상태를 확인하면 UFW가 지금 활성화가 되어 있는지, 허용된 포트는 어느 포트인지 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UFW 활성화하기:&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708267064580&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo ufw enable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특정 포트 허용하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708267082583&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo ufw allow 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특정 포트 차단하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708267092831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo ufw deny 80&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;table style=&quot;border-collapse: collapse; width: 27.3256%; height: 71px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;포트&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;22&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;80&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Http&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;443&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Https&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: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;UFW를 사용할 때는 실수로 자신을 잠그는 상황(예: SSH 접속 차단)을 피하기 위해 주의해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 마치게 되면, 기본적인 EC2 설정은 마쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에는 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Amazon EC2 인스턴스에 Nginx를 설치하고 기본 설정을 마친 후, HTTPS를 사용하기 위해 SSL/TLS 인증서를 발급받고 구성하는 과정을 진행할 예정이다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-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>Infra</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/79</guid>
      <comments>https://olrlobt.tistory.com/79#entry79comment</comments>
      <pubDate>Mon, 19 Feb 2024 00:05:56 +0900</pubDate>
    </item>
    <item>
      <title>[Git/Error] Merge blocked: merge confilcts must be resolved. 깃 병합 오류</title>
      <link>https://olrlobt.tistory.com/78</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Merge&amp;nbsp;blocked:&amp;nbsp;merge&amp;nbsp;confilcts&amp;nbsp;must&amp;nbsp;be&amp;nbsp;resolved.&lt;/b&gt;&lt;/h2&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;현재 우리 프로젝트는 한 레퍼지토리 안에서 BE/FE로 프로젝트 폴더를 만들고,&amp;nbsp;이 안에서 각각의 develop 브랜치에서 feature로 나누어 기능을 개발하는 브랜치 전략을 사용하고 있다.&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;develop을 FE와 BE가 다른 브랜치에서 사용하였는데 release 브랜치를 만들 계획이 없고, FE와 BE가 한 레퍼지토리 안에서 작업하기 때문에 구분을 위해서 이런 방식으로 진행을 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 FE와 BE를 한 파이프라인 안에서 배포를 진행하기 위해 병합하는 과정을 진행하려는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitLab에서 MR을 날리는 과정에서 다음과 같은 오류가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UiQaA/btsEAbWoW7O/8n30i6b4oGI1y55XpKe34k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UiQaA/btsEAbWoW7O/8n30i6b4oGI1y55XpKe34k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UiQaA/btsEAbWoW7O/8n30i6b4oGI1y55XpKe34k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUiQaA%2FbtsEAbWoW7O%2F8n30i6b4oGI1y55XpKe34k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;778&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 병합을 진행할 수 없게 되었는데, Resolve locally를 누르면 다음과 같은 안내 화면이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGI9QO/btsEAinHFc2/Oyxl6zS8gbLbUwmSngnWbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGI9QO/btsEAinHFc2/Oyxl6zS8gbLbUwmSngnWbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGI9QO/btsEAinHFc2/Oyxl6zS8gbLbUwmSngnWbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGI9QO%2FbtsEAinHFc2%2FOyxl6zS8gbLbUwmSngnWbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;347&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히, 원격 저장소에서 자동으로 conflicts를 해결할 수 없으니 local에서 해결하라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문구를 마주했다면, local환경에서 conflicts를 해결하여 push 해 주면 금방 해결이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 우리가 마주한 오류는 조금 복잡한 에러였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ERROR&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtquR0/btsEAQR1e5o/DcKOVUwPGkGzX0GGHHaggK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtquR0/btsEAQR1e5o/DcKOVUwPGkGzX0GGHHaggK/img.png&quot; data-alt=&quot;Merge 현황 (좌) / 원래 프로젝트 (우)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtquR0/btsEAQR1e5o/DcKOVUwPGkGzX0GGHHaggK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtquR0%2FbtsEAQR1e5o%2FDcKOVUwPGkGzX0GGHHaggK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;786&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Merge 현황 (좌) / 원래 프로젝트 (우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 프로젝트로 checkout을 하면 우측 사진과 같이 프로젝트 파일들이 보인다. 하지만 Merge를 진행하면서 왼쪽 사진과 같이 파일들이 사라지는 에러였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이 상에서 Merge를 진행하게 되면 자동으로 Merge Conflicts를 처리해 주는 부분이 있어서, Terminal에서 Merge를 진행했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqxbcE/btsEy2MdUxk/G84FLWy268Kqv4GNl9FjK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqxbcE/btsEy2MdUxk/G84FLWy268Kqv4GNl9FjK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqxbcE/btsEy2MdUxk/G84FLWy268Kqv4GNl9FjK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqxbcE%2FbtsEy2MdUxk%2FG84FLWy268Kqv4GNl9FjK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;355&quot; height=&quot;761&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;761&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그랬더니 위 사진과 같이 Conflicts난 파일들이 있었는데, 해당 파일들은 Master에서는 Deleted 처리가 되어 있고, FEDev 브랜치에서는 Modified 처리가 되어 있었다. 따라서 Conflicts Resolve를 진행하면, Deleted와 Modified 중에 선택으로 충돌을 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 해당 파일들을 Modified로 전부 살려 주어도 내가 원하는 파일들은 돌아오지 않았는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Commit 내역에 아래와 같이 회색 처리 된 파일들이 보였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pe4mC/btsECsvORSM/lV00NX1KYbxcUKk11ICGRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pe4mC/btsECsvORSM/lV00NX1KYbxcUKk11ICGRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pe4mC/btsECsvORSM/lV00NX1KYbxcUKk11ICGRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPe4mC%2FbtsECsvORSM%2FlV00NX1KYbxcUKk11ICGRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;342&quot; height=&quot;875&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일들은 Conflicts를 해결할 수 없는 파일로, 변경사항을 저장하면 바로 사라져 버린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 인텔리제이에서 Merge를 바로 실행한다면 해당 파일들은 자동으로 삭제되어 Merge가 되는데, 이 때문에 나는 내가 Merge를 잘못 진행하고 있는 줄 알았다.&lt;/p&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 log를 이용하여 커밋 내역을 추적했다.&lt;/p&gt;
&lt;pre id=&quot;code_1707323881689&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git log &amp;lt;추적 파일 경로&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZcJd1/btsEBq6j8KE/zCI3TalM88vvubFiwkMmZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZcJd1/btsEBq6j8KE/zCI3TalM88vvubFiwkMmZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZcJd1/btsEBq6j8KE/zCI3TalM88vvubFiwkMmZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZcJd1%2FbtsEBq6j8KE%2FzCI3TalM88vvubFiwkMmZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;442&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 파일에 대한 내역을 오로지 하나, 심지어 initial commit인 프로젝트를 생성할 때 만들어진 파일이었다. 그렇다면 커밋 상으로 삭제된 파일은 아니라는 뜻이다.&lt;/p&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뿐만 아니라 BE로 파생된 브랜치들에서도 삭제되는 똑같은 현상이 일어났는데, 이로써 나는 Git이 추적을 못하고 있다는 것을 깨달았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;Gitlab repository가 생성되기 전에 FE팀에서는 github를 이용하여 일찍이 프로젝트를 시작하고 있다는 점이었다. 여기서 나는 FE팀이 코드를 옮기는 과정에서 github에서 파일을 생성한 기록을 추적하지 못할 것이라는 가설을 세우고, FE팀의 github를 찾아가서 파일을 비교해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctprOD/btsEAi865tq/dLgeLLn63LdqRKqXO8DtZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctprOD/btsEAi865tq/dLgeLLn63LdqRKqXO8DtZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctprOD/btsEAi865tq/dLgeLLn63LdqRKqXO8DtZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctprOD%2FbtsEAi865tq%2FdLgeLLn63LdqRKqXO8DtZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;563&quot; height=&quot;912&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;이야기를 들어보니 Github에서 GitLab으로 프로젝트를 복사하는 과정에서. git 파일이 누락되었고, 이에. git에 저장되어 있는 파일 생성 기록이 master 브랜치에서는 추적되지 않아, Merge 과정에서 생성 기록이 없기 때문에 자동으로 삭제처리가 되고 있던 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Confilts에서 Modified 된 파일들은 파일을 가져온 이후로, 파일 내용이 수정된 파일들이라는 것을 깨달았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해결&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래의 Github를 clone 받은 후에 Gitlab의 프로젝트를 Merge를 한다면, Github. git의 생성 정보와 Gitlab. git의 업데이트 정보가 합쳐져서 보존될 것이라고 생각하고 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 기존에 작업했던 Github를 클론 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 레퍼지를 Gitlab으로 remote 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707406148437&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone &amp;lt;github-repo-url&amp;gt;
git remote add gitlab &amp;lt;gitlab-repo-url&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;이렇게 해 주면, 아래와 같이 remote에 연결되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (16).png&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ei03Oe/btsEF4hBGdU/HmrOisqkeDy1w9HZvE6eP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ei03Oe/btsEF4hBGdU/HmrOisqkeDy1w9HZvE6eP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ei03Oe/btsEF4hBGdU/HmrOisqkeDy1w9HZvE6eP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fei03Oe%2FbtsEF4hBGdU%2FHmrOisqkeDy1w9HZvE6eP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;674&quot; data-filename=&quot;Untitled (16).png&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하단의 origin/master가 기존에 사용하던 Github 저장소이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단의 gitlab이 Gitlab 저장소에서 가져온 브랜치 현황들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 브랜치 origin/master를 Gitlab의 저장소에 push 해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707406325015&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git push gitlab &amp;lt;branch-name&amp;gt;:&amp;lt;new-gitlab-branch-name&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;이렇게 Gitlab에 기존 저장소를 생성해 주고 Merge를 진행하면 될 줄 알았지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽게도, 기존 branch의. git의 정보들이 연결되지 않는지, 똑같이 deleted 처리가 되는 문제가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, IntelliJ에서 제공하는 auto merge가 아닌,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terminal로 옵션을 설정해서 merge를 진행해 주었는데, 설정한 옵션은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&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_1707407164329&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git merge --no-ff origin/&amp;lt;sourceBranch&amp;gt; --allow-unrelated-histories&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;--no-ff&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git은 기본적으로 Fast Forward Merge를 기본 옵션으로 Merge를 진행한다. 하지만 나의 경우에는 Merge를 BE와 FE에서 모두 해 오면서 기록을 남기고 싶었기 때문에 --no-ff를 사용하여 No Fast Forward Merge를 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;--allow-unrelated-histories&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 옵션은 두 브랜치 간에 공통된 조상이 없을 때 사용한다. 이 옵션을 사용하면 Git은 두 이력이 서로 관련이 없어도 병합을 허용하게 되고, 이를 통해 서로 다른 두 브랜치의 소스 코드를 하나로 합칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 두 옵션을 사용하여 Merge를 진행하였고, 당연히 Conflicts가 발생하였다.&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;1385&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mpoTe/btsEFQRpRf0/MYxYK5k6hw9qprhisRs3P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mpoTe/btsEFQRpRf0/MYxYK5k6hw9qprhisRs3P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mpoTe/btsEFQRpRf0/MYxYK5k6hw9qprhisRs3P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmpoTe%2FbtsEFQRpRf0%2FMYxYK5k6hw9qprhisRs3P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1385&quot; height=&quot;630&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terminal 상에서 발생한 conflicts는 intellJ의 commit 내역에서 쉽게 해결할 수 있었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 기존의 Github 브랜치가 아닌, Gitlab의 것을 가져오면 되기 때문에 쉽게 해결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 진행하고 나니, 거의 모든 파일들이 생성되어 있는 체로 Merge가 진행되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이유는 모르겠지만 파일이 1~2개 정도 누락이 되었었는데, 해당 파일만 따로 추가해 주는 방식으로 문제를 해결하였다.&lt;/p&gt;</description>
      <category>Error</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/78</guid>
      <comments>https://olrlobt.tistory.com/78#entry78comment</comments>
      <pubDate>Thu, 8 Feb 2024 01:56:22 +0900</pubDate>
    </item>
    <item>
      <title>[MySQL/Error] MySQL Workbench: Could not acquire management access for adminstraion 에러</title>
      <link>https://olrlobt.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MySQL Workbench 8.0 CE&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Could not acquire management access for adminstraion 에러.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 MySQL 워크벤치를 설치하였을 때는 문제없이 잘 작동하였다. 하지만, PC를 껐다 켰을 때, 이런 오류를 만날 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (11).png&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMSW4s/btsEngpsbvu/2uUlC3cSiiPgAw7DU0UBE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMSW4s/btsEngpsbvu/2uUlC3cSiiPgAw7DU0UBE1/img.png&quot; data-alt=&quot;Error : Could not acquire management access for adminstraion&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMSW4s/btsEngpsbvu/2uUlC3cSiiPgAw7DU0UBE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMSW4s%2FbtsEngpsbvu%2F2uUlC3cSiiPgAw7DU0UBE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;479&quot; height=&quot;289&quot; data-filename=&quot;Untitled (11).png&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Error : Could not acquire management access for adminstraion&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;발생원인&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 8.0.23 이상에서 발생하는 오류이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window 환경의 64비트에서 chcp.com 파일을 찾지 못하여 &lt;b&gt;MySQL 서버가 실행되지 못해 발생하는 오류&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;chcp.com은 Window의 시스템 파일로, &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Change Code Page(코드 페이지 변경) 컴퓨터에서 문자 인코딩 설정을 변경하는 곳에 사용하는 파일이다.&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: #374151; text-align: start;&quot;&gt;여러 곳을 찾아본 결과 64비트의 chcp.com은 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;C:\Windows\System32`&lt;/span&gt;과 `C:\Windows\SysWOW64`에 존재하는데, MySQL에서는 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;C:\Windows\SysWOW64`에 존재하는 chcp.com 파일 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;C:\Windows\SysWOW64`&lt;/span&gt; 을 찾지 못하는 문제가 있어서 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;C:\Windows\System32`의 chcp.com 파일을 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;C:\Windows\SysWOW64`로 덮었으라는 해결책을 곳곳에서 제시한다.&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;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;하지만, &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;chcp.com 파일은 시스템 파일로, 그렇게 막 변경해서는 안 되고, 변경하기도 쉽지 않다.&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;따라서 아래의 세 가지 방법을 제시한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151;&quot;&gt;해결방법&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 일시적인 가장 빠른 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;급하게 해결할 방법을 찾는 사람은 이 방법을 권한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 작업 관리자의 서비스에서 MySQL80을 찾아서 우클릭해 주고, 실행시켜 주는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 강제적으로 MySQL 서버를 실행시켜 주기 때문에 PC를 다시 시작하면 해당 방법을 다시 반복해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (15).png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KfWUh/btsEkg5xqTA/RazNKWb6aTRUTimywKhY3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KfWUh/btsEkg5xqTA/RazNKWb6aTRUTimywKhY3K/img.png&quot; data-alt=&quot;작업관리자로 MySQL 서버를 강제 실행한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KfWUh/btsEkg5xqTA/RazNKWb6aTRUTimywKhY3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKfWUh%2FbtsEkg5xqTA%2FRazNKWb6aTRUTimywKhY3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;888&quot; data-filename=&quot;Untitled (15).png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;작업관리자로 MySQL 서버를 강제 실행한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 일시적인 일반적인 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많은 블로그와 사이트에서 소개하고 있는 방식으로, 내가 처음 이 문제를 해결했던 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 역시 PC가 켜질 때마다 반복해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;시스템 &amp;gt; 실행 &amp;gt; services.msc&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (12).png&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WRkZP/btsEnWRXP7n/VoK04gd2O2fz0z1ngyOrk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WRkZP/btsEnWRXP7n/VoK04gd2O2fz0z1ngyOrk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WRkZP/btsEnWRXP7n/VoK04gd2O2fz0z1ngyOrk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWRkZP%2FbtsEnWRXP7n%2FVoK04gd2O2fz0z1ngyOrk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;444&quot; height=&quot;292&quot; data-filename=&quot;Untitled (12).png&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MySQL80을 강제 시작한다.&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (13).png&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k3nL3/btsEqUsl4DJ/dqEVyrQNMhn7YoKi3J9EA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k3nL3/btsEqUsl4DJ/dqEVyrQNMhn7YoKi3J9EA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k3nL3/btsEqUsl4DJ/dqEVyrQNMhn7YoKi3J9EA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk3nL3%2FbtsEqUsl4DJ%2FdqEVyrQNMhn7YoKi3J9EA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;733&quot; height=&quot;590&quot; data-filename=&quot;Untitled (13).png&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MySQL이 정상적으로 실행된다.&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Untitled (14).png&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/svGDu/btsEnhBVqBl/i7BUHiXuOShgYym4SvQFfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/svGDu/btsEnhBVqBl/i7BUHiXuOShgYym4SvQFfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/svGDu/btsEnhBVqBl/i7BUHiXuOShgYym4SvQFfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsvGDu%2FbtsEnhBVqBl%2Fi7BUHiXuOShgYym4SvQFfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;775&quot; height=&quot;784&quot; data-filename=&quot;edited_Untitled (14).png&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 지속적인 해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL이 chcp.com을 못 찾아서 발생하는 이 문제는 언어 설정에서 문제를 발생시키는데, 따라서 여러 사이트들에서 시스템 언어 설정을 변경하여 해결하였다는 글들이 보인다.&lt;/p&gt;
&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;따라서, 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;&lt;/p&gt;
&lt;pre id=&quot;code_1706993166589&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;C:\Program Files\MySQL\MySQL Workbench 8.0\workbench&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;먼저, 위 경로에서 os_utils.py 파일을 찾아 메모장으로 연다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;731&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLC4z7/btsEnfKO4Ul/KVzKQ2OjNzmsGjkeGihhq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLC4z7/btsEnfKO4Ul/KVzKQ2OjNzmsGjkeGihhq1/img.png&quot; data-alt=&quot;Ctrl + G로 줄 이동을 한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLC4z7/btsEnfKO4Ul/KVzKQ2OjNzmsGjkeGihhq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLC4z7%2FbtsEnfKO4Ul%2FKVzKQ2OjNzmsGjkeGihhq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;398&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;731&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ctrl + G로 줄 이동을 한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ctrl + G로 356번째 줄로 이동하면 인코딩 관련 설정을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1801&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ek0qKa/btsEkScfeYe/cXH9YkQ7lmGp03wCQ9tLkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ek0qKa/btsEkScfeYe/cXH9YkQ7lmGp03wCQ9tLkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ek0qKa/btsEkScfeYe/cXH9YkQ7lmGp03wCQ9tLkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fek0qKa%2FbtsEkScfeYe%2FcXH9YkQ7lmGp03wCQ9tLkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1801&quot; height=&quot;382&quot; data-origin-width=&quot;1801&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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_1706993295239&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;process = subprocess.Popen(command, stdin = subprocess.PIPE, text=True, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, shell=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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QvRoe/btsEoLbocCC/pmFeTFgscGKM0hrT7zIU70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QvRoe/btsEoLbocCC/pmFeTFgscGKM0hrT7zIU70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QvRoe/btsEoLbocCC/pmFeTFgscGKM0hrT7zIU70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQvRoe%2FbtsEoLbocCC%2FpmFeTFgscGKM0hrT7zIU70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;396&quot; height=&quot;346&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;에러 메시지를 잘 읽어보면 root 계정으로 접속할 수 없다는 말인데, MySQL의 파일을 직접 변경해 주었기 때문에 root 계정 설정을 다시 해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm8SGF/btsEpnByRfF/6MfyIDMCH4RzG9ffIhIlek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm8SGF/btsEpnByRfF/6MfyIDMCH4RzG9ffIhIlek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm8SGF/btsEpnByRfF/6MfyIDMCH4RzG9ffIhIlek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm8SGF%2FbtsEpnByRfF%2F6MfyIDMCH4RzG9ffIhIlek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;346&quot; height=&quot;253&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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 Installer를 실행해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;897&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dj3zQB/btsEnOGrdJH/jtcp2uGph7FB8Hc5wRYtwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dj3zQB/btsEnOGrdJH/jtcp2uGph7FB8Hc5wRYtwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dj3zQB/btsEnOGrdJH/jtcp2uGph7FB8Hc5wRYtwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdj3zQB%2FbtsEnOGrdJH%2Fjtcp2uGph7FB8Hc5wRYtwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;897&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;897&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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 Server의 Reconfigue를 통하여 비밀번호를 재 설정 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GIkSi/btsEmV0n4QU/ZYYzoVdjDZ3wF7Pf91G240/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GIkSi/btsEmV0n4QU/ZYYzoVdjDZ3wF7Pf91G240/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GIkSi/btsEmV0n4QU/ZYYzoVdjDZ3wF7Pf91G240/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGIkSi%2FbtsEmV0n4QU%2FZYYzoVdjDZ3wF7Pf91G240%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;892&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;897&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXH4YP/btsEkwmyUx7/SxoEqVwA49guWUKZhRwcV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXH4YP/btsEkwmyUx7/SxoEqVwA49guWUKZhRwcV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXH4YP/btsEkwmyUx7/SxoEqVwA49guWUKZhRwcV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXH4YP%2FbtsEkwmyUx7%2FSxoEqVwA49guWUKZhRwcV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;897&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;897&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Start the MySQL Server at System Startup을 체크해, 시스템이 부팅될 때 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;여기까지 마치면 PC를 재 시작하여도 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;&lt;/p&gt;</description>
      <category>Error</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/77</guid>
      <comments>https://olrlobt.tistory.com/77#entry77comment</comments>
      <pubDate>Sun, 4 Feb 2024 05:58:49 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 객체 소멸자 Finalizer와 Cleaner의 문제점과 대안책</title>
      <link>https://olrlobt.tistory.com/76</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Finalizer&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;finalize() 메서드는 java.lang.Object 클래스에 정의되어 있으며, 자바에서 객체가 가비지 컬렉션에 의해 제거될 때 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;즉, Finalizer는 자바에서 객체가 소멸될 때 마지막으로 수행할 수 있는 작업을 정의하는 데 사용된다. 주로 파일 핸들, 네트워크 연결, 데이터베이스 연결처럼 시스템 리소스를 정리하는 용도가 이런 작업이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, Finalizer는 예측할 수 없고, 상황에 따라 위험할 수 있어 불필요하며, 오작동, 낮은 성능, 이식성 문제의 원인으로 기본적으로는 쓰지 말아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;finalize()를 상속한 리소스 예제&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class Resource {

    private boolean isOpen;

    public Resource() {
        this.isOpen = true; // Resource open.
    }

    // 리소스 사용을 위한 메소드
    public void useResource() {
        if (!isOpen) {
            throw new IllegalStateException(&quot;Resource is closed.&quot;);
        }
    }

    public void closeResource() {
        isOpen = false; // Resource closed.
    }

    // finalize 메소드 오버라이드
    @Override
    protected void finalize() throws Throwable {

        if (isOpen) { 
            closeResource(); // 리소스가 아직 열려있다면, 리소스를 닫음
        }
        super.finalize();  // finalize 메소드를 super 클래스에게도 호출
    }

    public static void main(String[] args) {

        Resource resource = new Resource(); // 리소스 객체 생성
        resource.useResource(); // 리소스 사용

        // 리소스 해제를 명시적으로 호출하지 않음
        // 객체가 가비지 컬렉션될 때 finalize 메소드가 호출됨
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Finalizer 불확실한 실행&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer는 다음과 같은 방식으로 작동된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;객체가 더 이상 필요하지 않을 때, 즉 더 이상 참조되지 않을 때 객체는 GC의 대상이 된다.&lt;/li&gt;
&lt;li&gt;가비지 컬렉터가 이 객체를 회수하기 전에, JVM은 finalize() 메서드를 호출한다.&lt;/li&gt;
&lt;li&gt;finalize() 메서드에서는 객체가 사용하던 자원을 해제하거나, 정리하는 등의 작업을 수행할 수 있다.&lt;/li&gt;
&lt;li&gt;finalize() 메서드가 실행된 후, 객체는 가비지 컬렉터에 의해 실제로 메모리에서 제거된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer의 동작 방식에서 객체가 GC의 대상이 되어야만 작동한다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, GC 알고리즘에 따라 finalize()가 언제 실행될지 예측할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 자바에서 Finalizer 스레드는 일반적으로 다른 애플리케이션 스레드보다 낮은 우선순위를 가진다. 이는 시스템의 리소스가 제한적일 때나 시스템이 높은 부하 하에 있을 때 Finalizer 스레드가 실행될 기회를 충분히 얻지 못하게 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JAVA 명세에도 어떤 스레드가 Finalizer를 실행할지 구체적으로 명시하지 않고 있는데, 이는 JVM 구현에서 Finalizer의 동작이 일관되지 않을 수 있다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Finalizer 성능 저하&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에 Finalizer가 정의되어 있으면, GC는 객체를 단순히 메모리에서 회수하는 것 이상의 작업을 수행해야 한다. 이런 객체들은 일반 가비지 컬렉션 프로세스에서 제외되고, finalize()가 실행된 후에만 메모리에서 제거된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정은 GC의 효율을 떨어뜨려 전체 시스템의 성능에 영향을 미치게 된다. 또한, 낮은 우선순위로 인해 Finalizer 스레드가 실행되지 않으면, 해당 객체들이 메모리를 계속 점유하게 된다. 이는 리소스 해제 지연과 메모리 누수로 이어질 수 있으며, 시스템의 전반적인 성능에 영향을 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Finalizer 동시성 문제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer는 다른 스레드에서 비동기적으로 실행되므로, 동시성 문제를 야기할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Finalizer 내에서 예외가 발생하면, 이는 무시되며 객체의 정상적인 정리 과정을 방해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. Finalizer 보안 문제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 Finalizer는 객체가 가비지 컬렉션에 의해 제거될 때 호출되는 finalize() 메서드를 포함한다.&lt;br /&gt;이 과정에서 발생할 수 있는 보안 취약점, 즉 Finalizer 공격은 두 가지 주요 방식으로 이루어진다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;객체 재생성과 보안 검사 우회&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 객체 재생성: &lt;br /&gt;Finalizer 공격의 첫 번째 전략은 finalize() 메서드를 오버라이딩하여, 가비지 컬렉터가 객체를 수집하려는 순간에 객체를 다시 &quot;살려내는&quot; 것이다. 이는 finalize() 내에서 객체에 대한 새로운 참조를 생성하여, 객체가 가비지 컬렉션에서 제외되도록 만드는 방식으로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보안 검사 우회: &lt;br /&gt;두 번째 전략은 객체가 파괴되는 시점에 실행되는 Finalizer를 통해 보안 검사를 우회하거나 민감한 정보에 접근하는 것이다. 이 방식으로, 애플리케이션의 정상적인 흐름에서는 불가능한 보안 관련 행위들을 수행할 수 있게 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;위험성: 데이터 노출과 시스템 보안 무력화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 공격 방식은 크게 두 가지 위험을 초래한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터 노출: &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;- 시스템 보안 무력화: &lt;br /&gt;공격자는 Finalizer 공격을 통해 애플리케이션의 보안 메커니즘을 우회할 수 있다. 이는 시스템에 더 깊이 침투하거나, 더 심각한 손상을 가하는 결과를 초래할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. runFinalizersOnExit()의 결함&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer를 실행할 수 있게 보장해 주는 메서드가 2개 있는데, System.runFinalizersOnExit와 그 쌍둥이인 Runtime.runFinalizersOnExit이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;이 메서드들은 프로그램이 종료될 때 남아 있는 모든 객체에 대해 finalize() 메서드를 강제로 호출하는 기능을 한다. 하지만 이 역시, 앞서 설명한 Finalizer의 동시성문제, 보안 문제, 성능 저하 문제를 모두 포함하고 있어, 자바 9 이후 이 메서드들은 사용이 권장되지 않거나 제거되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. Finalizer 동작중 발생한 예외 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;finalize() 메서드 내에서 발생하는 예외는 JVM에 의해 잡히지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;만약 finalize() 메서드 실행 중에 예외가 발생하면, 이 예외는 무시되고, 해당 메서드의 나머지 부분은 실행되지 않는다. 즉, 중요한 리소스 해제나 정리 작업이 완료되지 않을 수도 있다는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cleaner&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 9에서는 Finalizer는 사용 자제 API로 지정되었고 Cleaner를 그 대안으로 소개하기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cleaner도 Finalizer와 마찬가지로, 리소스를 정리하기 위해 사용되기 위한 목적으로 설계되었다. 하지만, Cleaner는 Finalizer보다는 덜 위험하지만, 여전히 예측 불가능하고, 느리고 일반적으로 불필요하다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class Resource {
    private static final Cleaner Cleaner = Cleaner.create();

    private static class ResourceCleaner implements Runnable {
        @Override
        public void run() {
            // 리소스 정리 로직
        }
    }

    private final Cleaner.Cleanable cleanable;

    public Resource() {
        this.cleanable = Cleaner.register(this, new ResourceCleaner()); // Resource 객체에 대한 정리 작업 등록
    }

    public void useResource() {
        System.out.println(&quot;Resource is being used.&quot;);
    }

    public static void main(String[] args) {
        Resource resource = new Resource();
        resource.useResource();

        // Resource 객체에 대한 참조를 명시적으로 제거
        // Cleaner가 작동하기 위해서는 객체에 대한 모든 강한 참조가 제거되어야 함
        resource = null;

        // GC 수행시 Cleaner에서 객체 정리 로직 run() 수행
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cleaner가 해결한 Finalizer의 문제&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 제어 가능한 스레드:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cleaner는 자체적으로 관리하는 스레드를 사용하여 정리 작업을 수행하기 때문에, finalize()가 실행되는 스레드가 불명확하고 제어하기 어려운 것과 대비된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Cleaner의 스레드는 특정 정리 작업에 할당되어 더 효율적이고 관리하기 쉽다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 안정성과 예측 가능성:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer는 예외 발생 시 객체 정리가 중단될 수 있으며, 이로 인해 리소스 누수가 발생할 수 있다.&lt;br /&gt;반면, Cleaner는 예외 처리를 좀 더 안정적으로 관리할 수 있으며 예측 가능한 방식으로 리소스를 정리한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 리소스 누수와 안전성:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer는 잘못 사용될 경우 리소스 누수를 일으킬 수 있다. Cleaner는 이러한 리소스 누수의 위험을 줄이고, 보다 안전한 리소스 관리를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cleaner가 여전히 가지고 있는 문제&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 비동기적 실행:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cleaner의 정리 작업은 객체가 가비지 컬렉터에 의해 회수된 후 비동기적으로 수행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Finalizer와 유사하게, 정리 작업의 실행 시점을 정확히 예측할 수 없게 만든다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 리소스 해제 지연:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cleaner도 Finalizer와 마찬가지로, 리소스가 즉시 해제되지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가비지 컬렉터가 객체를 회수하고, Cleaner가 해당 작업을 수행하는 데까지 시간이 걸릴 수 있으며, 이는 특히 리소스가 제한적인 환경에서 문제가 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Finalizer와 Cleaner의 대안책&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. AutoCloseable 인터페이스 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer와 Cleaner의 대안책으로는 AutoCloseable이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;단순히 AutoCloseable 인터페이스를 구현하여 close() 메서드를 호출하는 것으로 명시적으로 리소스를 해제할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class CustomResource implements AutoCloseable {
    public CustomResource() {
        //Resource opened.
    }

    @Override
    public void close() {
        //Resource closed.
    }

    public static void main(String[] args) {
        CustomResource resource = null;
        try {
            resource = new CustomResource();
            //resource do something...
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (resource != null) {
                try {
                    resource.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. try-with-resources 구문 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try-with-resources 구문은 Finalizer와 Cleaner의 대안으로 가장 권장되는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try-with-resources 구문은 AutoCloseable 인터페이스를 구현하는 객체를 자동으로 관리하기 때문에, 이 구문 안에서 선언된 리소스는 구문이 종료될 때 자동으로 close() 메서드를 호출하여 리소스를 안전하게 닫는다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class ResourceManagementExample {

    public static void main(String[] args) {
        // 파일 읽기
        try (BufferedReader reader = new BufferedReader(new FileReader(&quot;input.txt&quot;))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 파일 쓰기
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(&quot;output.txt&quot;))) {
            writer.write(&quot;Hello, world!&quot;);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;명시적 리소스 관리의 장점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;자원 해제 보장:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try-with-resources 구문을 사용하면, 리소스를 사용하는 코드 블록이 종료될 때 자동으로 리소스가 해제된다.&lt;br /&gt;이는 리소스 누수를 방지한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예외 처리 용이:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구문을 사용하면, 리소스 사용 중 발생하는 예외를 쉽게 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;또한 리소스 해제 중 발생하는 예외도 적절히 처리할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드의 가독성 향상:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스 관리 코드가 명확하고 간결해져, 가독성과 유지보수성이 향상된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Finalizer와 Cleaner의 쓰임새&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer와 Cleaner를 앞서 본 것과 같이 명시적 리소스 해제로 대체할 수 있다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도대체 어디에 쓰일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 최종 안전망&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 정리가 try-with-resources를 통해 처리되지 않은 경우, Finalizer나 Cleaner가 최종 안전망으로 사용할 수 있다.&lt;/p&gt;
&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 네이티브 피어와 연결된 객체의 회수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 피어란 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체를 의미한다. 다시 말해, 자바나 다른 고수준 프로그래밍 언어로 직접 만들어진 객체가 아닌, 주로 C나 C++과 같은 저수준 언어로 작성된 코드에 의해 생성되고 관리되는 객체를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 객체는 Java의 관리 하에 있지 않기 때문에, GC의 대상이 되지 않는다. 또한, try-with-resources 구문의 경우 Java 내부의 자원을 자동으로 해제해 주는 기능이기 때문에 네이티브 피어 객체의 경우 Cleaner를 이용하여 직접 객체를 회수해야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finalizer는 앞선 설명과 같이 단점들이 너무나도 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이를 위해 Cleaner가 등장했지만, 기존 코드와의 호환성 문제로 사라지지 않고 점진적인 폐지가 이루어지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Finalizer를 사용해야 할 일이 있다면, Java의 권장 사항대로 명시적 리소스 해제로 대체할 수 없는지 생각해 보고, 최종적으로 Cleaner로 대체해 사용하도록 하자.&lt;/p&gt;</description>
      <category>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/76</guid>
      <comments>https://olrlobt.tistory.com/76#entry76comment</comments>
      <pubDate>Wed, 10 Jan 2024 19:49:30 +0900</pubDate>
    </item>
    <item>
      <title>[Spring/Error] Name for argument of type, @PathVariable name 생략 에러</title>
      <link>https://olrlobt.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1704629648139&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.IllegalArgumentException: Name for argument of type [long] not specified, 
and parameter name information not available via reflection. 
Ensure that the compiler uses the '-parameters' flag.&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;@PathVariable로 URL에서 변수를 받아오는 예제에서 위와 같은 에러가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제가 된 Contoller&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1704628737661&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/{itemId}&quot;)
public String item(@PathVariable long itemId, Model model) {
    Item item = itemRepository.findById(itemId);
    model.addAttribute(&quot;item&quot;, item);
    return &quot;basic/item&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;@PathVariable로 매핑된 변수와 파라미터 변수의 이름이 같아서 생략이 가능하다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;원인&lt;/b&gt;&lt;/h2&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;b&gt;@PathVariable&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;@RequestParam&lt;/b&gt;을 사용할 때, Spring MVC가 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;특히 스프링 부트에서 자동으로 파라미터 이름을 추론했을 때, 예상치 못한 결과를 가져오는 등의 문제를 해결하기 위해 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;스프링 부트 3.2 이후 버전에서는&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해결방안&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. @PathVariable name 속성 지정&lt;/b&gt;&lt;/h3&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;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_1704629018369&quot; class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@GetMapping(&quot;/{itemId}&quot;)
public String item(@PathVariable(&quot;itemId&quot;) itemId, Model model) {
    Item item = itemRepository.findById(itemId);
    model.addAttribute(&quot;item&quot;, item);
    return &quot;basic/item&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. -parameters 컴파일 옵션 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File &amp;gt; Setting &amp;gt; Build, Execution, Deployment &amp;gt; Compiler &amp;gt; Java Compiler에서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Additional command line parameters에 &quot;-parameters&quot;를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 옵션을 사용하면 컴파일러에서 메서드 파라미터의 실제 이름을 유지하여, 파라미터 이름 추론을 사용할 수 있다.&lt;/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;1478&quot; data-origin-height=&quot;1104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vb2oZ/btsC6TikSlT/zI1c2msJihQmfVYdDmBNl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vb2oZ/btsC6TikSlT/zI1c2msJihQmfVYdDmBNl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vb2oZ/btsC6TikSlT/zI1c2msJihQmfVYdDmBNl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvb2oZ%2FbtsC6TikSlT%2FzI1c2msJihQmfVYdDmBNl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1478&quot; height=&quot;1104&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;1104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정을 적용한 후, /out 폴더를 삭제해 주어야 프로젝트가 다시 컴파일되며 옵션이 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 프로젝트 Gradle 빌드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File &amp;gt; Setting &amp;gt; Build, Execution, Deployment &amp;gt; Build Tools &amp;gt; Gradle에서 프로젝트 빌드를 Gradle로 설정한다.&lt;/p&gt;
&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: #374151; text-align: left;&quot;&gt;Gradle을 사용하면 앞서 설명한 &lt;/span&gt;-parameters&lt;span style=&quot;color: #374151; text-align: left;&quot;&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;1480&quot; data-origin-height=&quot;1101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byy4vS/btsC4rfAYJd/2nKpowrenjNvcNA8eN8eE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byy4vS/btsC4rfAYJd/2nKpowrenjNvcNA8eN8eE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byy4vS/btsC4rfAYJd/2nKpowrenjNvcNA8eN8eE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyy4vS%2FbtsC4rfAYJd%2F2nKpowrenjNvcNA8eN8eE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1480&quot; height=&quot;1101&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;1101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1704629147467&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;@PathVariable name 생략 질문 드립니다. - 인프런&quot; data-og-description=&quot;학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/questions/1087879/pathvariable-name-%EC%83%9D%EB%9E%B5-%EC%A7%88%EB%AC%B8-%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4&quot; data-og-url=&quot;https://www.inflearn.com/questions/1087879/pathvariable-name-%EC%83%9D%EB%9E%B5-%EC%A7%88%EB%AC%B8-%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b9gjwi/hyU2pJxQRo/hDetmQARZUph5q3lVgKGa1/img.jpg?width=1200&amp;amp;height=628&amp;amp;face=751_416_794_462,https://scrap.kakaocdn.net/dn/hx5yN/hyU2q2Kca1/YpWSiIxlsyydG5HOGrmH00/img.jpg?width=1200&amp;amp;height=628&amp;amp;face=751_416_794_462,https://scrap.kakaocdn.net/dn/cUTFzy/hyUXQvhkxS/cLeenhoAuNeLsCCALbSX7k/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/questions/1087879/pathvariable-name-%EC%83%9D%EB%9E%B5-%EC%A7%88%EB%AC%B8-%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/questions/1087879/pathvariable-name-%EC%83%9D%EB%9E%B5-%EC%A7%88%EB%AC%B8-%EB%93%9C%EB%A6%BD%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/b9gjwi/hyU2pJxQRo/hDetmQARZUph5q3lVgKGa1/img.jpg?width=1200&amp;amp;height=628&amp;amp;face=751_416_794_462,https://scrap.kakaocdn.net/dn/hx5yN/hyU2q2Kca1/YpWSiIxlsyydG5HOGrmH00/img.jpg?width=1200&amp;amp;height=628&amp;amp;face=751_416_794_462,https://scrap.kakaocdn.net/dn/cUTFzy/hyUXQvhkxS/cLeenhoAuNeLsCCALbSX7k/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_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;@PathVariable name 생략 질문 드립니다. - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.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;</description>
      <category>Error</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/75</guid>
      <comments>https://olrlobt.tistory.com/75#entry75comment</comments>
      <pubDate>Sun, 7 Jan 2024 21:32:16 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 메모리 누수의 원인, 다 쓴 객체는 참조를 해제하라</title>
      <link>https://olrlobt.tistory.com/74</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;다 쓴 객체 참조를 해제하라&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다 쓴 객체는 GC의 대상이 되어, 자바에서는 자동으로 메모리 해제가 된다. 하지만 반대로, GC 언어에서 메모리 누수를 찾기가 아주 까다로운데, 다 쓴 객체의 참조가 하나라도 살아 있으면 GC의 대상이 되지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 사용자가 임의로 만든 Stack.class 가 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        return result;
    }

    private void ensureCapacity() {
        /* 생략 */
    }

}&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;해당 Stack에서는 얼핏 보면 pop(), push()를 잘해 주는 Stack처럼 보이지만, pop() 되어 다 쓴 객체가 참조 해제되지 않고, 여전히 elements에 살아있게 된다. 따라서 GC는 해당 pop 된 객체를 회수할 수 없고, 이는 불필요하게 메모리 사용량을 늘리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해법은 간단히 해당 객체에 null 처리를 해 주어, 참조를 해제하면 된다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null; // 다 쓴 참조 해제
        return result;
}&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;이처럼 null 처리를 해 주면, GC의 대상이 되도록 명시할 수 있고, 실수로 다 쓴 객체를 참조하려고 할 때는 NPE를 던지면서 프로그램 오류를 조기에 발견할 수 있게 해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 같은 이점 때문에 많은 개발자들이 null처리에 혈안이 되곤 하는데, 사실 객체 참조를 null 처리하는 것은 예외적인 경우여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;그 이유는 실제 Java의 Collection들은 이미 객체 참조 해제가 이루어지고 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Java Collection의 객체 참조 해제&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;stackClassPopMethod.png&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KBdZN/btsCZ2MfFa5/gSqXaaK88roQCXL4Z8WOB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KBdZN/btsCZ2MfFa5/gSqXaaK88roQCXL4Z8WOB1/img.png&quot; data-alt=&quot;Stack.class의 pop() 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KBdZN/btsCZ2MfFa5/gSqXaaK88roQCXL4Z8WOB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKBdZN%2FbtsCZ2MfFa5%2FgSqXaaK88roQCXL4Z8WOB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;622&quot; height=&quot;287&quot; data-filename=&quot;stackClassPopMethod.png&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;287&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Stack.class의 pop() 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Stack 클래스의 pop() 메서드는 Vector 클래스를 상속받고 있고, Vector 클래스의 removeElementAt() 메서드를 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;vectorClassRemoveMethod.png&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;591&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckO4pc/btsCZ4J4i3z/ZKqkuUTyIlwbHCqvW0SKyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckO4pc/btsCZ4J4i3z/ZKqkuUTyIlwbHCqvW0SKyk/img.png&quot; data-alt=&quot;Vector.class의 removeElementAt() 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckO4pc/btsCZ4J4i3z/ZKqkuUTyIlwbHCqvW0SKyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckO4pc%2FbtsCZ4J4i3z%2FZKqkuUTyIlwbHCqvW0SKyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;731&quot; height=&quot;591&quot; data-filename=&quot;vectorClassRemoveMethod.png&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;591&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vector.class의 removeElementAt() 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vector 클래스의 removeElementAt() 메서드의 경우 구현 마지막 부분에 elementData [elementCount]&lt;code&gt; = null;&lt;/code&gt; 을 통해 명시적으로 null 처리를 해 주고 있고, 주석으로 친절하게 GC 대상이 되게 하는 코드로 설명까지 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;arrayListRemoveMethod.png&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kgf9b/btsCWMpwzVj/N5ShRdGPZ3JRfqGTm2kIk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kgf9b/btsCWMpwzVj/N5ShRdGPZ3JRfqGTm2kIk1/img.png&quot; data-alt=&quot;ArrayList의 remove() 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kgf9b/btsCWMpwzVj/N5ShRdGPZ3JRfqGTm2kIk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKgf9b%2FbtsCWMpwzVj%2FN5ShRdGPZ3JRfqGTm2kIk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;365&quot; height=&quot;204&quot; data-filename=&quot;arrayListRemoveMethod.png&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ArrayList의 remove() 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;arrayListRemoveMethod2.png&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5UzsC/btsCOCPCiD0/4RfgdqivNfKJ97ahjK2xuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5UzsC/btsCOCPCiD0/4RfgdqivNfKJ97ahjK2xuK/img.png&quot; data-alt=&quot;ArrayList의 fastRemove() 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5UzsC/btsCOCPCiD0/4RfgdqivNfKJ97ahjK2xuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5UzsC%2FbtsCOCPCiD0%2F4RfgdqivNfKJ97ahjK2xuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;570&quot; height=&quot;164&quot; data-filename=&quot;arrayListRemoveMethod2.png&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ArrayList의 fastRemove() 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 자바에서 ArrayList와 같은 Collection들은 모두 요소 제거 작업을 할 때, 명시적으로 null 처리를 해 주며 객체 참조에 대한 해제를 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그렇다면 개발자들은 어떤 부분을 신경 써야 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 객체 참조 관리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 필요하지 않을 때, 객체의 참조를 해제한다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; strList = new ArrayList&amp;lt;&amp;gt;();
strList.add(&quot;Java&quot;);
strList.add(&quot;Python&quot;);

strList.clear(); // 리스트를 비워 참조를 해제&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;List&amp;lt;Object&amp;gt; objectList = new ArrayList&amp;lt;&amp;gt;();
Object obj = new Object();
objectList.add(obj);
objectList.remove(obj);

obj = 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;위 방법은 단편적인 예시로,&lt;br /&gt;실제 다 쓴 참조를 해제하는 가장 좋은 방법은 변수를 Scope 밖으로 밀어내는 것이다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class ScopeExample {
    public static void main(String[] args) {

        createAndUseObject();
        // 이 시점에서 obj는 더 이상 접근할 수 없다.

        // 다른 작업을 수행
        otherThing();
    }

    private static void createAndUseObject() {
        Object obj = new Object();
        System.out.println(obj);
    }

    private static void otherThing() {
        // 다른 작업 수행
        System.out.println(&quot;other..&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 리소스 해제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스의 경우 GC가 자동으로 관리하지 않기 때문에, 적절히 해제해 주는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 파일이나 데이터베이스 연결과 같은 리소스를 사용한 후 명시적으로 해제해 준다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;try (FileReader fr = new FileReader(&quot;example.txt&quot;);
     BufferedReader br = new BufferedReader(fr)) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}
// try-with-resources 구문이 자동으로 리소스를 닫아줌&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 캐시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시를 사용할 때, 객체 참조를 캐시에 넣어두고 이 사실을 잊은 채 그냥 놔두는 일을 자주 접할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;public class MemoryLeakExample {
    private Map&amp;lt;String, Object&amp;gt; cache = new HashMap&amp;lt;&amp;gt;();

    public void addToCache(String key, Object value) {
        if (!cache.containsKey(key)) {
            cache.put(key, value);
        }
    }
    // 이 메서드는 객체를 계속 캐시에 추가만 하고, 제거하지 않음
}&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;만약 운 좋게 캐시 외부에서 키를 참조하는 동안만 엔트리가 살아 있는 캐시가 필요한 상황이라면 WeakHashMap을 사용해 캐시를 만들자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;public class WeakHashMapExample {
    public static void main(String[] args) {
        Map&amp;lt;Object, String&amp;gt; weakHashMap = new WeakHashMap&amp;lt;&amp;gt;();
        Object key = new Object();

        weakHashMap.put(key, &quot;Example Value&quot;);

        // 키에 대한 참조가 존재하는 동안은 데이터가 유지됨
        System.out.println(&quot;Before removing reference: &quot; + weakHashMap.containsKey(key));

        key = null; // 키에 대한 참조를 제거, 이제 GC의 대상이 된다
    }
}&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;WeakHashMap은 key에 대한 다른 강한 참조가 있지 않다면, 언제든지 GC의 대상이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 WeakHashMap은 메모리 관리 측면에서 유리하지만, 키 객체에 대한 다른 참조가 없을 경우 예기치 않게 데이터가 사라질 수 있으므로 주의가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시를 만들 때 보통은 캐시 엔트리의 유효 기간을 정하기 어렵기 때문에, 시간이 지날수록 엔트리의 가치를 떨어뜨리는 방식을 흔히 사용한다. 이런 방식에서는 쓰지 않는 엔트리를 백그라운드 스레드나 캐시에 새 엔트리를 추가할 때, 부수적인 작업으로 정리해 줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 시간에 따라 캐시의 가치를 떨어뜨리는 단편적 예시이다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class ExpiringCache&amp;lt;K, V&amp;gt; {
    private class CacheEntry {
        V value;
        long timeStamp;
        int score;

        CacheEntry(V value, int score) {
            this.value = value;
            this.timeStamp = System.currentTimeMillis();
            this.score = score;
        }

        void decreaseScore() { // 시간이 지남에 따라 점수 감소
            long currentTime = System.currentTimeMillis();
            long age = currentTime - this.timeStamp;
            this.score -= (int) (age / 1000); // 예시: 1초마다 1점 감소
        }
    }

    private Map&amp;lt;K, CacheEntry&amp;gt; cache = new HashMap&amp;lt;&amp;gt;();

    public void put(K key, V value, int initialScore) {
        cache.put(key, new CacheEntry(value, initialScore));
    }

    public V get(K key) {
        CacheEntry entry = cache.get(key);
        if (entry == null) return null;

        entry.decreaseScore();
        if (entry.score &amp;lt;= 0) {
            cache.remove(key);
            return null;
        }

        return entry.value;
    }

    // 캐시 정리 (점수가 낮은 엔트리 제거)
    public void cleanup() {
        cache.entrySet().removeIf(e -&amp;gt; e.getValue().score &amp;lt;= 0);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 콜백 함수와 리스너&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체들은 종종 다른 객체에 등록되어 이벤트나 특정 조건 발생 시 호출되도록 설계되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 객체가 리스너로 등록되면, 해당 리스너를 가지고 있는 객체는 리스너에 대한 참조를 유지한다. 이 리스너 역시, 사용하지 않는다면 해제해 주어야 한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class SimpleEventExample {
    public static void main(String[] args) {
        EventManager manager = new EventManager();

        EventListener listener = new EventListener() {
            @Override
            public void onEvent() {
                System.out.println(&quot;Event occurred!&quot;);
            }
        };

        manager.addListener(listener);
        manager.fireEvent(); // 이벤트 발생

        manager.removeListener(listener); // 리스너 해제
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 누수는 겉으로 잘 드러나지 않아 시스템에 수년간 잠복하는 사례도 있다. 이러한 누수는 철저한 코드 리뷰나 힙 프로파일러와 같은 디버깅 도구를 동원해야만 발견되기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 위와 같은 예방법을 익혀두는 것이 중요하다.&lt;/p&gt;</description>
      <category>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/74</guid>
      <comments>https://olrlobt.tistory.com/74#entry74comment</comments>
      <pubDate>Tue, 2 Jan 2024 13:21:38 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 불필요한 객체 생성을 피하라</title>
      <link>https://olrlobt.tistory.com/73</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; font-size: 16px; letter-spacing: 0px;&quot;&gt;&quot;불필요한 객체 생성을 피하라&quot;는 원칙은 성능 향상과 메모리 효율성을 높이는 데 기여할 수 있다. 실제로, 똑같은 기능을 하는 객체를 반복적으로 생성하기보다는 필요할 때 재사용하는 것이 더 나을 수 있고, 종종 더 빠르기도 하다.&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: #374151; text-align: start;&quot;&gt;하지만, &lt;b&gt;이 원칙을 &quot;어떠한 상황에서도 불필요한 객체 생성을 하지 말라&quot;로 오해해서는 안 된다.&lt;/b&gt; 현대의 자바 가상 머신(JVM)은 작은 객체의 생성과 회수에 매우 효율적으로 최적화되어 있다. 간단한 객체를 생성하는 것이 프로그램의 명확성, 간결성, 기능성을 향상하는 경우, 이를 피할 이유는 없다.&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: #374151; text-align: start;&quot;&gt;특히, 방어적 복사가 필요한 상황에서 객체를 재사용하는 것은 주의가 필요한데, &lt;b&gt;잘못된 객체 재사용은 예측 불가능한 버그와 심각한 보안 문제를 초래할 수 있다. &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;반면, 불필요한 객체 생성은 주로 성능이나 코드의 형태에만 영향을 미치기 때문에, &lt;span style=&quot;color: #374151; text-align: start;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;불필요한 객체 생성을 피하라&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;극단 적인 예로, String에서 다음과 같이 인스턴스를 생성한다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;for(int i = 0; i &amp;lt; 100_000_000; i++) {
    String s = new String(&quot;sameObject&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;자바에서 문자열 String은 불변객체이고, 이 코드는 1억 번 반복하며 동일한 내용의 새로운 문자열 객체를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 자바의 문자열 상수 풀을 사용하지 않아, 메모리가 낭비되는 코드이고, 가독성도 떨어지며, 성능도 떨어지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개선된 버전은 아래와 같이 문자열 리터럴을 사용하면 된다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for(int i = 0; i &amp;lt; 100_000_000; i++) {
    String s = &quot;sameObject&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;위 예시에서 프로그램이 실행될 때, JVM은 문자열 상수 풀에서 이 문자열을 찾고, 해당 문자열이 존재하지 않으면 새로운 문자열 객체를 생성해 풀에 추가한다.&lt;/p&gt;
&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;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;불변 클래스에서는 정적 팩토리 메서드 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 대신 정적 팩토리&amp;nbsp;메서드를 제공하는 불변 클래스에서는 정적 팩토리 메서드를 사용하는 것이 불필요한 객체 생성을 피할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Boolean trueValue1 = new Boolean(&quot;true&quot;);
Boolean trueValue2 = new Boolean(&quot;true&quot;);

System.out.println(&quot;trueValue1 == trueValue2: &quot; + (trueValue1 == trueValue2)); // false&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;예를 들어 Boolean 객체를 만들 때, new Boolean(String)으로 새로운 객체를 만든다면 각각의 객체는 다른 메모리를 참조하는 새로운 객체 일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Boolean trueValue1 = Boolean.valueOf(&quot;true&quot;);
Boolean trueValue2 = Boolean.valueOf(&quot;true&quot;);

System.out.println(&quot;trueValue1 == trueValue2: &quot; + (trueValue1 == trueValue2)); // 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;하지만, 정적 팩토리 메서드인 Boolean.valueOf(String)는 Boolean 클래스 안의 미리 선언된 Boolean.True의 참조를 반환하기 때문에 불필요한 객체 생성을 피할 수 있다.&lt;/p&gt;
&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;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;생성 비용이 비싼 객체는 캐싱하여 재사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 비용이 비싼 경우에도 캐싱하여 재사용하길 권해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, DB 연결 객체를 만든다고 가정하고 객체 생성에 1초의 시간이 걸린다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class DatabaseConnection {
    private String connectionString;

    public DatabaseConnection(String connectionString) {
        this.connectionString = connectionString;
        // 데이터베이스 연결 설정에 시간이 걸린다고 가정
        try {
            Thread.sleep(1000); // 시뮬레이션을 위한 1초 지연
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public void executeQuery(String query) {
        System.out.println(&quot;Executing query on &quot; + connectionString);
        // 쿼리 실행 로직
    }
}&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와 연결할 때마다 호출을 하게 된다면, 호출마다 객체를 생성하기 때문에 1초의 시간이 걸린다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 이 객체를 팩토리 클래스를 통해 캐싱해 두고, 캐싱한 객체를 반환하는 방법을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class DatabaseConnectionFactory {
    private Map&amp;lt;String, DatabaseConnection&amp;gt; connectionCache = new HashMap&amp;lt;&amp;gt;();

    public DatabaseConnection getConnection(String connectionString) {
        if (!connectionCache.containsKey(connectionString)) { // 객체가 캐싱되지 않았다면, 캐싱한다.
            connectionCache.put(connectionString, new DatabaseConnection(connectionString));
        }
        return connectionCache.get(connectionString); // 캐싱된 객체를 반환한다.
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면, 값비싼 불필요한 객체가 반복 생성되는 것을 방지할 수 있어 성능 저하를 피할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&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;br /&gt;String.matches는 정규 표현식으로 문자열 형태를 확인하는 가장 쉬운 방법이지만, 정규표현식에서 사용하는 Pattern 인스턴스는 입력받은 정규표현식에 해당하는 유한 상태 머신을 만들기 때문에 인스턴스 생성 비용이 높다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class EmailValidator {
    // 이메일 주소의 유효성을 검사하는 정규식 패턴
    public boolean validate(String email) {
        return email.matcher(&quot;^[a-zA-Z0-9_+&amp;amp;*-]+(?:\\.[a-zA-Z0-9_+&amp;amp;*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$&quot;).matches();
    }
}&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;따라서 메서드가 위 예시처럼 내부에서 만드는 Pattern 인스턴스는, 한 번 쓰고 버려져서 곧바로 GC의 대상이 되고, 불필요한 값비싼 객체를 반복 생성하면서 성능 저하를 초래한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 개선하기 위해서 Pattern 인스턴스를 정적 초기화를 통해 캐싱해 두고, 메서드가 호출될 때마다 재사용하는 방법이 있다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class EmailValidator {
    // 이메일 주소의 유효성을 검사하는 정규식 패턴
    private static final Pattern EMAIL_PATTERN =
            Pattern.compile(&quot;^[a-zA-Z0-9_+&amp;amp;*-]+(?:\\.[a-zA-Z0-9_+&amp;amp;*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$&quot;);

    public boolean validate(String email) {
        return EMAIL_PATTERN.matcher(email).matches();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 개선하면, 메서드가 빈번히 호출되는 상황에서 성능을 상당히 끌어올릴 수 있고, 가독성 또한 좋아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&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;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Map의 keySet을 재호출 할 때&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;// 여기서 keySet()을 한 번만 호출
Set&amp;lt;String&amp;gt; keys = map.keySet();
for (String key : keys) {
    System.out.println(key);
}

// 맵이 변경된 후 다시 keySet을 호출할 필요가 없음
map.put(&quot;Four&quot;, 4);
for (String key : keys) {
    System.out.println(key); // &quot;Four&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;Map의 keySet()은 Map객체 안의 키 전부를 담은 Set뷰를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, keySet이 호출 때마다 새로운 Set 인스턴스가 만들어지리라 생각할 수 도 있지만, 그럴 때도 있고, 아닐 때도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환된 Set 인스턴스가 일반적으로 가변이더라도 반환된 인스턴스들은 기능적으로 똑같아서, 반환한 객체 중 하나를 수정하면 모든 객체가 바뀌게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, keySet()으로 뷰 객체를 여러 개 만들어도 상관은 없지만, 그럴 필요도 없고, 이득도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;오토박싱(Auto Boxing)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오토방식은 기본 타입과 그에 대응하는 박싱 된 기본 타입의 구분을 흐려주지만, 완전히 없애 주는 것은 아니다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;private static long sum() {
    Long sum = 0L;
    for (long i = 0; i &amp;lt;= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    return sum;
}&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;위 코드에서 sum 변수가 Long으로 선언되어 있어, 각 반복마다 기본형 'long' 값을 'Long' 객체로 박싱 하는 데 비용이 발생하게 된다. &lt;code&gt;sum += i&lt;/code&gt; 연산에서 내부적으로는 &lt;code&gt;sum = Long.valueOf(sum.longValue() + i)&lt;/code&gt;와 같이 처리되며, 이 과정에서 수많은 Long 객체가 불필요하게 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 박싱 된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 사용되지&amp;nbsp; 않도록 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;좋은 프로그래밍은 성능과 코드의 품질 사이에서 적절한 균형을 찾는 것을 의미한다. 때로는 성능을 위해 객체를 재사용할 필요가 있으며, 다른 때에는 코드의 명확성과 유지보수를 위해 새로운 객체를 생성하는 것이 옳을 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;중요한 것은 상황에 따라 적절한 접근 방식을 선택하는 것이고, 이 건 개발자의 몫이다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/73</guid>
      <comments>https://olrlobt.tistory.com/73#entry73comment</comments>
      <pubDate>Mon, 1 Jan 2024 20:20:51 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 자바에서 싱글톤(Singleton)패턴을 적용하는 방법</title>
      <link>https://olrlobt.tistory.com/72</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;싱글톤(Singleton)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;싱글톤(Singleton)&lt;/b&gt;은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;특정 클래스의 인스턴스가 애플리케이션 내에서 단 하나만 존재하도록 보장하는 패턴&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤은 전역 상태를 생성하거나, 리소스를 공유하는 데 유용하며, 객체의 중복 생성을 방지하고 전체 시스템에서 하나의 인스턴스만을 사용하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;싱글톤을 쓰는 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 패턴을 이용하는 이유는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;리소스 관리 및 접근 제어 :&lt;/b&gt;&lt;br /&gt;싱글톤 패턴은 특정 자원이나 서비스에 대한 접근을 제어하는 데 유용하다.&lt;br /&gt;싱글톤은 한 번에 하나의 인스턴스만이 자원을 사용하도록 보장함으로써,&lt;br /&gt;리소스의 과도한 사용을 방지할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;메모리 효율성 :&lt;/b&gt;&lt;br /&gt;싱글톤은 필요한 시점에만 인스턴스를 생성하고, 이후에는 동일 인스턴스를 재사용한다.&lt;br /&gt;이는 메모리 사용을 줄이고 시스템의 전반적인 효율성을 높이는 데 도움 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;공유 상태의 일관성 :&lt;/b&gt; &lt;br /&gt;싱글톤 인스턴스는 애플리케이션에서 전역 상태를 유지한다. 이는 여러 컴포넌트 간에 상태를 공유하고 일관성을 유지하는 데 유용하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 싱글톤 패턴의 구현&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. public static final 필드 방식의 싱글톤 &lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class Singleton {
    public static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        // private 생성자
    }
}&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;private 생성자는 public static final 필드인 &lt;code&gt;INSTANCE&lt;/code&gt;를 초기화할 때, 딱 한 번만 호출된다. public이나 protected 생성자가 없기 때문에, 초기화될 때 만들어진 인스턴스가 전체 시스템에서 하나뿐임이 보장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 방식은 Singleton.INSTANCE로 쉬운 접근성을 갖고, 간결하고 명백하다는 것이 특징이며, 해당 클래스가 싱글톤임이 API에 명백히 드러난다는 점이 가장 큰 특징이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 정적 팩토리 메서드 방식의 싱글톤 &lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        // private 생성자
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}&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;public static final 방식과 마찬가지로, 해당 방식도 &lt;code&gt;INSTANCE&lt;/code&gt;를 초기화할 때 딱 한 번만 호출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메서드인 &lt;code&gt;getInstance()&lt;/code&gt;는 항상 같은 객체의 참조를 반환하므로 인스턴스가 전체 시스템에서 하나뿐임이 보장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-1. 정적 팩토리 메서드 지연 초기화 방식의 싱글톤 &lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
        // private 생성자
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연 초기화 방식은 객체의 생성을 실제로 사용되는 시점까지 지연시키는 기법이다. 이 방식은 특히 객체의 생성 비용이 높거나, 사용되지 않을 가능성이 있는 경우에 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 방식은 해당 클래스를 싱글톤이&amp;nbsp; 아니게 하고 싶을 때, API를 바꾸지 않고도 싱글톤이 아니게 변경할 수 있다. 예를 들어 &lt;code&gt;getInstance()&lt;/code&gt;에서 반환 값을 &lt;code&gt;return new Singleton();&lt;/code&gt;으로만 바꿔주어도 싱글톤이 아니게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 원한다면 정적 팩토리를 제네릭 싱글톤 팩토리로도 만들 수도 있고, 정적 팩토리의 메서드 참조를 공급자로 사용할 수 있어 훨씬 유용한 면이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 두 방법으로 만든 싱글톤 클래스는 직렬화하기 위해서 단순히 Serializable 인터페이스를 구현하고 있다면, 역직렬화 과정에서 새로운 인스턴스가 생성될 수 있다. 이를 해결하기 위해서는 readResolve 메서드를 구현해 싱글톤이 보장되게 해야 한다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 지연초기화(Lazy Initialization) 방법을 사용하게 된다면, 멀티스레드 환경에서 동시 접근으로 인해 문제가 생길 수 있다. 이를 해결하기 위해 메서드 동기화 (Synchronized Method), 더블 체크 락킹 (Double-Checked Locking), 초기화-온-디맨드 홀더 (Initialization-on-demand Holder) 패턴을 적용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Enum을 이용한 싱글톤 &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enum을 사용하여 싱글톤을 구현하는 방법은 간단하면서도 효율적인 방법 중 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;enum을 이용한 싱글톤 구현은 직렬화(serialization)와 스레드 안전성(thread safety) 문제를 자동으로 해결해 주며,&lt;br /&gt;추가적인 구현 없이 싱글톤을 보장한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 싱글톤이 할 작업
    }
}&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;이 코드에서 Singleton은 enum으로 선언되었으며, INSTANCE는 Singleton의 유일한 인스턴스이다.&lt;br /&gt;doSomething 메서드는 이 싱글톤 인스턴스에 대한 메서드로, 필요에 따라 다양한 기능을 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Main {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        singleton.doSomething();
    }
}&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;Singleton 인스턴스에 접근하기 위해서는 위와 같이 Singleton.INSTANCE를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;가장 간결하고, 직렬화와 스레드 안전성 문제도 없으며, 리프렉션 공격도 방지할 수 있는 장점이 있지만,&lt;br /&gt;enum은 본질적으로 final 클래스이기 때문에, 상속을 지원하지 않는다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Spring에서 싱글톤&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 프레임워크에서 싱글톤 패턴을 구현하는 것은 매우 간단하다.&lt;/p&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의 핵심 기능 중 하나는 빈(Bean) 관리 기능이며, 기본적으로 Spring 컨테이너는 모든 빈을 싱글톤으로 관리하기 때문이다. 이는 각각의 빈 정의에 대해 컨테이너 내에서 단 하나의 인스턴스만을 생성하고 관리한다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 컴포넌트 스캔&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@Component
public class MySingletonService {
    // 클래스 구현
}&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;클래스에 @Component 또는 @Service, @Repository 등의 어노테이션을 사용하여 빈으로 등록하여 싱글톤으로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 수동 빈 등록&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class AppConfig {
    @Bean
    public MySingletonService mySingletonService() {
        return new MySingletonService();
    }
}&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;@Bean 어노테이션으로 수동으로 빈을 등록하여 싱글톤으로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 의존성 주입&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
public class SomeService {
   private final MySingletonService mySingletonService;

   @Autowired
   public SomeService(MySingletonService mySingletonService) {
      this.mySingletonService = mySingletonService;
   }

   // 여기서 mySingletonService를 사용하는 로직
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 의존성을 주입하는 방법은 여러 가지고, 그중 생성자 주입의 방법이 가장 많이 쓰인다.&lt;br /&gt;Spring에서 의존성 주입을 통해 생성된 인스턴스는 빈으로 등록되어 싱글톤이 보장된다.&lt;/p&gt;</description>
      <category>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/72</guid>
      <comments>https://olrlobt.tistory.com/72#entry72comment</comments>
      <pubDate>Fri, 29 Dec 2023 21:52:21 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 빌더 패턴(Builder Pattern)을 사용하는 이유와 구현</title>
      <link>https://olrlobt.tistory.com/71</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/70&quot;&gt;정적 팩토리&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1703226697933&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;[Java] 생성자 대신 정적 팩토리 메서드를 고려하라&quot; data-og-description=&quot;생성자 대신 정적 팩토리 메서드를 고려하라 생성자(constructor)는 객체 지향 프로그래밍에서 클래스의 인스턴스를 초기화하는 메서드이다. 주된 목적은 객체 생성시점에 필요한 초기화 작업을 &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/70&quot; data-og-url=&quot;https://olrlobt.tistory.com/70&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/r7cW0/hyUPNriLok/gtuTxgbjzyMTKzaSA3ICKK/img.png?width=759&amp;amp;height=240&amp;amp;face=0_0_759_240,https://scrap.kakaocdn.net/dn/QLIA6/hyUPzfxCtC/2ImiOEFTvpksMTTIHGiJT0/img.png?width=759&amp;amp;height=240&amp;amp;face=0_0_759_240,https://scrap.kakaocdn.net/dn/k7fZ7/hyUPGZZZT4/jZfkX1W3IZHDGwCnPqqkk1/img.png?width=759&amp;amp;height=240&amp;amp;face=0_0_759_240&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/70&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/70&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/r7cW0/hyUPNriLok/gtuTxgbjzyMTKzaSA3ICKK/img.png?width=759&amp;amp;height=240&amp;amp;face=0_0_759_240,https://scrap.kakaocdn.net/dn/QLIA6/hyUPzfxCtC/2ImiOEFTvpksMTTIHGiJT0/img.png?width=759&amp;amp;height=240&amp;amp;face=0_0_759_240,https://scrap.kakaocdn.net/dn/k7fZ7/hyUPGZZZT4/jZfkX1W3IZHDGwCnPqqkk1/img.png?width=759&amp;amp;height=240&amp;amp;face=0_0_759_240');&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;[Java] 생성자 대신 정적 팩토리 메서드를 고려하라&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;생성자 대신 정적 팩토리 메서드를 고려하라 생성자(constructor)는 객체 지향 프로그래밍에서 클래스의 인스턴스를 초기화하는 메서드이다. 주된 목적은 객체 생성시점에 필요한 초기화 작업을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;생성자에 매개변수가 많다면 빌더를 고려하라&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서는 객체를 생성하기 위해, 생성자와 정적 팩토리 메서드를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;// 생성자를 이용한 객체 생성
Car electricCar = new Car(&quot;테슬라&quot;);
Car petrolCar = new Car(&quot;모닝&quot;, 50);&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 정적 팩토리 메소드를 이용한 객체 생성
Car electricCar = Car.createElectricCar(&quot;테슬라&quot;);
Car petrolCar = Car.createPetrolCar(&quot;모닝&quot;, 50);
//Car hybridCar = Car.createHybridCar(&quot;프리우스&quot;, &quot;화이트&quot;, 2021, 30, 500, false);&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;br /&gt;많다. 하지만, 위 예제의 두 경우만 보더라도 50이 무엇을 나타내는지 쉽게 알기는 어렵다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 생성자에 선택적 매개변수가 많아질수록 다양한 매개변수 조합을 갖는 생성자를 작성해 주어야 한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Pizza {
    private final String dough; // 필수
    private final String sauce;
    private final String topping;
    private final int hotSauce;
    private final int cheeseSauce;

    public Pizza(String dough) { // 1
        this(dough, &quot;토마토&quot;, &quot;치즈&quot;, 0, 0);
    }

    public Pizza(String dough, String sauce) { // 2
        this(dough, sauce, &quot;치즈&quot;, 0, 0);
    }

    public Pizza(String dough, String sauce, String topping) { // 3
        this(dough, sauce, topping, 0, 0);
    }

    public Pizza(String dough, String sauce, String topping, int hotSauce) { // 4
        this(dough, sauce, topping, hotSauce, 0);
    }

    // 더 많은 생성자들은 생략했다.

    public Pizza(String dough, String sauce, String topping, int hotSauce, int cheeseSauce) { // 5
        this.dough = dough;
        this.sauce = sauce;
        this.topping = topping;
        this.hotSauce = hotSauce;
        this.cheeseSauce = cheeseSauce;
    }

    // Getter 메소드들
}&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;위 예제에서 dough 변수는 필수 매개변수이고, 나머지 변수들은 선택 매개변수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 사용자가 필수 매개변수인 dough만을 지정해 피자 객체를 생성하게 되면, 생성자는 ( 1 &amp;gt; 2 &amp;gt; 3 &amp;gt; 4 &amp;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;이처럼 단계적인 확장을 통해 하나 이상의 매개변수를 추가하여 이전 생성자를 확장하는 방식을 &lt;b&gt;점층적 생성자 패턴(Telescoping Constructor Pattern)&lt;/b&gt;이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 점층적 생성자 패턴은&lt;b&gt; 매개변수의 수가 많아질수록 생성자의 수가 기하급수적으로 &lt;/b&gt;&lt;b&gt;증가하여 관리가 어려워지고, 코드가 복잡해지는 단점&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단점을 보완할 수 있는 방법으로 객체의 생성과 설정을 분리하는 방법인 &lt;b&gt;자바빈즈 패턴(JavaBeans Pattern)&lt;/b&gt;을 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Pizza {
    private String dough;
    private String sauce;
    private String topping;

    public Pizza() {
        // 매개변수 없는 생성자
    }

    // Setter 메소드
    public void setDough(String dough) {
        this.dough = dough;
    }

    public void setSauce(String sauce) {
        this.sauce = sauce;
    }

    public void setTopping(String topping) {
        this.topping = topping;
    }

    // Getter 메소드들
}&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;자바빈즈 패턴은 매개변수 없는 생성자로 객체를 만든 후, Setter 메서드들을 호출해 원하는 매개변수의 값을 설정하는 방식으로, 아래 예시와 같이 사용한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Pizza pizza = new Pizza();
pizza.setDough(&quot;씬&quot;);
pizza.setSauce(&quot;바베큐&quot;);
pizza.setTopping(&quot;페퍼로니&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바빈즈 패턴에서는 점층적 생성자 패턴에서의 단점이 보이지 않지만, 이 방법 또한, 심각한 단점을 지니고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바빈즈 패턴에서는 객체 하나를 만들기 위해 여러 개의 메서드를 호출해야 하고&lt;b&gt;, 객체가 완전히 생성되기 전까지는 불완전한 상태로 존재&lt;/b&gt;한다는 것이다. 또한, &lt;b&gt;객체가 생성된 이후에도 상태가 변경될 수 있어&lt;/b&gt;, 스레드 안전성을 얻으려면 freezing 작업을 해 주어야 한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;public void setDough(String dough) {
        if (!isFrozen) {
            this.dough = dough;
        }
}

public void freeze() {
        isFrozen = true; // 이제부터 객체는 변경할 수 없는 상태가 됨
}

Pizza pizza = new Pizza();
// 객체 생성 후
pizza.freeze(); // 객체 '동결'
pizza.setTopping(&quot;치즈&quot;); // 동결 후 변경은 적용되지 않는다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;freezing 작업은 객체 생성이 끝난 후 freeze()를 호출하여 객체를 수정 불가능한 상태, 즉 사용 가능한 상태로 만든다.&lt;br /&gt;하지만, freezing 작업 역시, 프로그래머가 freeze()를 확실히 호출해 주었는지 컴파일러가 보증할 방법이 없어서 &lt;b&gt;런타임 오류에 취약하다는 단점&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&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;정리하자면, 선택적 매개변수가 많은 상황에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;점층적 생성자 패턴은 안전성이 있지만, 가독성이 떨어지고,&lt;/b&gt;&lt;br /&gt;&lt;b&gt;자바 빈즈 패턴은 가독성이 있지만, 안정성이 떨어지게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 단점들을 보완하면서, 매개변수가 많은 객체를 만들기 위한 방법으로는 빌더 패턴(Builder Pattern)을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;빌더 패턴 (Builder Pattern)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정을 단순화하기 위한 디자인 패턴이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;특히 &lt;b&gt;객체 생성에 필요한 매개변수가 많거나, 객체 생성 과정이 복잡할 때 유용한 패턴&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 흔히 사용하는 Java의 StringBuilder도 빌더 패턴에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌더 패턴의 구체 클래스 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 빌더 패턴의 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;public class Pizza {
    private final String dough; // 필수
    private final String sauce;
    private final String topping;
    private final int hotSauce;
    private final int cheeseSauce;

    private Pizza(Builder builder) {
        this.dough = builder.dough;
        this.sauce = builder.sauce;
        this.topping = builder.topping;
        this.hotSauce = builder.hotSauce;
        this.cheeseSauce = builder.cheeseSauce;
    }


    //Builder 인터페이스 또는 추상 클래스
    public static class Builder {
        private final String dough; // 필수
        private String sauce = &quot;토마토&quot;; // 기본값
        private String topping = &quot;치즈&quot;; // 기본값
        private int hotSauce = 0;
        private int cheeseSauce = 0;

        public Builder(String dough) { // 필수 매개변수 생성자
            this.dough = dough;
        }

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

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

        public Builder hotSauce(int hotSauce) {
            this.hotSauce = hotSauce;
            return this;
        }

        public Builder cheeseSauce(int cheeseSauce) {
            this.cheeseSauce = cheeseSauce;
            return this;
        }

        public Pizza build() {
            return new Pizza(this);
        }
    }

    // Getter 메소드들...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시는 구체 클래스를 이용한 구현 방식으로, 빌더 패턴은&lt;b&gt; 추상 클래스&lt;/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;구체 클래스는 보통 객체 안에 내부 클래스로 구현되는데, 내부 클래스로 구현하면 자연스럽게 캡슐화를 유지할 수 있고, 가독성과 접근성이 향상된다는 이점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더의 구조는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;객체의 필수 매개변수를 private final 필드로 갖고, 객체의 선택 매개변수는 일반 private 필드로 갖는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;필수 매개변수를 빌더 생성자의 매개변수로 받고 있으며, 선택 매개변수들은 빌더에 Setter와 비슷한 형식의 메서드를 갖고 있다. 이 메서드들은 각각 this를 반환하며, 이 this 값은 Builder 객체 자신이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;build() 메서드를 통하여 최종 객체(Pizza)를 생성한다.&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;필수 매개변수를 private final 필드로 가지면서, 객체의 불변성을 보장한다. 또한 빌더의 this를 반환하는 메서드들은 this로 빌더 객체 자신을 반환하기 때문에, 메서드 체이닝을 사용하여 더 편리하고 가독성 높은 코드를 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;497&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/77U5F/btsCtmTGlG5/AdTYYQJqsuKOLpv4ku9u30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/77U5F/btsCtmTGlG5/AdTYYQJqsuKOLpv4ku9u30/img.png&quot; data-alt=&quot;StringBuilder도 this를 반환한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/77U5F/btsCtmTGlG5/AdTYYQJqsuKOLpv4ku9u30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F77U5F%2FbtsCtmTGlG5%2FAdTYYQJqsuKOLpv4ku9u30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;497&quot; height=&quot;350&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;497&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;StringBuilder도 this를 반환한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더를 이용하여 객체를 생성할 때는, 아래와 같이 생성한다.&lt;/p&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;Pizza pizza = new Pizza.Builder(&quot;씬&quot;)
                .sauce(&quot;바베큐&quot;)
                .topping(&quot;페퍼로니&quot;)
                .hotSauce(5)
                .cheeseSauce(3)
                .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;필수 매개변수인 dough는 Builder의 생성자를 통해 설정하고, 선택적 매개변수들은 체인 형태의 메서드 호출을 통해 설정했다. 최종적으로는 build 메서드를 호출하여 Pizza 객체를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StringBuilder의 append()로 이어붙이고 toString()으로 마무리 짓는다고 생각하면 편하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 알 수 있듯이 &lt;b&gt;어떤 매개변수에 어떤 값이 들어가는지 명확히 알 수 있다는 것&lt;/b&gt;, &lt;b&gt;메서드 체이닝을 통해 가독성이 좋다&lt;/b&gt;는 것이 빌더 패턴의 가장 큰 특징이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌더 패턴의 추상클래스 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 패턴을 추상클래스로 유연하게 설계하게 되면 재사용성과 확장성을 높일 수 있는데, 특히 다양한 종류의 유사한 객체를 생성할 때 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 피자를 만들 수 있는 PizzaBuilder를 추상 클래스로 정의한다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;public abstract class PizzaBuilder {
    protected String dough;
    protected String sauce;
    protected String topping;
    protected int hotSauce;
    protected int cheeseSauce;

    public PizzaBuilder dough(String dough) {
        this.dough = dough;
        return this;
    }

    public PizzaBuilder sauce(String sauce) {
        this.sauce = sauce;
        return this;
    }

    public PizzaBuilder topping(String topping) {
        this.topping = topping;
        return this;
    }

    public PizzaBuilder hotSauce(int hotSauce) {
        this.hotSauce = hotSauce;
        return this;
    }

    public PizzaBuilder cheeseSauce(int cheeseSauce) {
        this.cheeseSauce = cheeseSauce;
        return this;
    }

    public abstract Pizza 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;이 추상 클래스를 구현한 구체적인 빌더 클래스를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 각각 피자의 종류인 매운 피자와 치즈 피자를 구현하였고, 각각 메서드에서 build() 단계를 오버라이드하여, 추가적인 설정을 넣어주었다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;public class SpicyPizzaBuilder extends PizzaBuilder {
    @Override
    public Pizza build() {
        // SpicyPizzaBuilder는 핫소스를 기본으로 추가.
        hotSauce = Math.max(hotSauce, 5); // 최소 핫소스 레벨을 5로 설정
        return new Pizza(dough, sauce, topping, hotSauce, cheeseSauce);
    }
}

public class CheesyPizzaBuilder extends PizzaBuilder {
    @Override
    public Pizza build() {
        // CheesyPizzaBuilder는 추가 치즈 소스를 기본으로 설정.
        cheeseSauce = Math.max(cheeseSauce, 3); // 최소 치즈 소스 양을 3으로 설정
        return new Pizza(dough, sauce, topping, hotSauce, cheeseSauce);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 추상 클래스를 구현한 구체적인 빌더 클래스로 객체를 생성한다.&lt;/p&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;Pizza spicyPizza = new SpicyPizzaBuilder()
                    .dough(&quot;씬&quot;)
                    .sauce(&quot;바베큐&quot;)
                    .topping(&quot;페퍼로니&quot;)
                    .build();

Pizza cheesyPizza = new CheesyPizzaBuilder()
                    .dough(&quot;두꺼운&quot;)
                    .sauce(&quot;화이트&quot;)
                    .topping(&quot;모짜렐라&quot;)
                    .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;이렇게 함으로써, 쉽게 코드를 확장하고 유연한 코드 작성이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌더 패턴의 Lombok 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lombok에서는&amp;nbsp;Builder를&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;@Builder&lt;/p&gt;
&lt;pre id=&quot;code_1703228958737&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class Pizza {
    private final String dough;
    private final String sauce;
    private final String topping;
    private final int hotSauce;
    private final int cheeseSauce;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;@Builder 어노테이션을 이용하여 손쉽게 Builder를 구현할 수 있고,&lt;/p&gt;
&lt;pre id=&quot;code_1703229011039&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Pizza pizza = Pizza.builder()
                   .dough(&quot;씬&quot;)
                   .sauce(&quot;바베큐&quot;)
                   .topping(&quot;페퍼로니&quot;)
                   .hotSauce(5)
                   .cheeseSauce(3)
                   .build();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;앞서&amp;nbsp;설명했던&amp;nbsp;것과&amp;nbsp;똑같이&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;@Builder를 사용하면서 추가로 설정할 수 있는 대표적인 설정들은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1703229039901&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Builder // ()안에 설정을 추가한다.
@Builder(builderMethodName = &quot;customBuilder&quot;) // 기본 빌더 메서드 이름을 변경한다.
@Builder(buildMethodName = &quot;createInstance&quot;) // build() 메서드 이름을 변경한다.
// true로 설정 시, 해당 객체의 현재 상태를 기반으로 새로운 빌더를 반환하는 
// toBuilder 메서드를 추가한다. 
@Builder(toBuilder = true) 
public class Pizza {
    private final String dough;
    private final String sauce;
//    private final String topping;
    private final int hotSauce;
    private final int cheeseSauce;
    
    // 해당 필드에 대한 빌더 메소드가 컬렉션을 통째로 넘기는 것이 아닌, 아이템을 하나씩 넘기는 방식으로 변경된다.
    @Singular 
    private List&amp;lt;String&amp;gt; toppings;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자바 빈즈를 빌더처럼 만들면 안 될까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더의 내부 메서드를 보면 Setter와 비슷하게 생겨서, 자바 빈즈 패턴에 반환값만 설정해 주면 쉽게 구현할 수 있을 것 같았다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// 자바 빈즈 패턴의 Setter에 반환 값을 주었다.
public Pizza setSauce(String sauce) {
        this.sauce = sauce;
        return this;
}

// 메서드 체이닝이 가능하다.
Pizza pizza = new Pizza()
                .dough(&quot;씬&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구현하면, 메서드 체이닝이 가능하기 때문에 가독성이 좋은 코드를 작성할 수 있는 것은 사실이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여전히 객체가 생성된 후에도 객체의 상태를 변경할 수 있기 때문에 객체의 불변성과 객체의 완전성은 보장되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;b&gt;항상 빌더로 만드는 것이 좋은 것은 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;하지만, &lt;b&gt;API는 시간이 지날수록 매개변수가 많아지는 경향이 있기 때문에, 애초에 빌더로 시작하는 편이 나을 때가 많다.&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>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/71</guid>
      <comments>https://olrlobt.tistory.com/71#entry71comment</comments>
      <pubDate>Fri, 22 Dec 2023 16:42:01 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 생성자 대신 정적 팩토리 메서드를 고려하라</title>
      <link>https://olrlobt.tistory.com/70</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;생성자 대신 정적 팩토리 메서드를 고려하라&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자(constructor)는 객체 지향 프로그래밍에서 클래스의 인스턴스를 초기화하는 메서드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주된 목적은 객체 생성시점에 필요한 초기화 작업을 수행하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정적 팩토리 메서드&lt;/b&gt;(static factory method)는 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;생성자의 제한점에 대한 대안을 제공하는 재사용 가능한 소프트웨어 설계의 해결책&lt;/span&gt;&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메서드를 생성자 대신 사용함으로써 객체의 생성을 단순화하고 코드의 가독성을 높이는데&lt;br /&gt;사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정적 팩토리 메서드가 생성자보다 좋은 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메서드가 생성자보다 좋은 이유 다섯 가지는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이름을 가질 수 있다.&lt;/li&gt;
&lt;li&gt;호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.&lt;/li&gt;
&lt;li&gt;반환 타입의 하위 객체를 반환할 수 있는 능력이 있다.&lt;/li&gt;
&lt;li&gt;입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.&lt;/li&gt;
&lt;li&gt;정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 이름을 가질 수 있다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자에 넘기는 매개변수와 생성자 자체만으로는 반환될 객체의 특성을 제대로 설명하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 아래와 같이 자동차(Car) 객체를 만들어보자.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;
public static void main(String[] args) {
    Car electricCar = new Car(&quot;테슬라&quot;, &quot;레드&quot;, 2022, 0, CarType.ELECTRIC);
    Car petrolCar = new Car(&quot;모닝&quot;, &quot;블루&quot;, 2020, 50, CarType.PETROL);
    Car hybridCar = new Car(&quot;프리우스&quot;, &quot;화이트&quot;, 2021, 30, CarType.HYBRID);
}

public static class Car {

    private final String name;
    private final String color;
    private final int year;
    private final int oil;
    private final CarType type;

    public Car(String name, String color, int year, int oil, CarType type) {
        this.name = name;
        this.color = color;
        this.year = year;
        this.oil = oil;
        this.type = type;
    }

    // CarType 열거형 정의
    public enum CarType {
        ELECTRIC, PETROL, HYBRID
    }
}
&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;main 메서드를 보면, 같은 생성자로 여러 가지 타입의 Car객체를 생성해 주고 있다.&lt;br /&gt;하지만, 이 &lt;b&gt;생성자만으로는 의미가 명확하게 전달되지 않는다는 것&lt;/b&gt;이 느껴질 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&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;이 코드를 정적 팩토리 메서드를 사용하면, 아래와 같이 변경할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Car electricCar = Car.createElectricCar(&quot;테슬라&quot;, &quot;레드&quot;, 2022);
    Car petrolCar = Car.createPetrolCar(&quot;모닝&quot;, &quot;블루&quot;, 2020, 50);
    Car hybridCar = Car.createHybridCar(&quot;프리우스&quot;, &quot;화이트&quot;, 2021, 30);
}

public static class Car {
    private final String name;
    private final String color;
    private final int year;
    private final int oil;
    private final CarType type;

    private Car(String name, String color, int year, int oil, CarType type) {
        this.name = name;
        this.color = color;
        this.year = year;
        this.oil = oil;
        this.type = type;
    }

    public static Car createPetrolCar(String name, String color, int year, int oil) {
        return new Car(name, color, year, oil, CarType.PETROL);
    }

    public static Car createElectricCar(String name, String color, int year) {
        return new Car(name, color, year, 0, CarType.ELECTRIC);
    }

    public static Car createHybridCar(String name, String color, int year, int oil) {
        return new Car(name, color, year, oil, CarType.HYBRID);
    }

    // CarType 열거형 정의
    public enum CarType {
        ELECTRIC, PETROL, HYBRID
    }
}
&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;main 메서드만 읽어보더라도, &lt;b&gt;어떤 Car객체를 생성하고 있는지 의미를 명확히 알 수 있다.&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;br /&gt;생성자가 여러 개인 경우에 특히 유리하게 작용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이미 생성된 객체를 재사용함으로써 메모리 사용을 줄이고 성능을 향상&lt;/b&gt;할 수 있다.&lt;br /&gt;이는 특히 불변 객체(immutable objects)를 다룰 때 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 예로 Boolean 클래스의 valueOf 메서드는 True, False 값을 가진 Bollean 객체를 반환한다.&lt;br /&gt;이때, 미리 생성해 둔 값을 사용하여 효과적으로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MdQLq/btsCols7exy/MoO3pA5YAemfOoAlIomEwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MdQLq/btsCols7exy/MoO3pA5YAemfOoAlIomEwK/img.png&quot; data-alt=&quot;Boolean은 True, False 객체를 미리 만들어둔다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MdQLq/btsCols7exy/MoO3pA5YAemfOoAlIomEwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMdQLq%2FbtsCols7exy%2FMoO3pA5YAemfOoAlIomEwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;462&quot; height=&quot;240&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Boolean은 True, False 객체를 미리 만들어둔다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;public static Boolean valueOf(boolean b) {
    return (b ? Boolean.TRUE : Boolean.FALSE);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메서드가 인터페이스나 상위 클래스 타입을 반환 타입으로 사용하면서,&lt;br /&gt;실제로는 &lt;b&gt;이 인터페이스나 클래스의 어떤 하위 타입(subtype)의 인스턴스를 반환할 수 있음&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;이 접근 방식은 API의 유연성을 제공해 주고,&lt;br /&gt;클라이언트에 필요 이상의 많은 정보를 노출하지 않고 API를 설계할 수 있게 해 주어서 보안 측면으로도 유리하다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;
public interface Shape {
    // Shape interface methods
}

public class Circle implements Shape {
    // Circle implementation
}

public class ShapeFactory {
    public static Shape newCircle() {
        return new Circle();
    }
}
&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;br /&gt;newCircle() 메서드는 Shape 타입을 반환하지만, 실제로는 Circle 객체를 생성하여 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 클라이언트 코드가 구체적인 클래스(Circle)보다는 인터페이스(Shape)에 의존하도록 유도하는 것이다.&lt;br /&gt;이는 Circle의 구현이 변경되더라도 클라이언트 코드에 영향을 주지 않고, 더 유연하고 확장 가능한&lt;br /&gt;코드를 작성할 수 있게끔 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Java의 Collections에는 내부 구현의 숨김과 정보 은닉에 관한 특징이 잘 나타나 있다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
List&amp;lt;String&amp;gt; unmodifiableList = Collections.unmodifiableList(list);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collections.unmodifiableList()는 매개변수로 받은 List를 수정할 수 없는 List로 만들어주는 메서드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;메서드의 반환 타입은 List 인터페이스이지만, 실제로 unmodifiableList()는 List 인터페이스를 구현하고 있는&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;하지만 사용자는 이 클래스를 굳이 알 필요 없이, List로 사용하면 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해, &lt;b&gt;사용자는 해당 클래스의 구현체를 알 필요 없이 List 인터페이스에 맞게 쉽게 사용할 수 있고,&lt;/b&gt;&lt;br /&gt;그와 동시에 &lt;b&gt;내부 구현이 숨겨지면서 정보은닉에 유용하게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 메서드는 &lt;b&gt;매개변수의 값이나 타입에 따라 다른 클래스의 인스턴스를 반환&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;또한, 조건적으로 객체를 생성하게 할 수 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식을 사용하면, 다양한 타입의 객체를 생성하는 복잡한 로직을 한 메서드 안에 캡슐화할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;public interface Parser {
    Object parse(String input);
}

public class JsonParser implements Parser {
    public Object parse(String input) {
        // JSON parsing
        return new Object();
    }
}

public class XmlParser implements Parser {
    public Object parse(String input) {
        // XML parsing
        return new Object();
    }
}

public class ParserFactory {
    public static Parser getParser(String type) {
        if (&quot;JSON&quot;.equals(type)) {
            return new JsonParser();
        } else if (&quot;XML&quot;.equals(type)) {
            return new XmlParser();
        }
        throw new IllegalArgumentException(&quot;Unknown type: &quot; + type);
    }
}&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;위 예제에서, ParserFactory의 getParser를 통해 type을 매개변수로 받고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, type이 Json이라면 JsonParser 객체를, XML이라면 XMLParser 객체를 반환해 준다.&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;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메서드의 유연성과 확장성을 매우 잘 보여주는 특징이다.&lt;br /&gt;이 특징은 특히 서비스 제공자 프레임워크(Service Provider Framework) 같은 디자인 패턴에서 유용하게 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 제공자 프레임워크는 세 가지 주요 컴포넌트로 구성된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서비스 인터페이스&lt;/li&gt;
&lt;li&gt;서비스 제공자 인터페이스&lt;/li&gt;
&lt;li&gt;서비스 접근 API&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 이전에는 JDBC가 서비스 제공자 프레임워크에 해당되고,&lt;br /&gt;자바 6 이후에는 ServiceLoader가 제공되어 프레임워크를 직접 만들 필요가 거의 없어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메서드는 실행 시간(Runtime)에 클래스가 로드되고 인스턴스가 생성된다.&lt;br /&gt;이 방식은 &lt;b&gt;메서드를 정의할 때는 해당 클래스가 존재하지 않더라도,&lt;/b&gt;&lt;br /&gt;&lt;b&gt;나중에 클래스가 로드되면 해당 클래스의 인스턴스를 반환&lt;/b&gt;할 수 있게 한다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;public interface Service {
    // 서비스 인터페이스 정의
}

public class ServiceProvider {
    // 서비스 제공자 구현
}

    // 서비스 로더를 사용하여 서비스 제공자 로드
ServiceLoader&amp;lt;Service&amp;gt; loader = ServiceLoader.load(Service.class);
for (Service service : loader) {
    // 사용 가능한 서비스 사용
}
&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;ServiceLoader.load(Service.class) 호출은 Service 인터페이스의 모든 사용 가능한 구현체를 로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, Service 인터페이스를 구현하는 구체적인 클래스들은 ServiceLoader가 작성되는 시점에 존재하지 않을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정적 팩토리 메서드 단점&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 상속의 제한&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메서드를 사용하는 클래스는 대체로 생성자를 공개하지 않고, 객체 생성을 전적으로 이러한 메서드에 의존하도록 설계된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 클래스가 private 또는 protected 생성자만 제공하고, 정적 팩토리 메서드로만 인스턴스를 생성할 수 있도록 설계되었을 때, 이 클래스를 상속받는 것이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 객체 생성을 전적으로 메서드에 의존하게 설계되기 때문에, 이 클래스를 상속받아 새로운 하위 클래스를 만드는 것도 어렵거나 불가능할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;public class BaseClass {
    private BaseClass() {
        // private 생성자
    }

    public static BaseClass createInstance() {
        return new BaseClass();
    }
}

public class DerivedClass extends BaseClass {
    public DerivedClass() {
        // 컴파일 에러: BaseClass의 생성자에 접근할 수 없음
    }
}
&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;위 예제에서, DerivedClass클래스는 BaseClass를 상속받으려 하지만, BaseClass의 생성자에 접근할 수 없기 때문에 컴파일 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 정적 팩토리 메서드 발견성 문제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 언급한 이름을 지을 수 있다는 특징과 상반된 단점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름을 지음으로써 코드의 의미와 명확성을 확실히 챙길 수 있는 반면, &lt;b&gt;개발자가 해당 정적 팩토리 메서드의 존재를 모른다면 다 의미 없다는 말이다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;public class Complex {
    private double real;
    private double imaginary;

    private Complex(double real, double imaginary) {
        this.real = real;
        this.imaginary = imaginary;
    }

    public static Complex fromPolar(double magnitude, double angle) {
        return new Complex(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
    }
}

// 사용 시
Complex c = Complex.fromPolar(1, Math.PI);
&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;br /&gt;개발자는 fromPolar() 메서드를 이용하여 복소수를 편하게 만들면 좋겠지만, 개발자는 해당 메서드를 찾기 위한 노력을 해야 한다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정적 팩토리 메서드 명명 방식&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[Type] from:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수 하나를 받아서 해당 타입의 인스턴스를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) Date.from(Instant)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;of:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 매개변수를 받아 적합한 타입의 인스턴스를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) List.of(1, 2, 3)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;valueOf:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수로 전달된 값으로 해당 타입의 인스턴스를 반환한다.&lt;br /&gt;valueOf는 주로 타입 변환에 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) Integer.valueOf(String)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;getInstance, instance:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스를 반환하지만, 같은 인스턴스임이 보장되지 않는다.&lt;br /&gt;매개변수에 따라 다른 인스턴스를 반환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) Calendar.getInstance()&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;newInstance, create:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 인스턴스를 생성한다.&lt;br /&gt;getInstance와 달리 항상 새로운 인스턴스를 생성함을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) Array.newInstance(Class, int)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;get [Type]:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 타입의 객체를 반환합니다.&lt;br /&gt;이 방식은 주로 다른 클래스나 타입의 객체를 반환할 때 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) Files.getBufferedReader(Path)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;new [Type]:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;newInstance와 유사하지만, 생성되는 객체의 타입을 명시적으로 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) Files.newBufferedReader(Path)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[Type]:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getType, newType의 간결한 버전이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) Collections.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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/70</guid>
      <comments>https://olrlobt.tistory.com/70#entry70comment</comments>
      <pubDate>Thu, 21 Dec 2023 13:20:46 +0900</pubDate>
    </item>
    <item>
      <title>[SSAFY/회고록] 싸피 10기 1학기 수료와 스터디 경험 회고</title>
      <link>https://olrlobt.tistory.com/69</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1701490896654&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;[회고록] 싸피9기 인터뷰 탈락 후기&quot; data-og-description=&quot;싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정해서, 탈락 후기를 쓰며 실수를 반복&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/28&quot; data-og-url=&quot;https://olrlobt.tistory.com/28&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WsTBp/hyUFbFZ0hX/19LlQSlm9hRjfyTDH7Xm7k/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/bDwSu8/hyUE9g6J2c/DiVEGkv9hyGJKJYcBEMK61/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/bDaRW4/hyUE4GPZHe/6ByPpVZOIZGaEEtOSQDfL1/img.png?width=327&amp;amp;height=425&amp;amp;face=0_0_327_425&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/28&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/28&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WsTBp/hyUFbFZ0hX/19LlQSlm9hRjfyTDH7Xm7k/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/bDwSu8/hyUE9g6J2c/DiVEGkv9hyGJKJYcBEMK61/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/bDaRW4/hyUE4GPZHe/6ByPpVZOIZGaEEtOSQDfL1/img.png?width=327&amp;amp;height=425&amp;amp;face=0_0_327_425');&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기 인터뷰 탈락 후기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정해서, 탈락 후기를 쓰며 실수를 반복&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;figure id=&quot;og_1701490903906&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;[회고록] 싸피 10기 합격 후기&quot; data-og-description=&quot;[회고록] 싸피9기 인터뷰 탈락 후기 싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/58&quot; data-og-url=&quot;https://olrlobt.tistory.com/58&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MEqpc/hyUFdw2tAt/2KCP9ZN42983JO7KpdPNY1/img.png?width=800&amp;amp;height=1237&amp;amp;face=0_0_800_1237,https://scrap.kakaocdn.net/dn/lXjut/hyUE9VHRbt/jzWxewpeuktKACCrXjw011/img.png?width=800&amp;amp;height=1237&amp;amp;face=0_0_800_1237,https://scrap.kakaocdn.net/dn/tmazl/hyUE7cvVED/2p9LWXIF2Ywv2hBMm3BG8K/img.png?width=1053&amp;amp;height=1713&amp;amp;face=0_0_1053_1713&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/58&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/58&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MEqpc/hyUFdw2tAt/2KCP9ZN42983JO7KpdPNY1/img.png?width=800&amp;amp;height=1237&amp;amp;face=0_0_800_1237,https://scrap.kakaocdn.net/dn/lXjut/hyUE9VHRbt/jzWxewpeuktKACCrXjw011/img.png?width=800&amp;amp;height=1237&amp;amp;face=0_0_800_1237,https://scrap.kakaocdn.net/dn/tmazl/hyUE7cvVED/2p9LWXIF2Ywv2hBMm3BG8K/img.png?width=1053&amp;amp;height=1713&amp;amp;face=0_0_1053_1713');&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;[회고록] 싸피 10기 합격 후기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[회고록] 싸피9기 인터뷰 탈락 후기 싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;내가 정말 들어가고 싶었던 삼성 청년 SW 아카데미의 5개월간의 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;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;p data-ke-size=&quot;size16&quot;&gt;또한 내가 SSAFY 10기 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;그리고 1학기에 이룬 성과와 앞으로 어떻게 할 것인가로 마무리하겠다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 스타트캠프&lt;/b&gt;&lt;/h2&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;이후, 1주가 지난 시점에서 임시로 배정된 반을 반 배정 시험을 통하여 본반으로 배정하는데, 이러한 과정 때문에 많은 친구들이 첫 1주 임시반에 있는 동안에는 소극적이고, 친구를 만드려고 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 나는 &lt;b&gt;여기서 친해진 7명 정도의 친구들과 1학기 동안 스터디&lt;/b&gt;를 같이 진행할 정도로 친해졌고, 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;잠깐 보고 말 사람이라고 허투루 대하지 않고, 최선을 다했던 덕분에, 좋은 사람들과 함께 더 성장할 수 있는 기회를 얻었다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; &amp;zwj;♂️본과정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 과정은 백엔드 기초 교육과 알고리즘 수업을 번갈아가면서 진행되었다. 나는 이 과정에서 수업하는 대부분의 내용을 이미 국비교육에서 듣거나, 강의를 통하여 알고 있었기 때문에, 따로 시간을 투자하면서 공부하지 않고, 개인적으로 공부를 진행하거나 스터디 진행에 힘썼던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;또한, 1학기 전반에 걸쳐 프로젝트를 진행하는데, 이를 &quot;관통 프로젝트&quot;라고 부른다. 배운 내용을 하나하나 적용해 나가며, 페어와 함께 프로젝트를 진화시켜 가는 과정을 거친다. 하나를 배우고 나면, 그 내용을 적용시키기 위해 프로젝트를 뒤엎기도 하고, 페어가 바뀌면 더 좋은 결과물을 위해 서로의 코드를 합치기도 한다. 처음에는 &quot;이럴 거면 그냥 처음부터 교육 다 하고, 프로젝트를 시작하는 게 더 효과적이지 않나?&quot;라는 생각도 들었지만, 모든 교육은 나를 중심으로 이루어지는 것이 아니니까, 처음 배우는 사람들에게는 꽤나 효과적인 교육 방법이라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 모든 교육이 상당히 체계적이며, 진도는 상당히 빠르게 진행되었기 때문에, 완전히 처음 배우는 사람들이라면 개인적인 시간을 많이 투자해야 했을 것으로 생각 든다.&lt;/p&gt;
&lt;p data-ke-size=&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 data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 스터디&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 과정 외에, 학생들은 자율적으로 스터디를 만들어 학습을 진행한다. 반에 따라서는 강제로 스터디를 만들게도 한다고 하는데, 우리 반은 완전 자율적인 반이었다.&lt;/p&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;b&gt;직접 인원을 모아서 스터디 장으로 진행&lt;/b&gt;하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했듯, 알고리즘 스터디는 많은 성과를 이룬 성공적인 스터디라고 생각해서 진행 방식과 경험을 자세히 소개하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️알고리즘 스터디&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1학기가 시작하고 얼마 지나지 않아 근처에 앉은 친구들과 스터디를 진행하려고 계획을 했고, 반 친구들과 같이 진행하면서 친해지면 좋겠다는 생각에 MatterMost(SSAFY에서 사용하는 메신저)를 통해 모집 공고를 올렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PDRbN/btsBjSkq72B/Kxka1sKdKZRqFrmGZVA2K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PDRbN/btsBjSkq72B/Kxka1sKdKZRqFrmGZVA2K1/img.png&quot; data-alt=&quot;MatterMost 소통 채널을 통하여 모집글을 게시하였다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PDRbN/btsBjSkq72B/Kxka1sKdKZRqFrmGZVA2K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPDRbN%2FbtsBjSkq72B%2FKxka1sKdKZRqFrmGZVA2K1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;447&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MatterMost 소통 채널을 통하여 모집글을 게시하였다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 6명 정도면 좋겠다는 생각으로, 초창기 인원 3명을 모은 뒤 MatterMost 소통 채널을 이용하여 모집 공지 글을 올렸다.&amp;nbsp;&lt;/p&gt;
&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;학기 마지막에는 17반 인원 &lt;b&gt;23명 중 무려 20명&lt;/b&gt;이 스터디에 참여하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(남은 3명은 학기 중 취업에 나갔거나, 코테에 떨어져 본 적 없다거나, 삼성 SDS_B형 보유자이시다.. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;743&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXXHwv/btsBkj9W8Tx/revqWS4kHC0BLkOW9KtC70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXXHwv/btsBkj9W8Tx/revqWS4kHC0BLkOW9KtC70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXXHwv/btsBkj9W8Tx/revqWS4kHC0BLkOW9KtC70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXXHwv%2FbtsBkj9W8Tx%2FrevqWS4kHC0BLkOW9KtC70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1568&quot; height=&quot;743&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;743&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스터디를 정식적으로 운영해 본 것도 처음이었고, 이렇게 많은 인원이 들어올 거라는 생각은 전혀 하지 못했어서, 진행에 많은 시간을 투자했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 인원들의 니즈를 충족시킬 진행방법은 무엇일지 오랫동안 고민하고, 같이 하기로 한 형들에게 물어봐가면서 진행 방법을 정했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행 방식을 간단히 소개하자면,&amp;nbsp;매주 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;3~4명씩&lt;/span&gt; 새로운 조를 형성해 주고, 주차별 문제를 출제해 준다. 스터디원들은 조원들과 소통하면서 문제를 해결해 오고, 정해진 시간에 오프라인 코드 리뷰를 진행했다. &lt;b&gt;최대한 많고 최대한 다양한 코드를 접할 수 있도록 하는 것에 초점&lt;/b&gt;을 맞췄다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 진행 방식은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;진행방식&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 조를 형성할 때, Java를 안 쓰던 사람들도 있었을 테니 Java에 익숙하거나 알고리즘 실력이 있는 사람들에게 양해를 구하고 조장으로 고정 배치를 한 상태로 진행했다. 처음 배울 때 Java를 원래 쓰던 사람에게 배우는 것이, 가장 빨리 성장할 수 있다고 생각해서였다. 하지만 이렇게 진행이 계속되다 보니, 스터디가 아닌 멘토링이 되어간다는 생각이 들어서 이내 전체 랜덤하게 조를 형성하기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 조를 형성하는 것도 네이버 사다리 타기를 통하여 일일이 수동으로 작업하였었는데, 자동화의 필요성을 느껴 GithubActions를 공부하여 자동화 시스템을 구축하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N9Gaq/btsBklG9fUa/DgrIL6CKsQltLyrooCYVr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N9Gaq/btsBklG9fUa/DgrIL6CKsQltLyrooCYVr1/img.png&quot; data-alt=&quot;랜덤한 로직이 돌아간다는 것을 로그를 통해 확인할 수 있도록 하였다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N9Gaq/btsBklG9fUa/DgrIL6CKsQltLyrooCYVr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN9Gaq%2FbtsBklG9fUa%2FDgrIL6CKsQltLyrooCYVr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;856&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;랜덤한 로직이 돌아간다는 것을 로그를 통해 확인할 수 있도록 하였다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, 편성된 조를 지정된 시간에 MatterMost 채널의 Webhook을 통하여 스터디원들에게 공지해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eP4IGp/btsBhXU0UE7/OxelMvTDkaBfafg7GFaE5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eP4IGp/btsBhXU0UE7/OxelMvTDkaBfafg7GFaE5K/img.png&quot; data-alt=&quot;MatterMost Webhook으로 조 편성을 자동화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eP4IGp/btsBhXU0UE7/OxelMvTDkaBfafg7GFaE5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeP4IGp%2FbtsBhXU0UE7%2FOxelMvTDkaBfafg7GFaE5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;307&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MatterMost Webhook으로 조 편성을 자동화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이 밖에도 스터디원들이 열심히 쌓아 올린 자료가 누군가의 PR 실수로 인하여 사라지는 일이 없도록, GithubActions를 통하여 PR 검증 체계를 만들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히. docs/AuthRegistry.txt 에는 아래와 같은 형식의 이름들이 : 콜론으로 매칭되어 있는데,&lt;/p&gt;
&lt;pre id=&quot;code_1701649389320&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;olrlobt:LeeSeungheon  // Githuv사용자명 : 폴더이름&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;PR을 날린 Github 사용자가 수정한 파일이 폴더 이름과 같지 않다면, 아래와 같이 X표시와 함께 Merge를 진행할 수 없도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lfk79/btsBl6XgHaV/CvaQqiVfcosMrLFed7e8J1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lfk79/btsBl6XgHaV/CvaQqiVfcosMrLFed7e8J1/img.png&quot; data-alt=&quot;다른 폴더를 수정했다면 실패 표기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lfk79/btsBl6XgHaV/CvaQqiVfcosMrLFed7e8J1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLfk79%2FbtsBl6XgHaV%2FCvaQqiVfcosMrLFed7e8J1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;223&quot; height=&quot;72&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다른 폴더를 수정했다면 실패 표기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 문제 출제는 백준 그룹을 이용하여 문제를 출제하였고, 문제 선정은 직접 문제와 문제를 푼 사람수, 알고리즘 종류, 정답률을 일일이 확인하며 출제를 하였다. 혼자 매주 12문제씩 출제하기에는 다소 시간이 걸리는 작업이어서, SSAFY 17반의 반장 형의 도움을 받아 번갈아 가면서 출제를 하며 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백준에서 문제를 선정하는 과정도 자동화를 해 보려고 고려를 했지만, 백준의 경우 정답률이 낮거나 이해하기 어려운(수학 지식을 요구하는) 등의 문제를 필터링할 수 없을 거 같아서, 최종적으로는 시도하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1751&quot; data-origin-height=&quot;1077&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lhn14/btsBqKVktVF/04S1pxqh1SYjNNJT3fuzy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lhn14/btsBqKVktVF/04S1pxqh1SYjNNJT3fuzy1/img.png&quot; data-alt=&quot;백준 그룹 기능을 이용하여, 스터디원이 풀 문제를 출제하였다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lhn14/btsBqKVktVF/04S1pxqh1SYjNNJT3fuzy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flhn14%2FbtsBqKVktVF%2F04S1pxqh1SYjNNJT3fuzy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1751&quot; height=&quot;1077&quot; data-origin-width=&quot;1751&quot; data-origin-height=&quot;1077&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;백준 그룹 기능을 이용하여, 스터디원이 풀 문제를 출제하였다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 올라간 문제를 스터디원은 모임 시간까지 풀어서 Github에 PR로 코드를 제출하고 코드 리뷰를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 리뷰에는 간단한 규칙이 있었는데, 비난은 절대 절대 금지시켰고, 본인이 자신의 코드를 자신 있게 설명할 수 있도록 본인이 짠 코드에 자신감을 갖고 말하라는 간단한 규칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1371&quot; data-origin-height=&quot;1414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvrj48/btsBvmyHa1Y/3V7ilmF7uszcr7ZK7kuJ30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvrj48/btsBvmyHa1Y/3V7ilmF7uszcr7ZK7kuJ30/img.png&quot; data-alt=&quot;PR로 본인의 풀이 방식을 공유한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvrj48/btsBvmyHa1Y/3V7ilmF7uszcr7ZK7kuJ30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvrj48%2FbtsBvmyHa1Y%2F3V7ilmF7uszcr7ZK7kuJ30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;446&quot; height=&quot;460&quot; data-origin-width=&quot;1371&quot; data-origin-height=&quot;1414&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PR로 본인의 풀이 방식을 공유한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR을 제출할 때는 간단한 템플릿을 이용해서 해결 방법과 각자 고민한 것을 메모할 수 있는 칸을 만들었고,&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;이렇게 7월 말부터 11월 중반까지 1학기 내내 알고리즘 스터디를 운영하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히도, 열심히 하고자 하는 학구열 넘치는 친구들 덕분에 알고리즘 스터디는 순조롭게 진행이 되었고, &lt;b&gt;우리 스터디는 SSAFY 10기 우수 스터디로 매 달 선정&lt;/b&gt;이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학기 말에는 강사님과 상담할 때, 강사님께서 스터디 운영 방식에 대해 자세히 물어보시기도 하였고, 나의 마지막 롤링페이퍼에 스터디에 관한 이야기가 많았던 것으로 보아, 꽤나 성공적인 알고리즘 스터디를 운영했지 않았나 하는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;알고리즘 스터디 Github&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시라도 해당 스터디가 더 궁금하다면 아래 링크를 참고하면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/SSAFY-10th-Seoul17/algorithm_ssafy&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/SSAFY-10th-Seoul17/algorithm_ssafy&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701798601385&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 - SSAFY-10th-Seoul17/algorithm_ssafy: SSAFY 서울 17반 알고리즘 스터디&quot; data-og-description=&quot;SSAFY 서울 17반 알고리즘 스터디. Contribute to SSAFY-10th-Seoul17/algorithm_ssafy development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/SSAFY-10th-Seoul17/algorithm_ssafy&quot; data-og-url=&quot;https://github.com/SSAFY-10th-Seoul17/algorithm_ssafy&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pHpop/hyUIF0kFlT/o8KjVkSRfLPvsc8E4KeeK1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/SSAFY-10th-Seoul17/algorithm_ssafy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/SSAFY-10th-Seoul17/algorithm_ssafy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pHpop/hyUIF0kFlT/o8KjVkSRfLPvsc8E4KeeK1/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 - SSAFY-10th-Seoul17/algorithm_ssafy: SSAFY 서울 17반 알고리즘 스터디&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSAFY 서울 17반 알고리즘 스터디. Contribute to SSAFY-10th-Seoul17/algorithm_ssafy development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; 기술 블로그 스터디&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 2023년 1월부터 블로그를 시작하면서 천천히 글을 쓰기 시작했고, SSAFY에서 많은 지식을 얻은 것을 바탕으로 많은 포스팅을 올리고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자로서 기록하고 문서화하는 것은 중요하다고 생각했기 때문에, 이를 함께할 친구들을 모아 같이 진행하면 더 좋을 것이라 생각되어 스터디를 기획하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciLAPT/btsBtJhcpSn/jRNw7CYWp5o1C9EuSFH9eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciLAPT/btsBtJhcpSn/jRNw7CYWp5o1C9EuSFH9eK/img.png&quot; data-alt=&quot;MatterMost 소통채널을 이용하여 모집하였다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciLAPT/btsBtJhcpSn/jRNw7CYWp5o1C9EuSFH9eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciLAPT%2FbtsBtJhcpSn%2FjRNw7CYWp5o1C9EuSFH9eK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;557&quot; height=&quot;289&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MatterMost 소통채널을 이용하여 모집하였다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술 블로그 스터디는 별도의 모임을 갖지는 않고, 블로그에 포스팅을 올리면 해당 포스팅을 스터디원들과 공유하면서 서로 자극을 많이 받자는 목적이 강했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그를 처음 하는 친구들을 포함하여 6명 정도의 인원이 하겠다는 의지를 보였고, MatterMost 채널에서 진행을 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 다들 어떤 것을 주제로 포스팅을 해야 할지 많은 고민들을 했지만, 차차 포스팅이 쌓여가면서 자연스러운 포스팅들을 많이 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8vFuE/btsBuccvVzi/8V3Zxjr7jk1vjUC3okBip1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8vFuE/btsBuccvVzi/8V3Zxjr7jk1vjUC3okBip1/img.png&quot; data-alt=&quot;GithubActions로 포스팅을 자동 공유하게 만들었다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8vFuE/btsBuccvVzi/8V3Zxjr7jk1vjUC3okBip1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8vFuE%2FbtsBuccvVzi%2F8V3Zxjr7jk1vjUC3okBip1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1050&quot; height=&quot;686&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GithubActions로 포스팅을 자동 공유하게 만들었다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서, 알고리즘 스터디에서도 MatterMost Webhook과 GithubActions로 반복적인 작업을 자동화했던 경험을 말했었다. 블로그 스터디에서도 역시 포스팅을 공유하는 과정이 나와 스터디원들에게 불필요한 작업이라 생각되었고, 자동으로 링크를 공유하는 프로젝트를 진행하였다.&lt;/p&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과 Java의 크롤링 라이브러리인 Jsoup을 이용하여 Tistory와 Velog의 포스팅을 크롤링해 오고, GithubActions로 매일 정해진 시간에 새로운 포스팅이 있는지 탐색하여 webhook으로 전송하는 방식으로 제작했다.&lt;/p&gt;
&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;a href=&quot;https://github.com/olrlobt/Blog-bot&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/olrlobt/Blog-bot&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701799422577&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 - olrlobt/Blog-bot: MM blog 포스팅 알림 시스템&quot; data-og-description=&quot;MM blog 포스팅 알림 시스템. Contribute to olrlobt/Blog-bot development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/olrlobt/Blog-bot&quot; data-og-url=&quot;https://github.com/olrlobt/Blog-bot&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jgU35/hyUE7KYd8U/oMCiIfs6VrRIkeDmYSvQ60/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236&quot;&gt;&lt;a href=&quot;https://github.com/olrlobt/Blog-bot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/olrlobt/Blog-bot&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jgU35/hyUE7KYd8U/oMCiIfs6VrRIkeDmYSvQ60/img.png?width=1200&amp;amp;height=600&amp;amp;face=973_148_1053_236');&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 - olrlobt/Blog-bot: MM blog 포스팅 알림 시스템&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MM blog 포스팅 알림 시스템. Contribute to olrlobt/Blog-bot development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;기술 블로그를 운영하면서 가장 중요한 것은 공부를 위해 블로그를 쓰는 것이지, 블로그를 위해 공부를 하는 것이 되면 안 된다고 생각한다. 블로그 운영 초기에 나는 공부하는 시간보다 포스팅에 시간을 더 많이 투자하기도 하였었으니 가장 잘 알고 있는 부분이었다.&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;따라서, 업로드 주기에 대한 제한은 해제하였고, 자유롭게 본인이 여유로울 때 포스팅을 할 수 있도록 진행 방식을 변경하였다. 1학기가 진행됨에 따라 다들 공부에 투자해야 할 시간이 많아지면서, 포스팅 수는 점점 줄었지만 기술 블로그를 시작하게 한 것에 큰 의의를 두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; 북스터디&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 스타트 캠프 때 만난 친구들과는 함께 북스터디를 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학기 초부터 같이 하자는 말들이 오고 갔지만, 알고리즘 스터디와 학업에 집중하기에도 시간이 많이 부족했기 때문에 처음에는 거절을 하였었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 알고리즘 스터디가 어느 정도 안정이 되고 나서야 다시 같이 하자는 말이 오가게 되었고, 우리 반 내에서도 이미 북스터디를 진행하는 일원들이 있었지만, 먼저 같이 하기로 한 스타트 캠프 친구들과 북스터디를 진행하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;191&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dm9pL4/btsBqKHMRpr/IyXM9qLiupGbddpI3QdKIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dm9pL4/btsBqKHMRpr/IyXM9qLiupGbddpI3QdKIK/img.png&quot; data-alt=&quot;자바 성능 튜닝 이야기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dm9pL4/btsBqKHMRpr/IyXM9qLiupGbddpI3QdKIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdm9pL4%2FbtsBqKHMRpr%2FIyXM9qLiupGbddpI3QdKIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;191&quot; height=&quot;264&quot; data-origin-width=&quot;191&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자바 성능 튜닝 이야기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책은 이상민 저자의 &quot;개발자가 반드시 알아야 할 자바 성능 튜닝 이야기&quot;로 진행을 하였다. 해당 책은 연로그(&lt;a href=&quot;https://yeonyeon.tistory.com/120&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yeonyeon.tistory.com/120&lt;/a&gt;)에도 언급이 되어 있는 책으로, 스터디를 진행하는 친구들이 챕터와 내용을 보고 결정한 책이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 평소에 성능에 관해 관심이 많았기 때문에 동의 하에 진행이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;북스터디는 다들 진행해 본 경험이 없어서, 구글에 많이 나오는 방식들을 참고했다. 특히 우아한 스터디에서 &quot;모던 자바 인 액션&quot; 책으로 북스터디를 진행한 레퍼지토리를 많이 참조하였다. (&lt;a href=&quot;https://github.com/woowacourse-study/2022-modern-java-in-action&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/woowacourse-study/2022-modern-java-in-action)&lt;/a&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;진행 방식을 간단하게 설명하면, 정해진 기간까지 정해진 챕터를 읽고, 해당 내용에 관해 궁금한 내용을 Github 레퍼지토리에 Issue로 작성한다. 해당 이슈를 담당하는 다른 스터디원은 해당 내용에 대해 자료를 조사하고 정리하여 Issue에 답변하고, 모임에서 발표까지 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행 방식 자체에는 되게 만족스러웠지만, 연로그에도 언급되어 있듯이 오래된 책이라는 점에서 아쉬운 점이 많았다. 또한 흥미로운 내용도 많았지만, 아직 경험해 보지 않은 멀티스레드 환경이나, 성능 테스트 관련한 챕터가 많아서 이해에 어려움이 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 해당 책을 다 끝내고, 두 번째 책으로&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&quot;Effective Java&quot;를 선정하여&lt;/span&gt;&amp;nbsp;진행하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p5ecE/btsBtHwW0ZM/KEKUZ5i1dLkL6JxkyXVoGk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p5ecE/btsBtHwW0ZM/KEKUZ5i1dLkL6JxkyXVoGk/img.jpg&quot; data-alt=&quot;Effective Java&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p5ecE/btsBtHwW0ZM/KEKUZ5i1dLkL6JxkyXVoGk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp5ecE%2FbtsBtHwW0ZM%2FKEKUZ5i1dLkL6JxkyXVoGk%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;275&quot; height=&quot;183&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Effective Java&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 책으로 스터디를 진행한 지는 이제 2회 차 정도 되었지만, 책의 내용이 아직까진 너무 만족스럽고 아이템 별로 실습을 따라 할 수 있는 부분이 재밌었다. 아직 더 진행을 해 보아야 알겠지만, 이 책을 마무리할 때쯤에는 얻은 것이 많을 것이라는 생각이 든다.&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: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;개발자가 반드시 알아야 할 자바 성능 튜닝 이야기&quot;&lt;/span&gt; &amp;nbsp;북스터디의 진행은 아래에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/SSARTEL-10th/JPTS_bookstudy&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/SSARTEL-10th/JPTS_bookstudy&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701801239214&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 - SSARTEL-10th/JPTS_bookstudy: &amp;quot;개발자가 반드시 알아야 할 자바 성능 튜닝 이야기&amp;quot; 완전 정복&quot; data-og-description=&quot;&amp;quot;개발자가 반드시 알아야 할 자바 성능 튜닝 이야기&amp;quot; 완전 정복. Contribute to SSARTEL-10th/JPTS_bookstudy development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/SSARTEL-10th/JPTS_bookstudy&quot; data-og-url=&quot;https://github.com/SSARTEL-10th/JPTS_bookstudy&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/znXvc/hyUFbs3Pbd/fplRVUTaw8KOrCKkpqnIwK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/SSARTEL-10th/JPTS_bookstudy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/SSARTEL-10th/JPTS_bookstudy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/znXvc/hyUFbs3Pbd/fplRVUTaw8KOrCKkpqnIwK/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 - SSARTEL-10th/JPTS_bookstudy: &quot;개발자가 반드시 알아야 할 자바 성능 튜닝 이야기&quot; 완전 정복&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&quot;개발자가 반드시 알아야 할 자바 성능 튜닝 이야기&quot; 완전 정복. Contribute to SSARTEL-10th/JPTS_bookstudy development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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학기에 한 것이 별로 없다고 생각했는데, 정말 많은 일들이 있었고 많은 것들을 얻지 않았나 생각이 든다. 특히 스터디도 많이 진행해서, 학기 중에 칼퇴근하는 일이 없었던 것도 생각하면, 열심히 1학기를 다니지 않았나 뿌듯한 생각이 든다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 내가 1학기 동안 SSAFY를 다니면서 이루어낸 것은 무엇이 있을까 정리해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 성과&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;우수 스터디 선정과 &lt;/b&gt;&lt;b&gt;3개의 스터디&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3개의 스터디를 진행했다. 스터디를 하며 많은 지식과 많은 친구들을 얻었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 스터디를 스터디 장으로 성공적으로 진행하며 우수 스터디로 매 달 선정되었고 많은 경험을 얻었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 운영한 스터디를 많은 친구들이 좋아해 줘서, 너무 뿌듯했다.&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;1257&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5W0as/btsBCZpIBUw/w9kKwK44kQLTwOAVboLMkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5W0as/btsBCZpIBUw/w9kKwK44kQLTwOAVboLMkk/img.png&quot; data-alt=&quot;SSAFY 10기 우수 스터디 선정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5W0as/btsBCZpIBUw/w9kKwK44kQLTwOAVboLMkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5W0as%2FbtsBCZpIBUw%2Fw9kKwK44kQLTwOAVboLMkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1257&quot; height=&quot;576&quot; data-origin-width=&quot;1257&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSAFY 10기 우수 스터디 선정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;삼성전자 SW역량평가 B형 취득&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY에서 진행하는 B형 대비 특강을 수강하고, 학기 진행 중 B형을 취득하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전국 SSAFY 10기 교육생 1150명 중 이번 10기에서는 44명의 B형 취득자가 나왔다고 한다. 그중 우리 반에서만 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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z8GjV/btsBqsgkzDl/OjExn0txdvlSUDyZGabw4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z8GjV/btsBqsgkzDl/OjExn0txdvlSUDyZGabw4k/img.png&quot; data-alt=&quot;삼성전자 SW역량평가 B형 취득&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z8GjV/btsBqsgkzDl/OjExn0txdvlSUDyZGabw4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz8GjV%2FbtsBqsgkzDl%2FOjExn0txdvlSUDyZGabw4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;310&quot; height=&quot;121&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;227&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;삼성전자 SW역량평가 B형 취득&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1학기 프로젝트 최우수상, 1학기 성적우수상&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1학기 관통 프로젝트에서 운 좋게 좋은 페어를 만나 최우수상을 시상하였다. 내 페어를 만났을 때부터, 우리가 가장 많이 했던 두 가지 말이 있는데,&lt;b&gt; &quot;우린 1등 할 거야&quot;&lt;/b&gt;와 &lt;b&gt;&quot;워라밸 절대 지켜&quot;&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 관통 프로젝트를 진행하는 내내 부담을 갖지도 않았고, 밤을 새우면서 무리하게 진행하지도 않았다. 심지어 쉬는 시간엔 절대로 쉬었고, 다른 조가 리허설 할 때는 끝까지 집중해서 들어주었다. 그리고 우리는 최우수 상을 받고 기분 좋게 하이파이브를 쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수상까지 하고 나서 내 페어가 나에게 정말 행복하게 관통 프로젝트를 했다고 말해주었는데, 나도 정말로 재밌고 행복하게 진행해서 더 뿌듯한 시상이었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4023&quot; data-origin-height=&quot;1976&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfFgz9/btsBr5EGykk/zqViRfJDmHuNSMmvJJcyLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfFgz9/btsBr5EGykk/zqViRfJDmHuNSMmvJJcyLk/img.png&quot; data-alt=&quot;1학기 프로젝트 최우수상, 1학기 성적 우수상&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfFgz9/btsBr5EGykk/zqViRfJDmHuNSMmvJJcyLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfFgz9%2FbtsBr5EGykk%2FzqViRfJDmHuNSMmvJJcyLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4023&quot; height=&quot;1976&quot; data-origin-width=&quot;4023&quot; data-origin-height=&quot;1976&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1학기 프로젝트 최우수상, 1학기 성적 우수상&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 어땠어&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 누군가가 나에게 1학기 SSAFY 생활은 어땠냐고 묻는다면, 당연히 너무 재밌었고 행복했다고 말할 것이다. 요즘 친구들을 만나보면 공통적으로 또래 친구들을 만나기 힘들다고들 한다. 하지만 SSAFY에서는 또래들밖에 없기도 하고, 다 같은 목적을 갖고 있는 친구들이라 이런 친구들을 만날 기회가 있다는 게 너무 좋았다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 진짜 개발공부에만 집중할 수 있는 환경이 된다. 다른 친구들은 오히려 하루 종일 갇혀 있는 게 힘들다고들 하는데, 나는 국비를 다녔었기 때문에 익숙하기도 해서 다른 친구들보다는 괜찮았던 것 같다. 그리고&lt;span&gt;&amp;nbsp;&lt;/span&gt;하루 종일 갇혀있다기엔 꽤나 자유로운 환경에, 오히려 일과 후에도 스스로 남아서 공부할 정도로 다들 체력이 남는 경우가 많다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SSAFY를 다니면서 크나큰 스터디도 운영해 보고, 발표와 같이 자신감을 얻을 기회도 많아서 나 스스로도 많이 성장했다는 생각이 든다. 특히 SSAFY에는 이미 충분히 대단한 친구들이 많은데, 그들에게 많이 배우거나 다른 친구들을 알려주면서 성장할 수 있다는 게 가장 값진 경험이었다는 생각이 든다. 나는 다른 친구들에게 알려줄 기회가 많았던 편인데, 알려주면서도 항상 어떻게 유도하면서 알려줘야 이 친구가 스스로 깨달음을 얻을 수 있을지를 많이 고민하였었다. 이런 고민을 하면서 내가 몰랐던 부분도 알게 되는 경우가 많았고, &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;다양한 관점을 이해하면서 내 생각의 한계를 넓힐 수 있는 많은 기회를 얻었다고 느낀다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 아쉬워&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 1학기가 끝났다니, 너무 아쉬운 마음이 든다. 이제 2학기가 되면 같은 반이었던 친구들과 뿔뿔이 흩어지고 새로운 반으로 재 편성이 된다. 가족같이 지냈던 친구들이라 너무 좋은 친구들이라 더 아쉬운 마음이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한편으로는 이때쯤 헤어져야 취업에 더 집중할 수 있을 테니, SSAFY는 이것까지 생각한 정말 무시무시하게 체계적인 기관이라는 생각도 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;또 아쉬웠던 점은, 1학기에 취업 지원을 많이 하지 않았다는 것이다. &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY에 처음 들어갈 때, 9기 친구가 내게 해 준 말이 있는데, 1학기에 자기소개서를 열심히 쓰라는 말이었다. 나는 내가 보여줄 것이 부족해서, 나를 증명할 것들이 많이 부족해서 지원을 안 한 경우가 대다수였고, 이 증명할 것들을 채우기 위해 SSAFY에 들어왔다.&lt;/p&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학기가 마무리되어 갈 때, 슬슬 지원서를 넣기 시작했는데, 생각보다 이력에 쓸 것들을 막힘없이 써 내려가는 것을 느꼈다. 그만큼 내가 SSAFY에서 이룬 것들이 많고, 준비가 많이 되었다고 생각하지만, 1학기에 이루었던 것들을 제외해도 충분히 지원할 수 있었을 것이란 생각이 든다. 지원을 많이 해보고 면접 경험을 많이 쌓아 둘 걸이라는 아쉬운 마음이 가득하다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 앞으로&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 정말 취업이 코 앞으로 다가왔다는 생각이 든다. 준비도 많이 되었고, 이제 미뤄두었던 CS공부도 하고, 여러 기업에 지원하면서 면접경험을 쌓을 일만 남았다. 사실은 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2학기를 준비하는 기간 동안 나는, 포트폴리오 정리와 블로그 포스팅에 더 집중할 생각이다. 기록된 것이야 말로 나를 더 잘 증명할 수 있는 것들이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 마지막으로, 내 롤링페이퍼에 누군가 이런 말을 써줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2314&quot; data-origin-height=&quot;1360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SCENq/btsBvpWuLEk/dfUmK8akgxxDkGCqWAEqm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SCENq/btsBvpWuLEk/dfUmK8akgxxDkGCqWAEqm0/img.png&quot; data-alt=&quot;누구야 대체 나 감동해&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SCENq/btsBvpWuLEk/dfUmK8akgxxDkGCqWAEqm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSCENq%2FbtsBvpWuLEk%2FdfUmK8akgxxDkGCqWAEqm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2314&quot; height=&quot;1360&quot; data-origin-width=&quot;2314&quot; data-origin-height=&quot;1360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;누구야 대체 나 감동해&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 감동적인데? 누군지 전혀 추측이 안 간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군진 모르겠지만 정말 고맙고, 덕분에 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;다들 정말 고마웠고, 2학기도 파이팅 해서 취뽀하자!! &amp;nbsp;SSAFY 10기 서울 17반 화이팅 !!&amp;nbsp;  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-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>else/회고</category>
      <category>ssafy</category>
      <category>SSAFY10기</category>
      <category>SSAFY1학기</category>
      <category>삼성청년SW아카데미</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/69</guid>
      <comments>https://olrlobt.tistory.com/69#entry69comment</comments>
      <pubDate>Wed, 6 Dec 2023 04:53:07 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Garbage Collection(GC , 가비지 컬렉션)과 Garbage Collector의 종류</title>
      <link>https://olrlobt.tistory.com/68</link>
      <description>&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Garbage Collection (GC , 가비지 컬렉션)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Garbage Collection은 JAVA의 메모리 관리 방법 중 하나이며,&amp;nbsp;JVM에 내장된 가바지 컬렉터로 메모리를 관리하는 방법이다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;말 그대로 쓰레기, 개발자가&amp;nbsp;동적으로 할당한 메모리 영역 중 &lt;b&gt;더 이상 사용하지 않는 &lt;span style=&quot;text-align: left;&quot;&gt;객체나 데이터&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;text-align: left;&quot;&gt;를 찾아내어 &lt;b&gt;메모리를 회수&lt;/b&gt;하는 과정을 의미한다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Garbage Collector&lt;/b&gt;&lt;b&gt; (&lt;/b&gt;&lt;b&gt;가비지 컬렉터&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; Garbage Collector는 &lt;span style=&quot;text-align: start;&quot;&gt;Garbage Collection을 수행하는 구체적인 시스템이나 알고리즘을 의미한다. JVM에 내장된 가비지 컬렉터는 Serial GC, Parallel GC, G1 GC 등 여러 가지가 있으며, 각 컬렉터는 서로 다른 전략을 사용하여 GC(가비지 컬렉션)을 수행한다.&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;&quot;&gt;또한, Garbage Collector의 역할은 &lt;b&gt;메모리 할당&lt;/b&gt;, &lt;b&gt;사용 중인 메모리 인식&lt;/b&gt;, &lt;b&gt;사용하지 않는 메모리 인식&lt;/b&gt;이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #1f2328;&quot;&gt;많은 개발자와 문서에서 GC라는 단어는 가비지 컬렉션(&lt;span style=&quot;text-align: start;&quot;&gt;Garbage Collection)을 의미하지만, 위와 같이 맥락에 따라 가비지 컬렉터(&lt;span style=&quot;text-align: start;&quot;&gt;Garbage Collector)를 의미하기도 한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;GC 동작원리&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 메모리 영역은 Heap 메모리와 Non-heap 메모리로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중, GC가 발생하는 영역은 Heap 메모리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Java Heap 구조&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Heap&lt;/span&gt; 메모리는 인스턴스, 배열 쌓이는 곳이다. &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Heap&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;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Java Heap은 객체의 생존 기간과 GC의 최적화를 위해,&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;nbsp;크게 두 부분 &lt;b&gt;Young Generation&lt;/b&gt;과 &lt;b&gt;Old Generation&lt;/b&gt;으로 나누어 설계되었다. 그리고 Young Generation 영역은 Eden 영역과 두 개의 Survivor 영역으로 나누어지므로, &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Java Heap의 구조는 크게 4개의 영역으로 나누어 볼 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; Young Generation &lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Young Generation은 새로 생성된 객체들이 위치하는 공간이다. 대부분의 객체는 금방 사용되지 않게 되므로, 이 영역에서 빠르게 수집된다.&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbY6ek/btsyJwRwHga/PJ4KRtkkO8HhMxRktCj441/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbY6ek/btsyJwRwHga/PJ4KRtkkO8HhMxRktCj441/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbY6ek/btsyJwRwHga/PJ4KRtkkO8HhMxRktCj441/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbY6ek%2FbtsyJwRwHga%2FPJ4KRtkkO8HhMxRktCj441%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;115&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;Eden Space&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;: &lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;객체들이 처음 생성될 때 할당되는 영역이다. Eden 영역의 데이터가 꽉 차면, 이 영역에 있는 객체는 어딘가로 옮겨지거나 삭제되어야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Survivor Space&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;: &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;이 영역은 우선순위 없는 두 부분(S01과 S02)으로 나뉘며,&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt; 두 개의 영역 중 한 영역은 반드시 비어 있어야 한다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Eden에서 GC 이후 살아남은 객체들이 Survivor의 빈 영역으로 이동한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Survivor의 한 영역이 꽉 차면 GC가 되면서 Eden 영역에 있던 객체들과 함께, 또 다른 Survivor 영역으로 이동하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; Old Generation&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Old Generation 영역에 있는 객체들은 일반적으로 애플리케이션 수명주기 동안 계속 사용되는 객체들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young Generation에서 &lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Survivor의 GC&lt;/span&gt; 사이클을 여러 번 살아남은 객체들이 이동하는 영역이고, 살아남는 횟수는 JVM의 설정 값에 따라 달라진다. 기본적으로는 대부분 15회 정도라고 생각하면 된다.&lt;/p&gt;
&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;Old Generation의 크기가 &lt;/span&gt;Young Generation의 크기보다 크기 때문에, 객체가 아주 큰 경우에는 Young Generation에서 Survivor을 거치지 않고 바로 Old Generation으로 넘어갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Minor GC와 Major GC&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Minor GC&lt;/b&gt;:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young Generation 내에서 가비지 컬렉션을 수행할 때 발생하는 GC를 &quot;Minor GC&quot;라고 한다. Minor GC는 Young Generation 내에서만 발생하며, 비교적 빈번하게 발생하지만 영역이 작기 때문에 대체로 빠르게 처리된다.&lt;/p&gt;
&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;Major GC (또는 Full GC)&lt;/b&gt;:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Old Generation에서 가비지 컬렉션을 수행할 때 발생하는 GC를 &quot;Major GC&quot; 또는 &quot;Full GC&quot;라고 한다. Major GC는 Old Generation 뿐만 아니라 전체 Heap을 대상으로 하기도 하며, Minor GC에 비해 덜 빈번하지만, 실행 시간이 훨씬 길다는 특징이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;Garbage Collector의&lt;/span&gt; 종류&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JDK 7 이상에서 지원하는 GC 방식에는 다섯 가지가 있다.&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;color: #000000;&quot;&gt;- Serial Collector&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;color: #000000;&quot;&gt;- Parallel &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector&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;&lt;span style=&quot;color: #000000;&quot;&gt;- &lt;span style=&quot;text-align: start;&quot;&gt;Parallel Compacting&amp;nbsp; &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector&lt;/span&gt; &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;&lt;span style=&quot;color: #000000;&quot;&gt;- CMS &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector&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;&lt;span style=&quot;color: #000000;&quot;&gt;- G1 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector&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;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;각각의 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;Garbage Collector는 약간의 차이가 있지만, &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;Stop-The-World와 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Mark and Sweep 알고리즘을 토대로 GC를 진행한다.&amp;nbsp; &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;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;1. Stop-The-World&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우리가 GC를 신경 써야 하는 이유는, GC가 진행되는 동안 애플리케이션의 다른 동작들은 모두 멈추기 때문이다.&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이렇게 JVM에서 GC를 실행하는 스레드를 제외한 나머지 스레드의 작업을 모두 멈추는 현상을 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Stop-The-World라고 부른다.&lt;/span&gt;&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;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Stop-The-World가 발생하는 이유&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GC를 수행하는 과정에서 사용하지 않는 객체를 식별하고, 회수하는 과정이 진행된다. 이 과정에서 애플리케이션의 스레드가 객체에 접근하는 것을 막기 위해 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Stop-The-World가 발생한다.&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;color: #000000; text-align: start;&quot;&gt;또한, 일부 GC 알고리즘 수행과정에서 Compact 단계를 실행할 때 살아있는 메모리를 이동시켜 &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;메모리 공간을 효율적으로 쓰게 해야 하는데, 이 과정(메모리 조각화를 해결하는 과정)에서 객체를 새로운 주소로 이동시키고 다시 주소를 참조하는 과정이 필요하게 된다. 이때 애플리케이션의 스레드가 해당 객체에 접근하는 것을 막기 위해 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Stop-The-World가 발생하게 된다.&amp;nbsp;&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;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;메모리 조각화 :&lt;br /&gt;&lt;br /&gt;가비지 컬렉션을 통해 불필요한 객체들이 해제되면서 메모리의 여러 위치에 여유 공간이 생기게 되는데, 이러한 여유 공간들이 연속되지 않고 여기저기 분산되어 있을 때 발생하는 현상.&lt;br /&gt;공간이 많아도, 분산으로 인해, 연속적인 공간이 부족하여 메모리 할당을 실패하는 문제가 발생할 수 있다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Mark and Sweep&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Mark (표시):&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 살아있는 객체를 식별하고 표시하는 단계&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Root Set에서 시작하여 접근 가능한 모든 객체를 표시한다.&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;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Root Set :&amp;nbsp;&lt;br /&gt;GC root의 집합&lt;br /&gt;&lt;br /&gt;GC root :&amp;nbsp;&lt;br /&gt;GC과정에서 생존하는 객체를 판별하기 위한 시작점.&lt;br /&gt;예를 들어, Stack영역의 변수들은 Heap영역의 객체를 참조한다. 이때, Stack 영역의 변수가 GC root가 된다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cG7Or3/btsyLIkRyWX/7hHKYvmIWQAK0Bk2cGo201/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cG7Or3/btsyLIkRyWX/7hHKYvmIWQAK0Bk2cGo201/img.png&quot; data-alt=&quot;Mark&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cG7Or3/btsyLIkRyWX/7hHKYvmIWQAK0Bk2cGo201/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcG7Or3%2FbtsyLIkRyWX%2F7hHKYvmIWQAK0Bk2cGo201%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;476&quot; height=&quot;337&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mark&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Sweep (정리):&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Mark 되지 않은 객체를 식별하고 메모리에서 제거하는 단계&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 이 단계에서 메모리의 불필요한 객체들이 제거되고, 재사용 가능한 메모리가 확보된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기까지가 대부분의 GC에서 기본적으로 수행되고, 일부 알고리즘에서는 추가 단계인 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Compact&lt;span&gt;&amp;nbsp; 단계가 존재한다.&lt;/span&gt;&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;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9q9yJ/btsyOHFhmuR/7PhXOKqbOLzCMB0cwAiEA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9q9yJ/btsyOHFhmuR/7PhXOKqbOLzCMB0cwAiEA0/img.png&quot; data-alt=&quot;Sweep&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9q9yJ/btsyOHFhmuR/7PhXOKqbOLzCMB0cwAiEA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9q9yJ%2FbtsyOHFhmuR%2F7PhXOKqbOLzCMB0cwAiEA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1046&quot; height=&quot;348&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sweep&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Compact (압축):&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 메모리 내의 불규칙하게 살아있는 객체들을 한 곳으로 압축하여 연속적인 메모리 블록으로 만드는 단계&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 이 단계는 메모리 조각화를 방지하고, 효과적으로 메모리를 쓸 수 있게 한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NAXNq/btsyLl4w4AZ/ur4CKv3oWQjTwb0TKlGZKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NAXNq/btsyLl4w4AZ/ur4CKv3oWQjTwb0TKlGZKk/img.png&quot; data-alt=&quot;Compact&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NAXNq/btsyLl4w4AZ/ur4CKv3oWQjTwb0TKlGZKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNAXNq%2FbtsyLl4w4AZ%2Fur4CKv3oWQjTwb0TKlGZKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;475&quot; height=&quot;337&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Compact&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Sweep단계의 그림처럼 메모리에 연속적이지 않은 공간을 차지하고 있다면, 배열과 같이 연속된 객체를 만들 수 없을 것이다. 따라서 &lt;b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Compact&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span&gt;단계를 수행하여, 연속된 공간을 확보한다.&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;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt; Serial &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector (시리얼 콜렉터)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young 영역과 Old 영역이 연속적으로 처리되며, 싱글 스레드로 GC를 수행하는 Garbage Collector이다. 주로 작은 힙 크기를 가진 단일 스레드 애플리케이션에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 Java 버전에서 많이 사용되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Young 역역 : Mark - Copy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Old 영역 : Mark-Sweep-Compact&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young 영역은 대부분의 객체가 빠르게 사라지기 때문에, 살아있는 객체를 직접 복사하고 영역을 Clear 하는 방식으로 작동한다. 이런 방식이 Copy 단계이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt; Parallel &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector (병렬 콜렉터)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병렬 콜렉터는 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;throughput collector(처리량)으로도 알려진 방식이다. 이 방식의 목표는 여러 스레드를 동시에 사용해서, 다중 CPU 환경에서의 애플리케이션 처리량을 높이는 것에 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 5, 6에서 주로 사용되었다.&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Young 역역 : Mark - Copy&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Old 영역 : Mark-Sweep-Compact&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young 영역은 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Serial&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector와 방식은 동일하지만, 복사 작업이 병렬로 처리된다.&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Old 영역은 &lt;span style=&quot;color: #000000;&quot;&gt;Serial&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector와 동일한 방식으로 진행된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Parallel &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Compacting&amp;nbsp;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector (병렬 콤팩팅 콜렉터)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병렬 콜렉터의 Old영역을 GC 할 때, Compact 단계도 멀티 스레드로 수행하는 Collector이다.&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Young 역역 : Mark - Copy&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Old 영역 : Mark-Sweep-Compact&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt; CMS(&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Concurrent Mark Sweep&lt;/span&gt;) &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector (CMS 콜렉터)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Old 영역에 대한 GC를 최적화하기 위해 설계된 Collector이다. 주 목표는 애플리케이션의 일시 중단 시간을 최소화하는 것이다. 그렇기 때문에 상당한 중지 시간을 갖는 Compaction 단계를 수행하지 않고 응답시간을 최소화한다. 하지만 이는 메모리 단편화 문제가 초래하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CMS가 완전히 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Compaction을 수행하지 않는다는 것은 아니다. 상황에 따라 Full GC가 발생할 수 있고, 이때 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Compaction&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;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;CMS GC와 Parallel GC는 Compaction 작업 유무로 구분될 수 있다.&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;Java 5부터 도입되었으나, &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Parallel GC에 비해 선호되지 않았다.&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Young 역역 : &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;ParNew&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Old 영역 : &lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Concurrent Mark-Sweep&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;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Concurrent Mark-Sweep(CMS)는 애플리케이션 중단 시간을 줄이기 위해, 아래와 같은 단계로 이루어진다.&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;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Initial Mark (초기 표시): GC roots에서 직접 액세스 가능한 객체를 표시하는 STW 단계&lt;/li&gt;
&lt;li&gt;Concurrent Mark (병렬 표시): 이 단계에서는 애플리케이션 스레드와 병행하여 살아있는 객체를 표시&lt;/li&gt;
&lt;li&gt;Precleaning: 변경된 객체를 다시 검사하는 단계&lt;/li&gt;
&lt;li&gt;Final Remark (최종 표시): 병렬 표시 중에 변경된 모든 객체를 표시하는 STW 단계&lt;/li&gt;
&lt;li&gt;Concurrent Sweep (병렬 정리): 표시되지 않은 (즉, dead) 객체를 정리하는 병렬 단계&lt;/li&gt;
&lt;li&gt;Concurrent Reset (병렬 재설정): 내부 데이터 구조를 재설정하고 다음 CMS 사이클을 준비&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;G1( &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Garbage First&lt;/span&gt; )&lt;/b&gt; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Collector&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CMS GC를 대체하기 위해 새롭게 등장한 Collector이다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대용량의 메모리가 있는 멀티 프로세서 시스템을 위해 제작되었고, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CMS GC보다 효율적으로 동시에 Application과 GC를 진행할 수 있다, 또한, 메모리 Compaction 과정까지 지원한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java 9 버전부터 기본 GC 방식으로 채택되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;왜 이름이 G1일까?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;G1 GC의 G1은 (Garbage-First)의 약자로, 가장 많은 가비지가 있는 영역부터 수집하는 핵심 전략을 갖고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 하면 가장 효율적으로 메모리를 재활용할 수 있으며, 주어진 일시 중지 시간(pause time) 목표 내에서 가능한 한 최대의 메모리를 회수하는 것이 가능해진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;G1은 어떤 안정성 문제가 있었을까?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;G1은 Java 7에서 처음으로 사용 가능하게 되었는데, 당시에 더 오래된 Parallel GC나 CMS GC&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;와 같은 GC들에 비해 완전히 검증되지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;초기 버전의 G1 GC는 일부 케이스에서 예상치 못한 행동이나 성능 문제를 일으킬 수 있었다.&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;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어떤 케이스였을까?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. G1 GC의 주요 목표 중 하나는 일시 중지 시간을 줄이는 것이지만, 초기 버전에서는 일부 상황에서 예상보다 긴 일시 중지 시간이 발생했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. G1은 메모리 조각화 문제를 해결하기 위해 설계되었지만, 초기 버전에서는 여전히 일부 상황에서 조각화 문제가 발생했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 일부 애플리케이션에서는 G1 GC를 사용할 때 전반적인 성능 저하가 발생했으며, 이는 CMS GC나 Parallel GC와 비교했을 때 특히 두드러졌다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 문제점들은 시간이 지나면서 Java 업데이트와 함께 개선되었고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;G1 GC는 현재 많은 환경에서 안정적으로 사용되고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;G1은 어떻게 기본 GC가 될 수 있었을까?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;G1이 기본 GC로 채택된 것에는 여러 가지 중요 요인들이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예측 가능한 일시 중지 시간: &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;G1 GC는 일시 중지 시간 목표 (예: -XX:MaxGCPauseMillis)를 설정할 수 있게 설계되었다. 이를 통해 개발자들은 일시 중지 시간을 예측 가능한 범위로 제한할 수 있다. 이러한 특징은 실시간성을 요구하는 애플리케이션에서 특히 중요하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대규모 힙 지원&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CMS GC의 한계점: &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메모리 조각화와 전체 GC로 인한 긴 일시 중지 시간문제 등의 한계점을 해결하기 위해 G1 이 등장했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현대 애플리케이션의 요구 사항 : &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대규모 메모리, 실시간 요구 사항 및 효율성을 중시하는 현대 애플리케이션의 요구 사항을 충족시키기 위해 G1 GC가 적합하다고 판단된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/68</guid>
      <comments>https://olrlobt.tistory.com/68#entry68comment</comments>
      <pubDate>Thu, 19 Oct 2023 17:08:50 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 카카오 지도 / 시도 시군구 행정구역 폴리곤으로 구분하기</title>
      <link>https://olrlobt.tistory.com/67</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;nbsp;시도&amp;nbsp;시군구&amp;nbsp;행정구역&amp;nbsp;폴리곤으로&amp;nbsp;구분하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오 맵 API에서는 지도 위에 원, 선, 다각형 등을 표시하고 이벤트를 넣을 수 있는데,&lt;/p&gt;
&lt;p style=&quot;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;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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;카&lt;/b&gt;&lt;b&gt;카오 지도 API 다각형 그리기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오 지도 API에서는 아래와 같은 방법으로 다각형을 그릴 수 있다.&amp;nbsp;&lt;/p&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://apis.map.kakao.com/web/sample/drawShape/&quot;&gt;https://apis.map.kakao.com/web/sample/drawShape/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;haxe&quot; style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;code&gt;// 다각형을 구성하는 좌표 배열입니다. 이 좌표들을 이어서 다각형을 표시합니다
var polygonPath = [
    new kakao.maps.LatLng(33.45133510810506, 126.57159381623066),
    new kakao.maps.LatLng(33.44955812811862, 126.5713551811832),
    new kakao.maps.LatLng(33.449986291544086, 126.57263296172184),
    new kakao.maps.LatLng(33.450682513554554, 126.57321034054742),
    new kakao.maps.LatLng(33.451346760004206, 126.57235740081413) 
];

// 지도에 표시할 다각형을 생성합니다
var polygon = new kakao.maps.Polygon({
    path:polygonPath, // 그려질 다각형의 좌표 배열입니다
    strokeWeight: 3, // 선의 두께입니다
    strokeColor: '#39DE2A', // 선의 색깔입니다
    strokeOpacity: 0.8, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다
    strokeStyle: 'longdash', // 선의 스타일입니다
    fillColor: '#A2FF99', // 채우기 색깔입니다
    fillOpacity: 0.7 // 채우기 불투명도 입니다
});

// 지도에 다각형을 표시합니다
polygon.setMap(map);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 이용해서, 아래와 같이 행정 구역을 다각형으로 나누어 줄 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5kOYH/btst0wbnyqM/zbiGKM8DcpKfYGVzvADu7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5kOYH/btst0wbnyqM/zbiGKM8DcpKfYGVzvADu7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5kOYH/btst0wbnyqM/zbiGKM8DcpKfYGVzvADu7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5kOYH%2Fbtst0wbnyqM%2FzbiGKM8DcpKfYGVzvADu7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;223&quot; height=&quot;317&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;대한민국 행정구역 다운로드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.gisdeveloper.co.kr/?p=2332&quot;&gt;http://www.gisdeveloper.co.kr/?p=2332&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1694669753165&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;대한민국 최신 행정구역(SHP) 다운로드 &amp;ndash; GIS Developer&quot; data-og-description=&quot;&quot; data-og-host=&quot;www.gisdeveloper.co.kr&quot; data-og-source-url=&quot;http://www.gisdeveloper.co.kr/?p=2332&quot; data-og-url=&quot;http://www.gisdeveloper.co.kr/?p=2332&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Lvgd3/hyTSzO1TWv/qDhfVQUicRybJC3xZ4psBk/img.png?width=1184&amp;amp;height=1125&amp;amp;face=0_0_1184_1125,https://scrap.kakaocdn.net/dn/dJCb5C/hyTVYNbf6c/1nQGkwLhnPkXn349n8qsF1/img.png?width=1184&amp;amp;height=1125&amp;amp;face=0_0_1184_1125,https://scrap.kakaocdn.net/dn/JBwah/hyTSomqFf7/Y9XfVXhSmQcsBLFBnN7uSK/img.png?width=1184&amp;amp;height=1125&amp;amp;face=0_0_1184_1125&quot;&gt;&lt;a href=&quot;http://www.gisdeveloper.co.kr/?p=2332&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.gisdeveloper.co.kr/?p=2332&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Lvgd3/hyTSzO1TWv/qDhfVQUicRybJC3xZ4psBk/img.png?width=1184&amp;amp;height=1125&amp;amp;face=0_0_1184_1125,https://scrap.kakaocdn.net/dn/dJCb5C/hyTVYNbf6c/1nQGkwLhnPkXn349n8qsF1/img.png?width=1184&amp;amp;height=1125&amp;amp;face=0_0_1184_1125,https://scrap.kakaocdn.net/dn/JBwah/hyTSomqFf7/Y9XfVXhSmQcsBLFBnN7uSK/img.png?width=1184&amp;amp;height=1125&amp;amp;face=0_0_1184_1125');&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;대한민국 최신 행정구역(SHP) 다운로드 &amp;ndash; GIS Developer&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;www.gisdeveloper.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;먼저 위 링크에서 행정구역을 나누기 위한 모델링 파일을 다운로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 다운로드한 파일들은. shp 등의 파일로, 사용하기 위해서는 Json 변환이 필요하다.&lt;/p&gt;
&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;&lt;b&gt;시/도 :&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/bwcb73/btstXzmwEJx/d27uWYbvq9rcZDBiqbj661/sido.json?attach=1&amp;amp;knm=tfile.json&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;sido.json&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.39MB&lt;/div&gt;
&lt;/div&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;b&gt;시/군/구 :&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/61WmZ/btstVh0A3Jz/i1bvJwNeXkGKKJkkyMxdE1/sig.json?attach=1&amp;amp;knm=tfile.json&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;sig.json&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;1.34MB&lt;/div&gt;
&lt;/div&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;폴리곤 간소화와 JSON 변경&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mapshaper.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mapshaper.org/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694674936303&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;mapshaper&quot; data-og-description=&quot;Drop or paste files here or select from a folder Shapefile, GeoJSON, TopoJSON, KML and CSV files are supported Files can be gzipped or in a zip archive Quick import Drop or paste files here to import with default settings&quot; data-og-host=&quot;mapshaper.org&quot; data-og-source-url=&quot;https://mapshaper.org/&quot; data-og-url=&quot;https://mapshaper.org/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://mapshaper.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mapshaper.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;mapshaper&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Drop or paste files here or select from a folder Shapefile, GeoJSON, TopoJSON, KML and CSV files are supported Files can be gzipped or in a zip archive Quick import Drop or paste files here to import with default settings&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mapshaper.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대한민국 행정구역의 경계는 일직선이 아니기 때문에 매우 복잡한 다각형으로 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 용량도 꽤나 크고, 다각형의 각의 개수도 많기 때문에 카카오 맵 API에서 폴리곤을 Load 할 때까지의 시간이 오래 걸리게 된다.&lt;/p&gt;
&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;위 사이트를 이용하여 폴리곤을 간소화하고 Json으로 추출해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사이트에 접속하면 아래와 같은 화면이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qdYGz/btstS8bQAf7/oiKh3IlbhEKHEo7kHxHAv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qdYGz/btstS8bQAf7/oiKh3IlbhEKHEo7kHxHAv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qdYGz/btstS8bQAf7/oiKh3IlbhEKHEo7kHxHAv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqdYGz%2FbtstS8bQAf7%2FoiKh3IlbhEKHEo7kHxHAv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;657&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;657&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;269&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba0snR/btstXxhSdCI/VIkhu7mKHU1dIIkJWPLWw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba0snR/btstXxhSdCI/VIkhu7mKHU1dIIkJWPLWw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba0snR/btstXxhSdCI/VIkhu7mKHU1dIIkJWPLWw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba0snR%2FbtstXxhSdCI%2FVIkhu7mKHU1dIIkJWPLWw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;252&quot; height=&quot;144&quot; data-origin-width=&quot;269&quot; data-origin-height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 앞선 홈페이지에서 다운로드한. shp 파일을 로드한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqchEi/btstY5ZtVKC/KQyLH6sD4P1VxniFt0fIKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqchEi/btstY5ZtVKC/KQyLH6sD4P1VxniFt0fIKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqchEi/btstY5ZtVKC/KQyLH6sD4P1VxniFt0fIKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqchEi%2FbtstY5ZtVKC%2FKQyLH6sD4P1VxniFt0fIKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;327&quot; height=&quot;199&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;199&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Import를 해 주면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;659&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6OYIR/btst6l1l5FP/8WlWhVrzyaIeoNklCmQTQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6OYIR/btst6l1l5FP/8WlWhVrzyaIeoNklCmQTQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6OYIR/btst6l1l5FP/8WlWhVrzyaIeoNklCmQTQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6OYIR%2Fbtst6l1l5FP%2F8WlWhVrzyaIeoNklCmQTQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;659&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;659&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;여기서 폴리곤을 간소화하기 위해, 우측 상단의 Simplify를 누른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;753&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5x1lE/btstXB5PtVM/uMj6idbxUIFu3GEjJJ650K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5x1lE/btstXB5PtVM/uMj6idbxUIFu3GEjJJ650K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5x1lE/btstXB5PtVM/uMj6idbxUIFu3GEjJJ650K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5x1lE%2FbtstXB5PtVM%2FuMj6idbxUIFu3GEjJJ650K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;753&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;753&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 위와 같은 설정 창이 하나 뜬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: start;&quot;&gt;Prevent shape removal : 폴리곤을 간소화하면서, 완전히 삭제되지는 않게 하는 설정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #374151;&quot;&gt;&lt;span style=&quot;background-color: #f7f7f8;&quot;&gt;Method : 폴리곤을 간소화하는 알고리즘을 선택한다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;42&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5mGh0/btstY4zxEpN/usYL9ESLwtSHQsedoCaTiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5mGh0/btstY4zxEpN/usYL9ESLwtSHQsedoCaTiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5mGh0/btstY4zxEpN/usYL9ESLwtSHQsedoCaTiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5mGh0%2FbtstY4zxEpN%2FusYL9ESLwtSHQsedoCaTiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;42&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;42&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;&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;1132&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJUf8h/btst0zsyjP5/dKqprjX6kuqg5MsXj5ptg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJUf8h/btst0zsyjP5/dKqprjX6kuqg5MsXj5ptg1/img.png&quot; data-alt=&quot;(좌) 100% / (우) 1.3%&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJUf8h/btst0zsyjP5/dKqprjX6kuqg5MsXj5ptg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJUf8h%2Fbtst0zsyjP5%2FdKqprjX6kuqg5MsXj5ptg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1132&quot; height=&quot;599&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(좌) 100% / (우) 1.3%&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 높은 수치를 사용하면, 용량과 점의 개수가 많아 로딩 시간이 오래 걸리게 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 낮은 수치를 사용하면, 생략되어 사라져 버리는 지점이 있기 때문에 적당한 지점을 찾아서 설정해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 15% ~ 5%가 사라지는 지점 없이 적당하다고 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRXEgT/btst6p3OToC/PHB7nZKqe4mNsq6hD8Z44K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRXEgT/btst6p3OToC/PHB7nZKqe4mNsq6hD8Z44K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRXEgT/btst6p3OToC/PHB7nZKqe4mNsq6hD8Z44K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRXEgT%2Fbtst6p3OToC%2FPHB7nZKqe4mNsq6hD8Z44K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;487&quot; height=&quot;396&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간소화를 마쳤다면, Export를 눌러 Json 형식으로 추출해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ds4QSu/btst5jQtonn/QMQr93mwu9myHJ6UZlJcO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ds4QSu/btst5jQtonn/QMQr93mwu9myHJ6UZlJcO0/img.png&quot; data-alt=&quot;json으로 변환된 모델링 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ds4QSu/btst5jQtonn/QMQr93mwu9myHJ6UZlJcO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fds4QSu%2Fbtst5jQtonn%2FQMQr93mwu9myHJ6UZlJcO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;833&quot; height=&quot;586&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;json으로 변환된 모델링 파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1694674230091&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let mapContainer = document.getElementById('map'), // 지도를 표시할 div
        mapOption = {
            center: new kakao.maps.LatLng(37.566826, 126.9786567), // 지도의 중심좌표
            level: 12 // 지도의 확대 레벨
        };

    let map = new kakao.maps.Map(mapContainer, mapOption),
        customOverlay = new kakao.maps.CustomOverlay({})

    let detailMode = false; // level에 따라 다른 json 파일 사용
    let level = '';
    let polygons = [];

    init(&quot;json/sido.json&quot;) // 초기 시작

    kakao.maps.event.addListener(map, 'zoom_changed', function () {
        level = map.getLevel()
        if (!detailMode &amp;amp;&amp;amp; level &amp;lt;= 10) { // level 에 따라 다른 json 파일을 사용한다.
            detailMode = true;
            removePolygon();
            init(&quot;json/sig.json&quot;)
        } else if (detailMode &amp;amp;&amp;amp; level &amp;gt; 10) { // level 에 따라 다른 json 파일을 사용한다.
            detailMode = false;
            removePolygon();
            init(&quot;json/sido.json&quot;)
        }
    });

  	// 모든 폴리곤을 지우는 함수
    function removePolygon() { 
        for (let i = 0; i &amp;lt; polygons.length; i++) {
            polygons[i].setMap(null);
        }
        areas = [];
        polygons = [];
    }

	// 폴리곤 생성
    function init(path) {

		//path 경로의 json 파일 파싱
        $.getJSON(path, function (geojson) {
            var units = geojson.features; // json key값이 &quot;features&quot;인 것의 value를 통으로 가져온다.

            $.each(units, function (index, unit) { // 1개 지역씩 꺼내서 사용. val은 그 1개 지역에 대한 정보를 담는다
                var coordinates = []; //좌표 저장할 배열
                var name = ''; // 지역 이름
                var cd_location = '';
                coordinates = unit.geometry.coordinates; // 1개 지역의 영역을 구성하는 다각형의 모든 좌표 배열
                name = unit.properties.SIG_KOR_NM; // 1개 지역의 이름
                cd_location = unit.properties.SIG_CD;


                var ob = new Object();
                ob.name = name;
                ob.path = [];
                ob.location = cd_location;
                $.each(coordinates[0], function (index, coordinate) { 
                    ob.path
                        .push(new kakao.maps.LatLng(coordinate[1],
                            coordinate[0]));
                });

                areas[index] = ob;
            });//each
        });//getJSON

		// 지도에 영역데이터를 폴리곤으로 표시
        for (var i = 0, len = areas.length; i &amp;lt; len; i++) {
            displayArea(areas[i]);
        }

        function displayArea(area) {

            var polygon = new kakao.maps.Polygon({
                map: map,
                path: area.path,
                strokeWeight: 2,
                strokeColor: '#004c80',
                strokeOpacity: 0.8,
                fillColor: '#fff',
                fillOpacity: 0.7
            });
            polygons.push(polygon);

            kakao.maps.event.addListener(polygon, 'mouseover', function (mouseEvent) {
                polygon.setOptions({fillColor: '#09f'});
                customOverlay.setContent('&amp;lt;div class=&quot;area&quot;&amp;gt;' + area.name + '&amp;lt;/div&amp;gt;');
                customOverlay.setPosition(mouseEvent.latLng);
                customOverlay.setMap(map);
            });

            kakao.maps.event.addListener(polygon, 'mousemove', function (mouseEvent) {

                customOverlay.setPosition(mouseEvent.latLng);
            });

            kakao.maps.event.addListener(polygon, 'mouseout', function () {
                polygon.setOptions({fillColor: '#fff'});
                customOverlay.setMap(null);
            });

            kakao.maps.event.addListener(polygon, 'click', function (mouseEvent) {
                if (!detailMode) {
                    map.setLevel(10); // level에 따라 이벤트 변경
                    var latlng = mouseEvent.latLng;

                    // 지도의 중심을 부드럽게 클릭한 위치로 이동시킵니다.
                    map.panTo(latlng);
                } else {
					// 클릭 이벤트 함수
                    // callFunctionWithRegionCode(area.location);
                }
            });
        }
    }&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;자세한 설명은 생략하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (9).gif&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKBcEd/btst5PPc11L/RN0PodlJVa6ZmPiqnPRb3K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKBcEd/btst5PPc11L/RN0PodlJVa6ZmPiqnPRb3K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKBcEd/btst5PPc11L/RN0PodlJVa6ZmPiqnPRb3K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dKBcEd/btst5PPc11L/RN0PodlJVa6ZmPiqnPRb3K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;344&quot; data-filename=&quot;ezgif.com-crop (9).gif&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;시/도에서 클릭하게 되면, 시/군/구로 이루어진 폴리곤이 나오게 확대되고,&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;</description>
      <category>Spring/Maps</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/67</guid>
      <comments>https://olrlobt.tistory.com/67#entry67comment</comments>
      <pubDate>Thu, 14 Sep 2023 16:51:51 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 블록킹큐(Blocking Queue)와 딜레이 큐(Delay Queue)</title>
      <link>https://olrlobt.tistory.com/66</link>
      <description>&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Queue&lt;/code&gt;를 사용해 오면서 구현체를 만들 때 &lt;code&gt;new&lt;/code&gt;를 치면 자동 완성 되는 수많은 구현체들을 보면서도, 아무 관심도 주지 않고 내가 쓰는 구현체만 써 왔었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp;&quot;개발자가 반드시 알아야 할 자바 성능 튜닝 이야기&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 읽으며, 그리고 북스터디에서 해당 내용을 이슈로 다루며 &lt;code&gt;Queue&lt;/code&gt;에는 어떤 구현체가 있고 어디에 사용되는 지를 알아보게 되었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Blocking Queue&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queue에 대한 개념은 생략하고 바로 &lt;code&gt;Blocking Queue&lt;/code&gt;로 넘어가겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;BlockingQueue&lt;/code&gt;는 &lt;code&gt;java.util.concurrent&lt;/code&gt; 패키지에 포함된, 구현체가 아닌 인터페이스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/SSARTEL-10th/JPTS_bookstudy/assets/99643732/e60e1c14-9dd4-44f1-8a9a-5a05a80f6aa5&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Blocking Queue&lt;/code&gt;는 동시성 프로그래밍에서 사용되는 스레드 안전한 큐이다.&lt;br /&gt;큐의 기본 작업에 &lt;code&gt;블로킹 연산&lt;/code&gt;을 추가하여, 큐가 가득 찼을 때나 항목을 추가하려는 스레드나,&lt;br /&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;블로킹 연산&lt;/b&gt;이란?&lt;br /&gt;&lt;br /&gt;특정 조건이 충족될 때까지 스레드를 일시 중지시키는 연산으로, 연산이 완료될 때까지 스레드를 대기 상태로 만든다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Blocking Queue의 특징, 일반 Queue와의 차이&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;스레드 안전:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;BlockingQueue&lt;/code&gt;는 내부적으로 동기화되어 있어 여러 스레드에서 동시에 접근해도 안전하다.&lt;/li&gt;
&lt;li&gt;일반 Queue는 동시성을 지원하지 않는 경우도 많다. 예로, &lt;code&gt;LinkedList&lt;/code&gt;나, &lt;code&gt;PriorityQueue&lt;/code&gt;의 경우 여러 스레드에서 동시에 접근하면 데이터 일관성이 깨질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;블로킹 연산:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;큐가 가득 찼을 때의 &lt;code&gt;put&lt;/code&gt; 연산과 큐가 비었을 때의 &lt;code&gt;take&lt;/code&gt; 연산이 블로킹된다. 이러한 연산들은 특정 조건이 충족될 때까지 스레드를 대기(Block)시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간제한 있는 연산:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;offer(E e, long timeout, TimeUnit unit)&lt;/code&gt; 및 &lt;code&gt;poll(long timeout, TimeUnit unit)&lt;/code&gt;로 대기 시간을 설정할 수 있다. 지정된 시간 내에 연산이 완료되지 않으면 타임아웃과 함께 실패한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BlockingQueue의 구현체&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;ArrayBlockingQueue&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고정 크기의 배열을 기반으로 한 구현. 크기가 일단 설정되면 변경할 수 없고, 구현 시 크기를 지정해 주어야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;LinkedBlockingQueue&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결 노드를 기반으로 한 구현. 선택적으로 최대 크기를 설정할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;PriorityBlockingQueue&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소를 우선순위에 따라 저장하는 구현.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;SynchronousQueue&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단 하나의 항목만 저장할 수 있는 블로킹 큐. 이 큐에 항목을 넣으면 다른 스레드가 그 항목을 꺼낼 때까지 현재 스레드는 대기(블록)한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java Code Example&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class BlockingQueueExample {

    public static void main(String[] args) {
        BlockingQueue&amp;lt;Integer&amp;gt; queue = new LinkedBlockingQueue&amp;lt;&amp;gt;(5);  // 큐의 최대 크기는 5

        // 프로듀서 스레드
        Thread producer = new Thread(() -&amp;gt; {
            int value = 0;
            while (true) {
                try {
                    queue.put(value);
                    System.out.println(&quot;Produced &quot; + value);
                    value++;
                    Thread.sleep(1000);  // 1초에 한 번 PUT
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 컨슈머 스레드
        Thread consumer = new Thread(() -&amp;gt; {
            while (true) {
                try {
                    int value = queue.take();
                    System.out.println(&quot;Consumed &quot; + value);
                    Thread.sleep(1500);  // 1.5초에 한 번 TAKE
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        producer.start();
        consumer.start();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Producer 스레드는 1초당 1회 값을 넣고.&lt;br /&gt;Consumer 스레드 1.5초당 1회 값을 소비한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BlockingQueue&lt;/code&gt;의 크기를 5로 설정하고 실행한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크기가 5가 다 찰 때까지는 우리가 아는 일반 &lt;code&gt;Queue&lt;/code&gt;와 똑같이 실행될 것이다.&lt;br /&gt;크기가 다 찬 순간부터는 앞서 본 개념대로라면, &lt;code&gt;Consumer&lt;/code&gt; 가 실행되어야만 &lt;code&gt;Producer&lt;/code&gt;가 실행될 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 결과&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;... 생략 ...
Consumed 4
Produced 6
Produced 7
Consumed 5
Produced 8
Consumed 6
Produced 9  // 아직 5까지 안 차서
Produced 10  // Produced가 두 번 실행 된다.

... 중략 ...
Consumed 9  // 일정 시간 이후부터는 size = 5
Produced 14 // Consumed 해야만 Produced 된다.
Consumed 10
Produced 15
Consumed 11
Produced 16
Consumed 12
Produced 17
Consumed 13
Produced 18&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 개념처럼 사이즈가 다 찰 때까지, 대기 없이 동작하다가 사이즈가 가득 찬 시점 이후부터는 &lt;code&gt;Consumed&lt;/code&gt;가 실행되어야 &lt;code&gt;Produced&lt;/code&gt;가 실행되는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;DelayQueue&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DelayQueue는 java.util.concurrent패키지에&lt;/code&gt; 포함된 동시성 유틸리티 중 하나로 &lt;code&gt;BlockingQueue&lt;/code&gt;의 구현체이다.&lt;br /&gt;요소가 지정된 지연 시간이 지날 때까지 가져올 수 없으며,&lt;br /&gt;이는 스케쥴링 또는 재시도와 같은 연산에서 유용하게 사용될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DelayQueue 특징&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;DelayQueue&lt;/code&gt;의 요소는 &lt;code&gt;Delayed&lt;/code&gt; 인터페이스를 구현해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Delayed&lt;/code&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;Delayed&lt;/b&gt;?&lt;br /&gt;&lt;br /&gt;DelayQueue에서 얼마나 Delay 될지는 Delayed 인터페이스의 getDelay() 메서드를 통해서 제공된다.&lt;br /&gt;따라서, Delayed 인터페이스를 구현하지 않으면 컴파일 에러가 발생한다.&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;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;내부적으로 PriorityQueue를 사용하여, 지연시간에 따라 자동으로 정렬된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/SSARTEL-10th/JPTS_bookstudy/assets/99643732/33331a71-582d-4e04-bac4-b445c32f6776&quot; alt=&quot;img_1&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java Code Example&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class DelayQueueExample {

    static class DelayedTask implements Delayed {
        private long delayUntil;
        private String taskName;

        public DelayedTask(String taskName, long delayInMillis) {
            this.taskName = taskName;
            this.delayUntil = System.currentTimeMillis() + delayInMillis;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(delayUntil - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed o) {
            return Long.compare(this.delayUntil, ((DelayedTask) o).delayUntil);
        }

        @Override
        public String toString() {
            return taskName;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        DelayQueue&amp;lt;DelayedTask&amp;gt; delayQueue = new DelayQueue&amp;lt;&amp;gt;();
        delayQueue.add(new DelayedTask(&quot;Task1&quot;, 5000)); // 5초 후 실행
        delayQueue.add(new DelayedTask(&quot;Task2&quot;, 2000)); // 2초 후 실행

        long startTime = System.currentTimeMillis();
        System.out.println(&quot;Start: &quot; + startTime);

        while (!delayQueue.isEmpty()) {
            DelayedTask task = delayQueue.take(); // 지연이 만료될 때까지 블로킹
            System.out.println(&quot;Executed &quot; + task + &quot; at &quot; + (System.currentTimeMillis() - startTime));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Start: 1694526619763
Executed Task2 at 2008 // 실행 2초 후 발생
Executed Task1 at 5003 // 실해 5초 후 발생&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DelayQueue&lt;/code&gt;는 지연시간에 따라 자동 정렬되기 때문에, 2초의 지연 시간을 갖는 Task 2가 먼저 실행이 되었다.&lt;br /&gt;이후 3초 후에 Task1이 실행된 것을 확인하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, &lt;code&gt;DelayQueue&lt;/code&gt;는 &lt;code&gt;BlockingQueue&lt;/code&gt;의 구현체이므로, 아래처럼 바꾸어 주어도 무방하다.&lt;br /&gt;Java에서 &lt;code&gt;Colletion&lt;/code&gt; 계열은 인터페이스로 선언을 해 주는 것이 유지보수 관점에서 좋다니,&lt;br /&gt;아래 방법을 더 권장한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static void main(String[] args) throws InterruptedException {
//        DelayQueue&amp;lt;DelayedTask&amp;gt; delayQueue = new DelayQueue&amp;lt;&amp;gt;();
        BlockingQueue&amp;lt;DelayedTask&amp;gt; blockingQueue = new DelayQueue&amp;lt;&amp;gt;();
        blockingQueue.add(new DelayedTask(&quot;Task1&quot;, 5000)); // 5초 후 실행
        blockingQueue.add(new DelayedTask(&quot;Task2&quot;, 2000)); // 2초 후 실행

        long startTime = System.currentTimeMillis();
        System.out.println(&quot;Start: &quot; + startTime);

        while (!blockingQueue.isEmpty()) {
            DelayedTask task = blockingQueue.take(); // 지연이 만료될 때까지 블로킹
            System.out.println(&quot;Executed &quot; + task + &quot; at &quot; + (System.currentTimeMillis() - startTime));
        }
    }&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Java/자료구조</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/66</guid>
      <comments>https://olrlobt.tistory.com/66#entry66comment</comments>
      <pubDate>Wed, 13 Sep 2023 08:48:22 +0900</pubDate>
    </item>
    <item>
      <title>[MatterMost] Webhooks와 Bot으로 메세지 보내기</title>
      <link>https://olrlobt.tistory.com/64</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/63&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://olrlobt.tistory.com/63&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1692189185370&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;[MatterMost] 매터모스트 개인 테스트 서버(Preview server) 열기&quot; data-og-description=&quot;매터모스트 MatterMost 매터모스트(Mattermost)는 오픈 소스 메시징 플랫폼으로, 팀들이 소통할 수 있는 자체 호스팅 방식의 채팅 서비스이다. Slack이나 Microsoft Teams과 같은 서비스와 비슷하게 작동하&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/63&quot; data-og-url=&quot;https://olrlobt.tistory.com/63&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bYiYbc/hyTCyQifPC/SkU81VKvlpESkDekvKOYg0/img.png?width=800&amp;amp;height=425&amp;amp;face=0_0_800_425,https://scrap.kakaocdn.net/dn/9oiaE/hyTFlIxkpy/KUYEbVvFwqqJZ6Xs3KYSW0/img.png?width=800&amp;amp;height=425&amp;amp;face=0_0_800_425,https://scrap.kakaocdn.net/dn/bzQosc/hyTCFaN6Gg/wSEhA411tyXSJLmkNyi3Xk/img.png?width=1830&amp;amp;height=1353&amp;amp;face=0_0_1830_1353&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/63&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/63&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bYiYbc/hyTCyQifPC/SkU81VKvlpESkDekvKOYg0/img.png?width=800&amp;amp;height=425&amp;amp;face=0_0_800_425,https://scrap.kakaocdn.net/dn/9oiaE/hyTFlIxkpy/KUYEbVvFwqqJZ6Xs3KYSW0/img.png?width=800&amp;amp;height=425&amp;amp;face=0_0_800_425,https://scrap.kakaocdn.net/dn/bzQosc/hyTCFaN6Gg/wSEhA411tyXSJLmkNyi3Xk/img.png?width=1830&amp;amp;height=1353&amp;amp;face=0_0_1830_1353');&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;[MatterMost] 매터모스트 개인 테스트 서버(Preview server) 열기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;매터모스트 MatterMost 매터모스트(Mattermost)는 오픈 소스 메시징 플랫폼으로, 팀들이 소통할 수 있는 자체 호스팅 방식의 채팅 서비스이다. Slack이나 Microsoft Teams과 같은 서비스와 비슷하게 작동하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;지난번, 매터모스트를 이용하여 Webhook과 Bot을 테스트하기 위하여 개인 Preview Server를 생성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 여기서 Webhook과 Bot으로 메시지 보내기를 진행해 보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후에는, 이미 만들어서 사용 중인 Webhooks를 어떻게 스터디에서 활용 중인지까지 포스팅하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Webhook으로 메시지 보내기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Webhook은 웹 애플리케이션에서 일정 이벤트가 발생하였을 때, 정의된 URL로 HTTP POST 요청을 자동으로 보내는 방식을 말한다. 이를 통해 다른 시스템이나 서비스에 알림을 전송할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #374151;&quot;&gt;나는 이 Webhook과 Bot을 테스트해 보고, 알맞은 기능을 사용할 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 컨테이너 실행&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d97KEz/btsrf5oHHon/l1Tesy60LtcxBRGGdEAKBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d97KEz/btsrf5oHHon/l1Tesy60LtcxBRGGdEAKBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d97KEz/btsrf5oHHon/l1Tesy60LtcxBRGGdEAKBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd97KEz%2Fbtsrf5oHHon%2Fl1Tesy60LtcxBRGGdEAKBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;595&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;595&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 도커에서 다운로드하여 놓았던 컨테이너를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 실행이 된 상태여야 개인 Preview 서버에 접속이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;1192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o1oHr/btsrhEw446R/u9ODr8NNl5LsibmucAnCIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o1oHr/btsrhEw446R/u9ODr8NNl5LsibmucAnCIk/img.png&quot; data-alt=&quot;개인 Preivew Server 홈 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o1oHr/btsrhEw446R/u9ODr8NNl5LsibmucAnCIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo1oHr%2FbtsrhEw446R%2Fu9ODr8NNl5LsibmucAnCIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;737&quot; height=&quot;481&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;1192&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개인 Preivew Server 홈 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Webhook 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gn3XG/btsrhG2J72P/yUdnK187xKc6DnF9z7okEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gn3XG/btsrhG2J72P/yUdnK187xKc6DnF9z7okEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gn3XG/btsrhG2J72P/yUdnK187xKc6DnF9z7okEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGn3XG%2FbtsrhG2J72P%2FyUdnK187xKc6DnF9z7okEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;334&quot; height=&quot;393&quot; data-origin-width=&quot;455&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;상단 Channels &amp;gt; Integrations에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;1067&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxUG54/btsrhEw46FV/k1KElkvBSAxX07OOgw6BRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxUG54/btsrhEw46FV/k1KElkvBSAxX07OOgw6BRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxUG54/btsrhEw46FV/k1KElkvBSAxX07OOgw6BRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxUG54%2FbtsrhEw46FV%2Fk1KElkvBSAxX07OOgw6BRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1818&quot; height=&quot;1067&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;1067&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Webhook의 경우 Incoming Webhooks를 통해 http 요청을 받을 링크 생성을,&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bot의 경우 Bot Accounts를 통해 봇을 생성해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bot은 아래에서 보도록 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Incoming Webhooks를 누른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1815&quot; data-origin-height=&quot;1057&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dx3hHZ/btsroJYTF7r/tBhcLlWfKCjsL3SYAEcLq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dx3hHZ/btsroJYTF7r/tBhcLlWfKCjsL3SYAEcLq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dx3hHZ/btsroJYTF7r/tBhcLlWfKCjsL3SYAEcLq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdx3hHZ%2FbtsroJYTF7r%2FtBhcLlWfKCjsL3SYAEcLq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1815&quot; height=&quot;1057&quot; data-origin-width=&quot;1815&quot; data-origin-height=&quot;1057&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우측 상단의 Add Incoming Webhook을 누른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;952&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzvxY3/btsrqzBILWR/nh6m9nrdsqCCJxeDKE7kTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzvxY3/btsrqzBILWR/nh6m9nrdsqCCJxeDKE7kTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzvxY3/btsrqzBILWR/nh6m9nrdsqCCJxeDKE7kTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzvxY3%2FbtsrqzBILWR%2Fnh6m9nrdsqCCJxeDKE7kTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;749&quot; height=&quot;952&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Webhooks 사용 목적에 맞게 작성을 해 준 후, Save로 저장하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Channel은 사용 Webhook을 사용할 채널을 선택하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;마지막 Lock to this channel을 체크하면, 생성한 Webhook에 발생된 http 요청으로 해당 채널 접근만 가능하게 막을 수 있다. 웬만하면 꼭 체크해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br7FfC/btsrh3cBNVs/mksf2XIJNejRVnWLZQ1gg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br7FfC/btsrh3cBNVs/mksf2XIJNejRVnWLZQ1gg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br7FfC/btsrh3cBNVs/mksf2XIJNejRVnWLZQ1gg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr7FfC%2Fbtsrh3cBNVs%2Fmksf2XIJNejRVnWLZQ1gg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1334&quot; height=&quot;461&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성해 주면, 아래와 같이 작성한 정보에 따라, Webhooks가 정상적으로 생성되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YglDy/btsrqFooLo6/gPU1x9DmQpCzVeiu4Fg7jK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YglDy/btsrqFooLo6/gPU1x9DmQpCzVeiu4Fg7jK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YglDy/btsrqFooLo6/gPU1x9DmQpCzVeiu4Fg7jK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYglDy%2FbtsrqFooLo6%2FgPU1x9DmQpCzVeiu4Fg7jK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1362&quot; height=&quot;516&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 Webhooks URL에 Http 요청을 보내보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Webhooks Http 요청&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://developers.mattermost.com/integrate/webhooks/incoming/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developers.mattermost.com/integrate/webhooks/incoming/&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1692190566094&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;Incoming webhooks&quot; data-og-description=&quot;Edit on GitHub Incoming webhooks Create an incoming webhook&amp;nbsp; Let&amp;rsquo;s learn how to create a simple incoming webhook that posts the following message to Mattermost. In Mattermost, go to Product menu &amp;gt; Integrations &amp;gt; Incoming Webhook. If you don&amp;rsquo;t have the&quot; data-og-host=&quot;developers.mattermost.com&quot; data-og-source-url=&quot;https://developers.mattermost.com/integrate/webhooks/incoming/&quot; data-og-url=&quot;https://developers.mattermost.com/integrate/webhooks/incoming/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bwYJXN/hyTFdjqjZ5/IOPyKhDjnomHySdZLYyfG1/img.png?width=1023&amp;amp;height=617&amp;amp;face=0_0_1023_617,https://scrap.kakaocdn.net/dn/cH7jS8/hyTCz2I8MU/TsQxNmvoqP6QLtpt3ALTlK/img.png?width=1280&amp;amp;height=399&amp;amp;face=0_0_1280_399,https://scrap.kakaocdn.net/dn/dd3oa1/hyTCEJKQLf/YYfMRVSh1dZQyddpkuJjl1/img.png?width=572&amp;amp;height=212&amp;amp;face=0_0_572_212&quot;&gt;&lt;a href=&quot;https://developers.mattermost.com/integrate/webhooks/incoming/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.mattermost.com/integrate/webhooks/incoming/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bwYJXN/hyTFdjqjZ5/IOPyKhDjnomHySdZLYyfG1/img.png?width=1023&amp;amp;height=617&amp;amp;face=0_0_1023_617,https://scrap.kakaocdn.net/dn/cH7jS8/hyTCz2I8MU/TsQxNmvoqP6QLtpt3ALTlK/img.png?width=1280&amp;amp;height=399&amp;amp;face=0_0_1280_399,https://scrap.kakaocdn.net/dn/dd3oa1/hyTCEJKQLf/YYfMRVSh1dZQyddpkuJjl1/img.png?width=572&amp;amp;height=212&amp;amp;face=0_0_572_212');&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;Incoming webhooks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Edit on GitHub Incoming webhooks Create an incoming webhook&amp;nbsp; Let&amp;rsquo;s learn how to create a simple incoming webhook that posts the following message to Mattermost. In Mattermost, go to Product menu &amp;gt; Integrations &amp;gt; Incoming Webhook. If you don&amp;rsquo;t have the&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.mattermost.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;매터모스트 공식 홈페이지에서 Webhooks에 관한 문서를 찾아보면 아래와 같이 사용하라고 적혀있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터 형식도 자세하게 나와 있으니, 사용 목적에 맞게 참고하여 사용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2561&quot; data-origin-height=&quot;1206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqeo8X/btsrgUNMXRF/hZK6YkeJ3LmgQes2HGzkU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqeo8X/btsrgUNMXRF/hZK6YkeJ3LmgQes2HGzkU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqeo8X/btsrgUNMXRF/hZK6YkeJ3LmgQes2HGzkU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqeo8X%2FbtsrgUNMXRF%2FhZK6YkeJ3LmgQes2HGzkU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2561&quot; height=&quot;1206&quot; data-origin-width=&quot;2561&quot; data-origin-height=&quot;1206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은 메시지 전송에 필요한 파라미터는, text만 있으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Http 헤더는 Json 형식을 요청한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Webhooks가 정상적으로 생성됐는지 확인하기 위해 cmd에서 http 요청을 해보자,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. cmd curl 설치 확인&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wt1ms/btsrrbtOmoo/Cvr3cFOCWLKt5L4kXPfqdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wt1ms/btsrrbtOmoo/Cvr3cFOCWLKt5L4kXPfqdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wt1ms/btsrrbtOmoo/Cvr3cFOCWLKt5L4kXPfqdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWt1ms%2FbtsrrbtOmoo%2FCvr3cFOCWLKt5L4kXPfqdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1084&quot; height=&quot;133&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1692191224621&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl --version&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;위 명령어를 이용하여 Windows 환경에 curl이 설치되어 있는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 공식 홈페이지의 curl 코드를 복사해서 실행&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFnOmC/btsrrUyEwv6/Hf8DpNCJWeMYaKHf84W7Xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFnOmC/btsrrUyEwv6/Hf8DpNCJWeMYaKHf84W7Xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFnOmC/btsrrUyEwv6/Hf8DpNCJWeMYaKHf84W7Xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFnOmC%2FbtsrrUyEwv6%2FHf8DpNCJWeMYaKHf84W7Xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1128&quot; height=&quot;294&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1692191325659&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -i -X POST -H &quot;Content-Type: application/json&quot; -d &quot;{\&quot;text\&quot;: \&quot;Hello, this is some text\\nThis is more text. \&quot;}&quot; http://localhost:8065/hooks/&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;당연히 URL은 본인 Webhooks URL로 변경해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 cmd에서의 작은따옴표 대신 큰 따옴표를 사용하여야 하며, 문자열 내부의 &quot; 큰 따옴표의 경우는 escape 해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 \n과 같은 문자도 escape 해주고 전송해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 200 OK라는 값이 나왔다면 정상적으로 작동한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 정상작동 확인&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;727&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQLTSk/btsrfgKvt2z/9vfVaBrxueb9AqI599pj3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQLTSk/btsrfgKvt2z/9vfVaBrxueb9AqI599pj3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQLTSk/btsrfgKvt2z/9vfVaBrxueb9AqI599pj3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQLTSk%2FbtsrfgKvt2z%2F9vfVaBrxueb9AqI599pj3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;641&quot; height=&quot;456&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;727&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 생성한 계정의 이름으로, 이름 옆에 BOT이라는 라벨과 함께 메시지가 정상적으로 전송된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Bot으로 메시지 보내기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 내 계정이 아닌 봇으로 메시지를 보내보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 봇을 생성하기 위해선 채널에 봇 생성을 허용해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Bot 사용 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rH0gj/btsrraPc3Jj/I21qxEksObPWtn6UKJwVHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rH0gj/btsrraPc3Jj/I21qxEksObPWtn6UKJwVHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rH0gj/btsrraPc3Jj/I21qxEksObPWtn6UKJwVHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrH0gj%2FbtsrraPc3Jj%2FI21qxEksObPWtn6UKJwVHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;319&quot; height=&quot;383&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Channels를 누르고, System Console을 누른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1761&quot; data-origin-height=&quot;1084&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BDwfP/btsrkRbTvtp/1mFRkIjUUE9fPpyduqb56K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BDwfP/btsrkRbTvtp/1mFRkIjUUE9fPpyduqb56K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BDwfP/btsrkRbTvtp/1mFRkIjUUE9fPpyduqb56K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBDwfP%2FbtsrkRbTvtp%2F1mFRkIjUUE9fPpyduqb56K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1761&quot; height=&quot;1084&quot; data-origin-width=&quot;1761&quot; data-origin-height=&quot;1084&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측 탭에서 Bot Accounts를 찾아 상단 Enable을 true로 바꿔준 후, Save로 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Bot 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;1067&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxUG54/btsrhEw46FV/k1KElkvBSAxX07OOgw6BRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxUG54/btsrhEw46FV/k1KElkvBSAxX07OOgw6BRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxUG54/btsrhEw46FV/k1KElkvBSAxX07OOgw6BRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxUG54%2FbtsrhEw46FV%2Fk1KElkvBSAxX07OOgw6BRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1818&quot; height=&quot;1067&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;1067&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Channels &amp;gt; Integrations에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 선택했던 Webhooks가 아닌, Bot Accounts에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1065&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPA5qP/btsrgkMFnKJ/gsjgV7QvkwViGEn12uHUak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPA5qP/btsrgkMFnKJ/gsjgV7QvkwViGEn12uHUak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPA5qP/btsrgkMFnKJ/gsjgV7QvkwViGEn12uHUak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPA5qP%2FbtsrgkMFnKJ%2FgsjgV7QvkwViGEn12uHUak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1808&quot; height=&quot;1065&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1065&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add Bot Accounts를 눌러, Bot을 추가해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Webhooks과 진행 방식은 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;Create Bot Account를 눌러 봇을 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;1724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcouBa/btsrrQXjyAn/yR2Mw5mVnQkbBpBmBRRn81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcouBa/btsrrQXjyAn/yR2Mw5mVnQkbBpBmBRRn81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcouBa/btsrrQXjyAn/yR2Mw5mVnQkbBpBmBRRn81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcouBa%2FbtsrrQXjyAn%2FyR2Mw5mVnQkbBpBmBRRn81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;621&quot; height=&quot;775&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;1724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 가장 하단의 post:channels를 체크해 주어야, channels에 메시지를 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnVs7S/btsrkSaPyny/QRY9UUGLjiULm3o7Ka0hkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnVs7S/btsrkSaPyny/QRY9UUGLjiULm3o7Ka0hkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnVs7S/btsrkSaPyny/QRY9UUGLjiULm3o7Ka0hkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnVs7S%2FbtsrkSaPyny%2FQRY9UUGLjiULm3o7Ka0hkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1350&quot; height=&quot;549&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 봇의 토큰 값이 나오게 되는데, 이 값은 한 번밖에 표시되지 않기에 잘 저장해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 무심코 Done을 누르게 되면, 봇을 다시 생성해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpd54d/btsrfihg3LH/oZIfAuEdiYn7Ce1Yv8vxh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpd54d/btsrfihg3LH/oZIfAuEdiYn7Ce1Yv8vxh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpd54d/btsrfihg3LH/oZIfAuEdiYn7Ce1Yv8vxh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpd54d%2Fbtsrfihg3LH%2FoZIfAuEdiYn7Ce1Yv8vxh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1365&quot; height=&quot;588&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤로 나오면, Token의 ID 값만 확인할 수 있고, 기존 Token값과는 완전히 다른 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 채널 ID 보기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rxwNq/btsrgTOPl7W/ANkApZBQpVezkdgdsOhL40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rxwNq/btsrgTOPl7W/ANkApZBQpVezkdgdsOhL40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rxwNq/btsrgTOPl7W/ANkApZBQpVezkdgdsOhL40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrxwNq%2FbtsrgTOPl7W%2FANkApZBQpVezkdgdsOhL40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;493&quot; height=&quot;590&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bot이 메시지를 보낼 채널을 누르고, View Info를 눌러 정보창을 연다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LA9fF/btsrraPdkEF/54DwOmSLy2nSBuITbFUtO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LA9fF/btsrraPdkEF/54DwOmSLy2nSBuITbFUtO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LA9fF/btsrraPdkEF/54DwOmSLy2nSBuITbFUtO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLA9fF%2FbtsrraPdkEF%2F54DwOmSLy2nSBuITbFUtO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1393&quot; height=&quot;479&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;479&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면, 채널 우측으로 채널의 고유 ID를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보는 HTTP 요청에 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. HTTP 요청&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developers.mattermost.com/integrate/reference/bot-accounts/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developers.mattermost.com/integrate/reference/bot-accounts/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1692193615853&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;Bot accounts&quot; data-og-description=&quot;Edit on GitHub Using bot accounts Bot accounts access the Mattermost RESTful API on behalf of a bot through the use of the personal access tokens feature. Bot accounts are just like user accounts, except they: Can&amp;rsquo;t be logged into. Can&amp;rsquo;t be used to cre&quot; data-og-host=&quot;developers.mattermost.com&quot; data-og-source-url=&quot;https://developers.mattermost.com/integrate/reference/bot-accounts/&quot; data-og-url=&quot;https://developers.mattermost.com/integrate/reference/bot-accounts/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developers.mattermost.com/integrate/reference/bot-accounts/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.mattermost.com/integrate/reference/bot-accounts/&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;Bot accounts&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Edit on GitHub Using bot accounts Bot accounts access the Mattermost RESTful API on behalf of a bot through the use of the personal access tokens feature. Bot accounts are just like user accounts, except they: Can&amp;rsquo;t be logged into. Can&amp;rsquo;t be used to cre&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.mattermost.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;위 공식 사이트에 접속하면, 아래와 같이 RESTful API 사용법과, 간단하게 실행해 볼 수 있는 예제를 얻을 수 있다.&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;1295&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eAsHFE/btsrtgBpNqI/EDLoZX3TmKW5zzW2Y47uIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eAsHFE/btsrtgBpNqI/EDLoZX3TmKW5zzW2Y47uIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eAsHFE/btsrtgBpNqI/EDLoZX3TmKW5zzW2Y47uIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeAsHFE%2FbtsrtgBpNqI%2FEDLoZX3TmKW5zzW2Y47uIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1295&quot; height=&quot;301&quot; data-origin-width=&quot;1295&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1265&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7FenP/btsrfiO9ScV/aCWvb2EKMxFNCUFeqvUKrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7FenP/btsrfiO9ScV/aCWvb2EKMxFNCUFeqvUKrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7FenP/btsrfiO9ScV/aCWvb2EKMxFNCUFeqvUKrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7FenP%2FbtsrfiO9ScV%2FaCWvb2EKMxFNCUFeqvUKrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1265&quot; height=&quot;264&quot; data-origin-width=&quot;1265&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1692194522816&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -i -X POST -H &quot;Content-Type: application/json&quot; -d &quot;{\&quot;channel_id\&quot;:\&quot;&amp;lt;channel-id&amp;gt;\&quot;, \&quot;message\&quot;:\&quot;This is a message from a bot\&quot;, \&quot;props\&quot;:{\&quot;attachments\&quot;: [{\&quot;pretext\&quot;: \&quot;Look some text\&quot;,\&quot;text\&quot;: \&quot;This is text\&quot;}]}}&quot; -H &quot;Authorization: Bearer &amp;lt; YOUR Bot Token&amp;gt;&quot; http://localhost:8065/api/v4/posts&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;이 역시 escape를 통해 windows cmd에서 사용할 수 있게 변경해 주고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmd를 통해 전송해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 BOT token과, URL, Channel ID는 알맞게 변경해 주어야 한다.&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;1144&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byABsK/btsrh1lzIJk/cr0bKeM2hkY5kEDMvFVG7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byABsK/btsrh1lzIJk/cr0bKeM2hkY5kEDMvFVG7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byABsK/btsrh1lzIJk/cr0bKeM2hkY5kEDMvFVG7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyABsK%2Fbtsrh1lzIJk%2Fcr0bKeM2hkY5kEDMvFVG7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1144&quot; height=&quot;438&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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로 curl 요청을 보내면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 정상작동 화면&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1765&quot; data-origin-height=&quot;733&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LY4Mm/btsrf4DjJ0K/1mkRdkuEKh3SRb86tuZJQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LY4Mm/btsrf4DjJ0K/1mkRdkuEKh3SRb86tuZJQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LY4Mm/btsrf4DjJ0K/1mkRdkuEKh3SRb86tuZJQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLY4Mm%2Fbtsrf4DjJ0K%2F1mkRdkuEKh3SRb86tuZJQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1765&quot; height=&quot;733&quot; data-origin-width=&quot;1765&quot; data-origin-height=&quot;733&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정했던 채널에 정상적으로 메시지가 전송된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>else/MatterMost</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/64</guid>
      <comments>https://olrlobt.tistory.com/64#entry64comment</comments>
      <pubDate>Wed, 16 Aug 2023 23:16:57 +0900</pubDate>
    </item>
    <item>
      <title>[MatterMost] 매터모스트 개인 테스트 서버(Preview server) 열기</title>
      <link>https://olrlobt.tistory.com/63</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;매터모스트 MatterMost&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;매터모스트(Mattermost)는 오픈 소스 메시징 플랫폼으로, 팀들이 소통할 수 있는 &lt;b&gt;자체 호스팅 방식의 채팅 서비스&lt;/b&gt;이다. Slack이나 Microsoft Teams과 같은 서비스와 비슷하게 작동하지만, 매터모스트는 자체 서버에 설치하고 운영할 수 있어 데이터의 &lt;b&gt;사생활 및 보안을 보다 통제할 수 있다는 점&lt;/b&gt;에서 차별화되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;따라서 기업이나, 보안이 필요한 단체에서 많이 쓰이게 되는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;나 역시 매터모스트를 사용하고 있고, 봇 기능과 Webhook에 관심이 있어서 개인 서버를 열어 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;여러 기능들을 테스트해보려 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;개인 서버 (Preview&amp;nbsp;server) 열기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;나는 개인 서버를 열어 완벽하게 사용할 목적이 아닌,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;봇을 만들 목적으로 테스트를 할 개인 서버를 열 것이기 때문에 MM에서 제공하는 &lt;b&gt;Preview server&lt;/b&gt;를 열 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;공식 매터모스트 사이트 접속&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mattermost.com/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mattermost.com/download/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691673026021&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;Host Mattermost on Your Own Infrastructure&quot; data-og-description=&quot;Learn how to download and install Mattermost on your own infrastructure for flexible and trusted secure collaboration&quot; data-og-host=&quot;mattermost.com&quot; data-og-source-url=&quot;https://mattermost.com/download/&quot; data-og-url=&quot;https://mattermost.com/download/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cvQRKQ/hyTCxvqIFA/In8MItObqsedD57VIMqWV0/img.png?width=2400&amp;amp;height=1256&amp;amp;face=258_360_494_596,https://scrap.kakaocdn.net/dn/KYnxF/hyTCDvEzWd/ASzbXdp5eAkkDy584wOy6k/img.png?width=2400&amp;amp;height=1256&amp;amp;face=258_360_494_596,https://scrap.kakaocdn.net/dn/cLcuRK/hyTCLNYq8G/iyFmTWiKG9KGPoFdIScK9k/img.png?width=1594&amp;amp;height=1309&amp;amp;face=0_0_1594_1309&quot;&gt;&lt;a href=&quot;https://mattermost.com/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mattermost.com/download/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cvQRKQ/hyTCxvqIFA/In8MItObqsedD57VIMqWV0/img.png?width=2400&amp;amp;height=1256&amp;amp;face=258_360_494_596,https://scrap.kakaocdn.net/dn/KYnxF/hyTCDvEzWd/ASzbXdp5eAkkDy584wOy6k/img.png?width=2400&amp;amp;height=1256&amp;amp;face=258_360_494_596,https://scrap.kakaocdn.net/dn/cLcuRK/hyTCLNYq8G/iyFmTWiKG9KGPoFdIScK9k/img.png?width=1594&amp;amp;height=1309&amp;amp;face=0_0_1594_1309');&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;Host Mattermost on Your Own Infrastructure&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to download and install Mattermost on your own infrastructure for flexible and trusted secure collaboration&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mattermost.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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxVCXq/btspxaLD0Uz/wqJLA9KoaNnZ7gwBTLtTn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxVCXq/btspxaLD0Uz/wqJLA9KoaNnZ7gwBTLtTn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxVCXq/btspxaLD0Uz/wqJLA9KoaNnZ7gwBTLtTn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxVCXq%2FbtspxaLD0Uz%2FwqJLA9KoaNnZ7gwBTLtTn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1146&quot; height=&quot;610&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brR5DW/btspFkGMV3u/I8PKhyKCrLhf6sjRCSs8G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brR5DW/btspFkGMV3u/I8PKhyKCrLhf6sjRCSs8G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brR5DW/btspFkGMV3u/I8PKhyKCrLhf6sjRCSs8G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrR5DW%2FbtspFkGMV3u%2FI8PKhyKCrLhf6sjRCSs8G1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1119&quot; height=&quot;738&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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 style=&quot;color: #333333; text-align: start;&quot; 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;b&gt; Docker를 사용하여 다운로드&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Docker 선택&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r2kdZ/btsqJRqRPmY/eq21s9pgVChndCEXRgvflK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r2kdZ/btsqJRqRPmY/eq21s9pgVChndCEXRgvflK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r2kdZ/btsqJRqRPmY/eq21s9pgVChndCEXRgvflK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr2kdZ%2FbtsqJRqRPmY%2Feq21s9pgVChndCEXRgvflK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;605&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 누르면 설치 가이드가 나오는데,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 가이드는 서버 배포 가이드(개인 서버용)이므로 스크롤을 위로 올려주자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 개인 서버를 열 생각이면 해당 가이드를 따라가면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Preview M.M. using Docker&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1041&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tmtmZ/btsqT2DS2ma/mKKhAiZzfDIP264V7O90i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tmtmZ/btsqT2DS2ma/mKKhAiZzfDIP264V7O90i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tmtmZ/btsqT2DS2ma/mKKhAiZzfDIP264V7O90i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtmtmZ%2FbtsqT2DS2ma%2FmKKhAiZzfDIP264V7O90i0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;1041&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1041&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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를 사용해서 Preview 서버를 여는 방법이 자세히 설명되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Docker 설치&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pNaJ3/btsqT3iucHt/4CQIzLZtiv0TK0K1tM72L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pNaJ3/btsqT3iucHt/4CQIzLZtiv0TK0K1tM72L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pNaJ3/btsqT3iucHt/4CQIzLZtiv0TK0K1tM72L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpNaJ3%2FbtsqT3iucHt%2F4CQIzLZtiv0TK0K1tM72L1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;407&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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가 없다면, 스크롤 위쪽에 위치한 Install Docker에서 맞는 버전을 다운로드하여 설치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 Window 환경에 이미 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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 터미널 작성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pAPZp/btsqKzQ272p/J3xqyihTFkQK5Rnd79d4Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pAPZp/btsqKzQ272p/J3xqyihTFkQK5Rnd79d4Zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pAPZp/btsqKzQ272p/J3xqyihTFkQK5Rnd79d4Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpAPZp%2FbtsqKzQ272p%2FJ3xqyihTFkQK5Rnd79d4Zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1594&quot; height=&quot;114&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1691676166430&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --name mattermost-preview -d --publish 8065:8065 mattermost/mattermost-preview&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래와 같이 Docker에 MM- preview 컨테이너가 실행된 것을 확인할 수 있다.&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;1360&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPrSQR/btsqRRbL151/ny7BHaPKhW3bTbgU6QIktk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPrSQR/btsqRRbL151/ny7BHaPKhW3bTbgU6QIktk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPrSQR/btsqRRbL151/ny7BHaPKhW3bTbgU6QIktk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPrSQR%2FbtsqRRbL151%2Fny7BHaPKhW3bTbgU6QIktk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;692&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. URL 접속&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너가 실행 중이라면, 아래 URL에 접속할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691675451145&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://localhost:8065/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1833&quot; data-origin-height=&quot;1358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBzxYb/btsqKxyS6Av/e0suKgfgXBSQ6NoXj5PmPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBzxYb/btsqKxyS6Av/e0suKgfgXBSQ6NoXj5PmPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBzxYb/btsqKxyS6Av/e0suKgfgXBSQ6NoXj5PmPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBzxYb%2FbtsqKxyS6Av%2Fe0suKgfgXBSQ6NoXj5PmPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1833&quot; height=&quot;1358&quot; data-origin-width=&quot;1833&quot; data-origin-height=&quot;1358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이메일과 테스트용 UserName, 비밀번호를 입력해서 가입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1835&quot; data-origin-height=&quot;1355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/REIBo/btsqTQXLDuV/IxeFkqvOAVrcdcjWND51wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/REIBo/btsqTQXLDuV/IxeFkqvOAVrcdcjWND51wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/REIBo/btsqTQXLDuV/IxeFkqvOAVrcdcjWND51wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FREIBo%2FbtsqTQXLDuV%2FIxeFkqvOAVrcdcjWND51wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1835&quot; height=&quot;1355&quot; data-origin-width=&quot;1835&quot; data-origin-height=&quot;1355&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 단체의 이름을 적어준다. 나는 봇을 테스트할 목적이기에 Bot-test로 적어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;1353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xSGQ7/btsqKzXPwXb/D6mMCnX1MKxpnwUilhPD50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xSGQ7/btsqKzXPwXb/D6mMCnX1MKxpnwUilhPD50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xSGQ7/btsqKzXPwXb/D6mMCnX1MKxpnwUilhPD50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxSGQ7%2FbtsqKzXPwXb%2FD6mMCnX1MKxpnwUilhPD50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1830&quot; height=&quot;1353&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;1353&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 서비스 연결이 필요하면 선택해 주면 되고, 없으면 Skip 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1833&quot; data-origin-height=&quot;1354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/16DaM/btsqJQ6Ay2o/ZyKRpkQk93ETz5e7NShiOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/16DaM/btsqJQ6Ay2o/ZyKRpkQk93ETz5e7NShiOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/16DaM/btsqJQ6Ay2o/ZyKRpkQk93ETz5e7NShiOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F16DaM%2FbtsqJQ6Ay2o%2FZyKRpkQk93ETz5e7NShiOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1833&quot; height=&quot;1354&quot; data-origin-width=&quot;1833&quot; data-origin-height=&quot;1354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finish Setup&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. Preview&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;1351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lkt5f/btsqS6NiXjD/P8XmKKk9mey3abOXTXv6c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lkt5f/btsqS6NiXjD/P8XmKKk9mey3abOXTXv6c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lkt5f/btsqS6NiXjD/P8XmKKk9mey3abOXTXv6c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flkt5f%2FbtsqS6NiXjD%2FP8XmKKk9mey3abOXTXv6c0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1826&quot; height=&quot;1351&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;1351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 MM Preview 서버를 이용하여 여러 가지 테스트를 진행하거나, 봇 테스트를 진행할 수 있다.&lt;/p&gt;
&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;</description>
      <category>else/MatterMost</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/63</guid>
      <comments>https://olrlobt.tistory.com/63#entry63comment</comments>
      <pubDate>Thu, 10 Aug 2023 23:10:09 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Integer.toString()와 String.valueOf()의 차이</title>
      <link>https://olrlobt.tistory.com/61</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;정수를 문자열로 변환할 때, 당연하게 &lt;b&gt;String.valueOf()&lt;/b&gt;만을 사용하고 있었는데, 알고리즘 스터디를 진행하며 &lt;b&gt;Integer.toString()&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_1691113460422&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int pri = -3;

String valueOf = String.valueOf(pri);
System.out.println(valueOf);

String toString = Integer.toString(pri);
System.out.println(toString);

//-3
//-3&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Integer.toString() vs String.valueOf()&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서는 정수를 문자열로 변경할 때, 일반적으로 두 가지를 이용할 수 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드들은 변환하는 정수 타입이 &lt;b&gt;int&lt;/b&gt; (P&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;rimitive type) 이냐, &lt;b&gt;Integer&lt;/b&gt; (Wrapper class) 이냐에 따라 약간의 차이가 발생하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;int&lt;/b&gt; (Primitive type)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 int를 사용할 때는, 두 메서드는&amp;nbsp; 완벽히 동일한 작동을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 String 클래스의 valueOf 메서드 자체가 Integer.toString() 메서드를 사용하기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VmV5p/btspYee8b4v/lAFO29hKpvjOG1qUsFCKg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VmV5p/btspYee8b4v/lAFO29hKpvjOG1qUsFCKg0/img.png&quot; data-alt=&quot;int가 오버라이드한 valueOf() 메소드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VmV5p/btspYee8b4v/lAFO29hKpvjOG1qUsFCKg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVmV5p%2FbtspYee8b4v%2FlAFO29hKpvjOG1qUsFCKg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;218&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;int가 오버라이드한 valueOf() 메소드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckiUGW/btspUnXYX3S/FHowlFDIrxPuXddznTaG3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckiUGW/btspUnXYX3S/FHowlFDIrxPuXddznTaG3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckiUGW/btspUnXYX3S/FHowlFDIrxPuXddznTaG3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckiUGW%2FbtspUnXYX3S%2FFHowlFDIrxPuXddznTaG3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;302&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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: start;&quot;&gt;그렇다면 여기서 valueOf를 사용하는 것이, 메서드를 하나 더 호출하니까 호출 스택이 하나 늘어나서 성능적으로 안 좋은 것이 아니냐는 의문이 들 수 있는데, &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: start;&quot;&gt;JVM에서의 메서드 최적화 프로세스인 인라이닝을 통해, 결과적으로는 아주 같은 결과를 제공하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;b&gt;Integer&lt;/b&gt; (Wrapper class)&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Wrapper class 인 Integer 같은 경우에, null이 아닌 값을 사용할 때에는 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;String.valueOf() 메서드와 Integer.toString() 메서드 모두 기본형인 int의 결과와 같은 결과를 제공한다.&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_1691116945887&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Integer wrapper = -3;

String valueOf = String.valueOf(wrapper);
System.out.println(valueOf);

String toString = Integer.toString(wrapper);
System.out.println(toString);

// -3
// -3&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;하지만 Integer의 값이 null 일 경우를 보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691117087854&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Integer wrapper = null;

String valueOf = String.valueOf(wrapper);
System.out.println(valueOf);

String toString = Integer.toString(wrapper);
System.out.println(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;이 경우, String.valueOf()의 경우에는 값이 잘 나오지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Integer.toString() 메서드에서 NPE(NullPointerException)를 던진다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqPtdm/btsp33Rcn5g/bCmswWGAmy7rThXFPJdMV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqPtdm/btsp33Rcn5g/bCmswWGAmy7rThXFPJdMV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqPtdm/btsp33Rcn5g/bCmswWGAmy7rThXFPJdMV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqPtdm%2Fbtsp33Rcn5g%2FbCmswWGAmy7rThXFPJdMV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;150&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 결과가 나오는 이유는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Integer의 toString() 메서드와 int의 toString() 메서드가 같은 메서드를 호출하고 있고, 이 메서드는 Wrapper클래스인 Integer가 아닌, int를 인자로 받고 있기 때문에 발생한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPU2ce/btspTK6YEhW/V56LI8IMCUJViRUuMSqKXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPU2ce/btspTK6YEhW/V56LI8IMCUJViRUuMSqKXk/img.png&quot; data-alt=&quot;int 와 같은 메소드를 호출하고 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPU2ce/btspTK6YEhW/V56LI8IMCUJViRUuMSqKXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPU2ce%2FbtspTK6YEhW%2FV56LI8IMCUJViRUuMSqKXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;302&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;int 와 같은 메소드를 호출하고 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, String.valueOf에서는 왜 에러가 나지 않고 출력이 나왔을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 이유는&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;String.valueOf의 경우 Wapper클래스에서 오버라이딩 하게 되면,&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기본형에서 오버라이딩한 메서드와는 다른, Object를 인자로 받는 메서드를&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 오버라이딩 하기 때문에,&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;호출 결과가 조금 다르게 작동하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOQEbc/btsp0SQxArq/TDWeXj88rxwjcvZ6AYvxXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOQEbc/btsp0SQxArq/TDWeXj88rxwjcvZ6AYvxXK/img.png&quot; data-alt=&quot;Integer가 오버라이드한 valueOf() 메소드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOQEbc/btsp0SQxArq/TDWeXj88rxwjcvZ6AYvxXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOQEbc%2Fbtsp0SQxArq%2FTDWeXj88rxwjcvZ6AYvxXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;634&quot; height=&quot;194&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Integer가 오버라이드한 valueOf() 메소드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 본 int의 valueOf 와는 다르게 Wrapper class가 null일 경우를 처리해 주는 것을 알 수 있고,&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 NPE를 던지지 않고 null을 잘 출력해 주게 된다.&lt;/p&gt;
&lt;p data-ke-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</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/61</guid>
      <comments>https://olrlobt.tistory.com/61#entry61comment</comments>
      <pubDate>Fri, 4 Aug 2023 14:21:33 +0900</pubDate>
    </item>
    <item>
      <title>[IDE] IntelliJ에서 이클립스 프로젝트 가져오기 및 개별 빌드하는 방법</title>
      <link>https://olrlobt.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;인텔리제이에서 이클립스 프로젝트 가져오기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이와 이클립스는 둘 다 Java Project를 실행시킬 수 있는 IDE이지만, 제조사와 설계 방식에서 차이가 있기 때문에 프로젝트를 생성하면 다른 구조로 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 이클립스로 만든 프로젝트를 인텔리제이에서 실행하게 될 경우,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 이클립스 프로젝트 import&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;File &amp;gt; new &amp;gt; Project from Exisiting Sources&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi6Lck/btsn6NvOm5p/aDMP2XFmzM2h0UT3H3K42K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi6Lck/btsn6NvOm5p/aDMP2XFmzM2h0UT3H3K42K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi6Lck/btsn6NvOm5p/aDMP2XFmzM2h0UT3H3K42K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi6Lck%2Fbtsn6NvOm5p%2FaDMP2XFmzM2h0UT3H3K42K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;379&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로젝트 경로 선택 &amp;gt; OK&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD7UqU/btsn12792Of/CwXEzlBEDsjwFk2NytVjjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD7UqU/btsn12792Of/CwXEzlBEDsjwFk2NytVjjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD7UqU/btsn12792Of/CwXEzlBEDsjwFk2NytVjjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD7UqU%2Fbtsn12792Of%2FCwXEzlBEDsjwFk2NytVjjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;562&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;연달아 &quot;Next&quot; 클릭&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MCCfr/btsn7rmf7sx/ARt1rVC6VsnSe1cO6DDX21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MCCfr/btsn7rmf7sx/ARt1rVC6VsnSe1cO6DDX21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MCCfr/btsn7rmf7sx/ARt1rVC6VsnSe1cO6DDX21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMCCfr%2Fbtsn7rmf7sx%2FARt1rVC6VsnSe1cO6DDX21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;369&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSdQxL/btsn7Ky4ZV3/rULRtD6bAxPcinHE6xKBK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSdQxL/btsn7Ky4ZV3/rULRtD6bAxPcinHE6xKBK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSdQxL/btsn7Ky4ZV3/rULRtD6bAxPcinHE6xKBK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSdQxL%2Fbtsn7Ky4ZV3%2FrULRtD6bAxPcinHE6xKBK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;368&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wMUNA/btsnZBRCHUg/R1O3dLQKn8Jyr3YvkXDTt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wMUNA/btsnZBRCHUg/R1O3dLQKn8Jyr3YvkXDTt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wMUNA/btsnZBRCHUg/R1O3dLQKn8Jyr3YvkXDTt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwMUNA%2FbtsnZBRCHUg%2FR1O3dLQKn8Jyr3YvkXDTt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;419&quot; height=&quot;369&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;589&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Create&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsF5fM/btsn0fHxb5Q/vCe4aUrjAz2uzKmGpW11P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsF5fM/btsn0fHxb5Q/vCe4aUrjAz2uzKmGpW11P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsF5fM/btsn0fHxb5Q/vCe4aUrjAz2uzKmGpW11P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsF5fM%2Fbtsn0fHxb5Q%2FvCe4aUrjAz2uzKmGpW11P1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;368&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로젝트 Import 완료&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;417&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMnQUb/btsn3kH2vX2/IHfGIl2VycmKhuWkCwZ8BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMnQUb/btsn3kH2vX2/IHfGIl2VycmKhuWkCwZ8BK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMnQUb/btsn3kH2vX2/IHfGIl2VycmKhuWkCwZ8BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMnQUb%2Fbtsn3kH2vX2%2FIHfGIl2VycmKhuWkCwZ8BK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;417&quot; height=&quot;381&quot; data-origin-width=&quot;417&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이클립스로 생성한 프로젝트 기 때문에, 빌드를 위한. classpath ,. project 파일이 보이고. 인텔리제이로 생성했을 때와는 다른 프로젝트 구조를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;인텔리제이 개별 빌드하는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이는 이클립스와는 다르게, 한 클래스를 빌드하게 되면 모든 클래스가 빌드된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Error가 있는 클래스가 하나라도 있으면 실행 결과를 볼 수가 없는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이클립스는 개별 빌드를 기본적으로 제공하기 때문에 이클립스에서 만든 프로젝트를 인텔리제이에서 사용하게 된다면 마주하기 쉬운 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcJDeM/btsnZy1T4ZW/K3ht4uyDvwNiw2OJiN9M91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcJDeM/btsnZy1T4ZW/K3ht4uyDvwNiw2OJiN9M91/img.png&quot; data-alt=&quot;test2 패키지의 Error클래스들 때문에 test1.Test1 클래스가 실행되지 않는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcJDeM/btsnZy1T4ZW/K3ht4uyDvwNiw2OJiN9M91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcJDeM%2FbtsnZy1T4ZW%2FK3ht4uyDvwNiw2OJiN9M91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;634&quot; height=&quot;781&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;test2 패키지의 Error클래스들 때문에 test1.Test1 클래스가 실행되지 않는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Build, No Error Check&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Run &amp;gt; Edit Configurations&amp;nbsp; 또는,&amp;nbsp; 우측 상단 Class명 &amp;gt; Edit Configurations&lt;/h4&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/bjqJr3/btsn09G6BEf/EGKQ7IpNt6HbLgOT8clDh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjqJr3/btsn09G6BEf/EGKQ7IpNt6HbLgOT8clDh0/img.png&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;321&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;48.13&quot; width=&quot;320&quot; height=&quot;280&quot; style=&quot;width: 47.5662%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjqJr3/btsn09G6BEf/EGKQ7IpNt6HbLgOT8clDh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjqJr3%2Fbtsn09G6BEf%2FEGKQ7IpNt6HbLgOT8clDh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;367&quot; height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Om1mB/btsnZJPI5pa/J3rRG3BI7uXUyYMJV8pxR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Om1mB/btsnZJPI5pa/J3rRG3BI7uXUyYMJV8pxR0/img.png&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;340&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;51.87&quot; width=&quot;315&quot; height=&quot;256&quot; style=&quot;width: 51.271%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Om1mB/btsnZJPI5pa/J3rRG3BI7uXUyYMJV8pxR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOm1mB%2FbtsnZJPI5pa%2FJ3rRG3BI7uXUyYMJV8pxR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;419&quot; height=&quot;340&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;h4 data-ke-size=&quot;size20&quot;&gt;Modify options &amp;gt; Add befor launch task&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lsUQe/btsn7O2GqIu/ZLU8z3qyUd4mur5RlhHgS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lsUQe/btsn7O2GqIu/ZLU8z3qyUd4mur5RlhHgS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lsUQe/btsn7O2GqIu/ZLU8z3qyUd4mur5RlhHgS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlsUQe%2Fbtsn7O2GqIu%2FZLU8z3qyUd4mur5RlhHgS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1093&quot; height=&quot;792&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Build, no error check &amp;gt; Build 체크 해제&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1043&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bliQiE/btsnZYFQaV8/vZ8X0tdShEuMAqfBMZbwpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bliQiE/btsnZYFQaV8/vZ8X0tdShEuMAqfBMZbwpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bliQiE/btsnZYFQaV8/vZ8X0tdShEuMAqfBMZbwpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbliQiE%2FbtsnZYFQaV8%2FvZ8X0tdShEuMAqfBMZbwpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1043&quot; height=&quot;677&quot; data-origin-width=&quot;1043&quot; data-origin-height=&quot;677&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;오류: 기본 클래스 ~~ 을(를) 찾거나 로드할 수 없습니다.&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkS8hN/btsn6I3aEcR/QtOcXjoCXbkxXGXowzbc00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkS8hN/btsn6I3aEcR/QtOcXjoCXbkxXGXowzbc00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkS8hN/btsn6I3aEcR/QtOcXjoCXbkxXGXowzbc00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkS8hN%2Fbtsn6I3aEcR%2FQtOcXjoCXbkxXGXowzbc00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;146&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 이클립스의 java 프로젝트의 경우, bin 폴더에 컴파일된 class 파일을 저장하게 되고,&lt;br /&gt;인텔리제이의 경우 out폴더에 컴파일된 class 파일을 저장하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 차이로 인하여 인텔리제이가 컴파일된 class 파일을 찾지 못하여, 로드할 수 없는 문제가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서는, 해당 프로젝트에서 bin 폴더에 컴파일된 파일을 저장하고 있으므로,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러를 변경하여 컴파일해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Compiler 변경&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;File &amp;gt; Settings&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;263&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJFppx/btsnZxaZf47/GQLXnk9FXlUkHT8fOtkYV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJFppx/btsnZxaZf47/GQLXnk9FXlUkHT8fOtkYV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJFppx/btsnZxaZf47/GQLXnk9FXlUkHT8fOtkYV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJFppx%2FbtsnZxaZf47%2FGQLXnk9FXlUkHT8fOtkYV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;263&quot; height=&quot;474&quot; data-origin-width=&quot;263&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Compiler : Eclipse로 변경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Proceed on errors 체크&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/smZJZ/btsn1JBsF9U/uDMKhJhZHuI4ImKukzOANK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/smZJZ/btsn1JBsF9U/uDMKhJhZHuI4ImKukzOANK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/smZJZ/btsn1JBsF9U/uDMKhJhZHuI4ImKukzOANK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsmZJZ%2Fbtsn1JBsF9U%2FuDMKhJhZHuI4ImKukzOANK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;987&quot; height=&quot;715&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;개별 빌드 성공&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkzuIG/btsn3khiLRw/uyDGO6Mz7lJrFjPLIqkhbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkzuIG/btsn3khiLRw/uyDGO6Mz7lJrFjPLIqkhbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkzuIG/btsn3khiLRw/uyDGO6Mz7lJrFjPLIqkhbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkzuIG%2Fbtsn3khiLRw%2FuyDGO6Mz7lJrFjPLIqkhbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1068&quot; height=&quot;783&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-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>else/개발환경</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/60</guid>
      <comments>https://olrlobt.tistory.com/60#entry60comment</comments>
      <pubDate>Tue, 18 Jul 2023 15:43:58 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 람다식, 함수형 인터페이스와 메소드 참조(::)</title>
      <link>https://olrlobt.tistory.com/59</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;람다식 &lt;/b&gt;(&lt;span style=&quot;color: #000000;&quot;&gt;Lambda Expression)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;자바 8부터 람다식(lambda expression)이 도입되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;람다식은 자바에서도 익명 함수(anonymous function)를 표현하기 위한 간결하고 효율적인 방법으로,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;함수형 인터페이스(functional interface)를 구현하기 위해 사용되며, 주로 함수형 프로그래밍과 스트림 처리(stream processing)에서 유용하게 쓰인다.&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: #374151;&quot;&gt;자바에서 람다식은 Stream과 Optional에 자주 쓰이며, 이 클래스들을 사용을 안 했더라도, 자바스크립트에서 자주 접했을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;Optional ex)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689343506594&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; name = Optional.ofNullable(getName());
name.ifPresent(n -&amp;gt; System.out.println(n));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Stream ex)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689343517456&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().forEach(n -&amp;gt; System.out.println(n));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Java Script ex)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689343563098&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;element.addEventListener('click', () =&amp;gt; {
  console.log('Clicked!');
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;위 예제처럼 &lt;/span&gt;&lt;span style=&quot;color: #374151; letter-spacing: 0px;&quot;&gt;함수, 특히 &lt;b&gt;컬렉션 자료구조에서 유용하게 사용&lt;/b&gt;되며, &lt;b&gt;코드를 간결하고 가독성 있게 처리할 수 있다는 장점&lt;/b&gt;이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; letter-spacing: 0px;&quot;&gt;함수형 인터페이스 &lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #374151; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;(Functional Interface)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;함수형 인터페이스(Functional Interface)는 람다식을 지원하기 위해 도입된 개념이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;함수형 인터페이스는&lt;b&gt; 단 하나의 추상 메서드만을 가지는 인터페이스&lt;/b&gt;를 말한다. 이러한 특성 때문에 함수형 인터페이스는 람다식과 함께 사용될 수 있으며, 함수를 값으로 다룰 수 있는 기능을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 인터페이스는 java.util.function 패키지에 내장되어 있으며, 대표적인 주요 함수형 인터페이스는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Supplier &amp;lt;T&amp;gt;:&lt;/b&gt; 매개변수를 받지 않고, T 타입의 값을 제공하는 함수형 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Consumer &amp;lt;T&amp;gt;:&lt;/b&gt; T 타입의 값을 받아서 소비하는 함수형 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Function &amp;lt;T, R&amp;gt;:&lt;/b&gt; T 타입의 값을 입력으로 받아서 R 타입의 값을 반환하는 함수형 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Predicate &amp;lt;T&amp;gt;:&lt;/b&gt; T 타입의 값을 입력으로 받아서 boolean 값을 반환하는 함수형 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BinaryOperator &amp;lt;T&amp;gt;:&lt;/b&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt; 두 개의 입력 값을 받아서 같은 타입의 결과 값을 반환하는 함수. 입력과 출력의 타입이 동일&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 방법 :&lt;/p&gt;
&lt;pre id=&quot;code_1689344383593&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.function.BinaryOperator;
import java.util.function.Predicate;

public class example {
    public static void main(String[] args) {

        Predicate&amp;lt;Integer&amp;gt; isPositive = x -&amp;gt; x &amp;gt; 0;
        boolean result = isPositive.test(5);
        System.out.println(result); // true
        
        BinaryOperator&amp;lt;Integer&amp;gt; sum = (x, y) -&amp;gt; x + y;
        int result1 = sum.apply(2, 3); 
        System.out.println(result1); // 5
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;@FunctionalInterface&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;@FunctionalInterface는 &lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;컴파일러에게 &lt;b&gt;해당 인터페이스가 함수형 인터페이스임을 알려주는 역할&lt;/b&gt;을 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;앞서 예제에서 사용한 인터페이스들 역시, 패키지 내부를 보면 &lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;@FunctionalInterface를 사용한 것을 알 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;897&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn9dST/btsnD2BVDBd/oKEw1giko5Hxn6Id59ss8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn9dST/btsnD2BVDBd/oKEw1giko5Hxn6Id59ss8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn9dST/btsnD2BVDBd/oKEw1giko5Hxn6Id59ss8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn9dST%2FbtsnD2BVDBd%2FoKEw1giko5Hxn6Id59ss8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;897&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;897&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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: #374151; text-align: left;&quot;&gt;@FunctionalInterface를 이용하여 사용자가 함수형 인터페이스를 생성, 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689344636532&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FunctionalInterface
interface SumLamda{
    int sum(int a, int b);
}

public class example {
    public static void main(String[] args) {
        SumLamda sumLamda = (a, b) -&amp;gt; a + b;
        System.out.println(sumLamda.sum(3,5)); //8
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; letter-spacing: 0px;&quot;&gt;메소드 참조(::)&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;메소드 참조(::)는 자바에서 메서드를 간결하게 참조하고 호출하는 기능을 제공하는 표현 방식이다. 메소드 참조를 사용하면 &lt;b&gt;람다식보다 더 간결한 코드&lt;/b&gt;를 작성할 수 있으며, 코드의 가독성을 높일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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: #374151; letter-spacing: 0px;&quot;&gt;예를 들어, 자주 사용하는 Math 클래스의 sqrt 메소드를 참조해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689345415342&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class example {
    public static void main(String[] args) {

        Function&amp;lt;Double, Double&amp;gt; sqrtFx = Math::sqrt;
        double result = sqrtFx.apply(16.0); // 4.0

        System.out.println(result);
    }
}&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;Function 함수형 인터페이스의 경우, 첫 번째 인자값 형식을 받아서 두 번째 인자값으로 반환해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 앞서 설명했듯이 함수형 인터페이스에는 하나의 메소드만 존재해야 하기 때문에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언해 준 sqrtFx에 Math클래스의 sqrt 메소드 하나만 대입해 준 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply()는 Fuction에서 반환해 주는 메소드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 메소드&amp;nbsp;참조를 이용하면 컬렉션 사용에 유용해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 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;평소에 List의 값을 반복문을 통하여 출력하곤 했다.&lt;/p&gt;
&lt;pre id=&quot;code_1689345857946&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class example {
    public static void main(String[] args) {
        List&amp;lt;Integer&amp;gt; list = List.of(1,2,3,4);
        for(int i : list){
        	System.out.print(i);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 메소드 참조로 변경하면,&lt;/p&gt;
&lt;pre id=&quot;code_1689345880784&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class example {
    public static void main(String[] args) {
        List&amp;lt;Integer&amp;gt; list = List.of(1,2,3,4);
        list.forEach(System.out::print);
    }
}&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Java</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/59</guid>
      <comments>https://olrlobt.tistory.com/59#entry59comment</comments>
      <pubDate>Fri, 14 Jul 2023 23:49:04 +0900</pubDate>
    </item>
    <item>
      <title>[회고록] 싸피 10기 합격 후기</title>
      <link>https://olrlobt.tistory.com/58</link>
      <description>&lt;figure id=&quot;og_1687428966089&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;[회고록] 싸피9기 인터뷰 탈락 후기&quot; data-og-description=&quot;싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정해서, 탈락 후기를 쓰며 실수를 반복&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/28&quot; data-og-url=&quot;https://olrlobt.tistory.com/28&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cbfo9x/hyS4r5d68l/b3uJt4cMiCBUEQtKknvms0/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/r79N7/hyS4noemzr/Pm0T9JUDks0VCBslw7IJL0/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/bPLjLw/hyS5xvZagP/XVN4PUjGSzcBI8Gi4ZuUq1/img.png?width=327&amp;amp;height=425&amp;amp;face=0_0_327_425&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/28&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/28&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cbfo9x/hyS4r5d68l/b3uJt4cMiCBUEQtKknvms0/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/r79N7/hyS4noemzr/Pm0T9JUDks0VCBslw7IJL0/img.png?width=800&amp;amp;height=1403&amp;amp;face=0_0_800_1403,https://scrap.kakaocdn.net/dn/bPLjLw/hyS5xvZagP/XVN4PUjGSzcBI8Gi4ZuUq1/img.png?width=327&amp;amp;height=425&amp;amp;face=0_0_327_425');&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기 인터뷰 탈락 후기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;싸피 9기 합격자가 발표된 지 2주가 지났다. 추가합격이 될지 모른다는 희망에 탈락 후기를 미루었었는데, 이제는 그냥 마음을 접고 공부를 열심히 하기로 정해서, 탈락 후기를 쓰며 실수를 반복&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싸피는 10기까지만 한다는 소문이 있다. 멀티캠퍼스와의 계약 기간 때문이라는 말이 있던데, 오피셜 인지는 확실하지 않기 때문에 혹시 11기가 생긴다면 도움이 되는 바람으로 후기를 작성한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;지원동기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싸피 9기 인터뷰에서 탈락하고, 취업 준비를 하던 나는 인프런 강의를 통해 똑같은 내용을 또 공부하였었다.&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;취업을 계속 시도해 보았지만, 9기 이후 10기를 모집할 때까지 내가 원하는 곳에 들어가기엔 무리가 있어 10기 지원을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;준비과정&lt;/b&gt;&lt;/h2&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;&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 대외비라서 많은 부분을 얘기하지 못하지만, 내가 준비했던 과정을 위주로 이야기해 보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 에세이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싸피 지원은 에세이로 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에세이 문항이 9기와는 조금 달라진 것 같지만, 결국 쓰는 내용은 별 반 다르지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 작성해 보는 에세이기에, 어떤 점을 말하고 싶은 지를 전략적으로 생각해서 작성을 했고, &lt;b&gt;주저리주저리를 하지 않았다&lt;/b&gt;. 나는 저번 과정에서, 내가 너무 주저리 주저리 된다고 생각하여, 이번 에세이에는 &lt;b&gt;팩트만 담백하게&lt;/b&gt; 담아냈다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 했던 것들. 어떤 것이 장점인지. 내가 싸피가 필요한 지원 동기에 대해 담백하게 작성해 보려 노력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 저번과 다르게 길게, 오래, 다시 읽어보며 수정을 거쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 코테&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싸피는 교육기관인 만큼 코테 난이도가 높지 않다. 여느 기업들의 코딩테스트를 보아본 경험으로 말하자면, 엄청 쉬운 편에 속한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;본인이 DFS, BFS, DP 등의 기본적인 알고리즘을 어느 정도 풀 수 있다면 충분히 통과할 수 있는 난관이라 생각한다.&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;9기 때는 알고리즘을 한 달도 채 공부하지 않은 상태로&amp;nbsp;시험을 보고, 운이 좋게 합격을 했었는데.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 싸피 떨어지고 알고리즘 공부를 많이 했기 때문에, 따로 준비는 하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;1713&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tFe0U/btsk27cZYh8/kM64QIey25M3XQkmz5fMKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tFe0U/btsk27cZYh8/kM64QIey25M3XQkmz5fMKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tFe0U/btsk27cZYh8/kM64QIey25M3XQkmz5fMKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtFe0U%2Fbtsk27cZYh8%2FkM64QIey25M3XQkmz5fMKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;223&quot; height=&quot;1713&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;1713&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 인터뷰&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터뷰 일정이 나오고, 바로 면접 스터디를 준비했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 사는 지역의 이름으로 오픈채팅방을 바로 만들었지만, 들어오는 인원이 현저히 적어서 면접스터디를 진행할 수 없었다. 심지어 다른 오픈채팅방조차 내 지역에는 없었다.&lt;/p&gt;
&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;이번에는 지역이 조금 멀어서 가는 시간만 50~60분이 걸렸다.&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;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;9기 때의 스터디와 다른 점이 있다면, 9기 때 노션을 사용하면 훨씬 좋을 것이라 생각했는데, 이번에 사용을 해보니 훨씬 간단하고 너무 편리했다. 또한, 나는 혼자 따로 질문 답변에 대한 준비를 하지 않고 질의응답에 응했다. &lt;b&gt;어떤 질문이 나오던, 바로 대답하는 능력이 중요&lt;/b&gt;하다고 생각해서, 그에 맞춰 잘 대답할 수 있는 능력을 키우려 노력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;모든 대답과 자기소개에서 주저리주저리를 뺐다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실전&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 실전에 항상 아프다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실전 전에 또 감기에 걸려, 면접 스터디를 빠졌는데. 이건 오히려 다행이라고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p 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;새벽 4시에 잤나?&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;PT문제를 잘 해결했다고 생각하고,&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h2&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;p data-ke-size=&quot;size16&quot;&gt;그냥 이럴 바엔 놀자라고 생각해서 안 가던 피시방에 가서 하루를 보내기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 1주일이 거의 다 되었을 때부터는, 2시 3시마다 싸피에 들어가서 새로고침을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;1573&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cdw1H/btskZk6rmEZ/rtw5xQVMkkLVDMRNIYEaek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cdw1H/btskZk6rmEZ/rtw5xQVMkkLVDMRNIYEaek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cdw1H/btskZk6rmEZ/rtw5xQVMkkLVDMRNIYEaek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCdw1H%2FbtskZk6rmEZ%2Frtw5xQVMkkLVDMRNIYEaek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;398&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;1573&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;가서 더 좋은 환경에서 더 열심히 공부해서 꼭 좋은 개발자가 되어야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>else/회고</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/58</guid>
      <comments>https://olrlobt.tistory.com/58#entry58comment</comments>
      <pubDate>Thu, 22 Jun 2023 23:38:52 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 스프링 컨테이너와 스프링 빈(Bean) 등록</title>
      <link>https://olrlobt.tistory.com/57</link>
      <description>&lt;figure id=&quot;og_1685014572858&quot; style=&quot;color: #333333; text-align: start;&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;[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의&quot; data-og-description=&quot;스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QVpAn/hySKCd9OGp/kFWWEnWPkyikikek7KcgA1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/cenmBD/hySKyW8Irn/f8cNzsFjgbZfx5QJEkBXak/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/mcMnY/hySKwZk1B4/ypzWCX94MgbKBaBiIwXoL1/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QVpAn/hySKCd9OGp/kFWWEnWPkyikikek7KcgA1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/cenmBD/hySKyW8Irn/f8cNzsFjgbZfx5QJEkBXak/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/mcMnY/hySKwZk1B4/ypzWCX94MgbKBaBiIwXoL1/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인프런의 김영한 강사님의 무료강의를 듣고 궁금한 내용을 찾아서 정리하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;스프링 빈 (Spring Bean)&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Java Bean은 일반적으로는 getter와 setter를 갖고 있는 클래스(객체)를 의미하고, new를 통해 객체를 생성해 사용하곤 한다. 이러한 객체를 스프링 컨테이너에 등록해 주어, 개발자가 아닌 스프링 프레임워크가 객체를 관리하게 하는 것을 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;제어의 역전 (IoC)&lt;/b&gt;&lt;/span&gt;라고 하며, 이때 &lt;span style=&quot;color: #000000; background-color: #9feec3;&quot;&gt;&lt;b&gt;스프링 프레임워크가 관리하는 객체&lt;/b&gt;&lt;/span&gt;들을 &lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;스프링 빈 (Spring Bean)&lt;/span&gt;&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스프링 빈 등록&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 프레임워크에서는 Bean을 등록하는 대표적인 세 가지 방법이 존재한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;XML을 이용한 Bean 등록&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1685015615843&quot; class=&quot;xml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;beans&amp;gt;
    &amp;lt;bean id=&quot;exampleBean&quot; class=&quot;com.example.ExampleBean&quot;&amp;gt;
        &amp;lt;property name=&quot;message&quot; value=&quot;Hello, Spring!&quot; /&amp;gt;
    &amp;lt;/bean&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;스프링의 초기 버전에서는 주로 XML 파일을 사용해 빈을 선언했다.&amp;nbsp;&lt;/span&gt;&amp;lt;bean&amp;gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&amp;nbsp;태그를 사용하여 빈을 정의하고, 이 안에서&amp;nbsp;&lt;/span&gt;&amp;lt;constructor-arg&amp;gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;나&amp;nbsp;&lt;/span&gt;&amp;lt;property&amp;gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&amp;nbsp;태그를 사용하여 빈의 의존성을 주입해 주어야 했다.&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;스프링 부트가 출시된 지금은 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;잘 사용하지 않는 방법&lt;/span&gt;&lt;/b&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;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;컴포넌트 스캔 : 어노테이션 기반 Bean 등록&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1685015300582&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class ExampleService {

    private final ExampleRepository exampleRepository;

    @Autowired
    public ExampleService(ExampleRepository exampleRepository) {
        this.exampleRepository = exampleRepository;
    }
    
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;스프링 2.5 이후 &lt;/span&gt;&lt;b&gt;@Component&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;, @Controller, &lt;/span&gt;@Service&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;, &lt;/span&gt;@Repository&lt;/b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;등의 어노테이션을 사용하여 클래스를 빈으로 선언할 수 있다. 또한, &lt;/span&gt;&lt;b&gt;@Autowired&lt;/b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;나 &lt;/span&gt;&lt;b&gt;@Inject&lt;/b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&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;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 등록 방식이 컴포넌트 스캔이라고 불리는 이유는 @Component 어노테이션이 스프링 컴포넌트 스캔의 대상이며 스프링 빈으로 인스턴스화될 수 있음을 나타내는 어노테이션이기 때문이다.&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;@Component 이외의 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;@Controller,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Service&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Repository의 각 인터페이스에 들어가 보면 @Component를 메타-어노테이션(어노테이션에 붙는 어노테이션으로 상속과 비슷함)으로 갖고 있다는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX29Hf/btshp7oQiHh/Hz3lHxTSCRN2pgWtSiXkAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX29Hf/btshp7oQiHh/Hz3lHxTSCRN2pgWtSiXkAK/img.png&quot; data-alt=&quot;다른 어노테이션 안에 메타-어노테이션으로 @Component가 있는 것을 알 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX29Hf/btshp7oQiHh/Hz3lHxTSCRN2pgWtSiXkAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX29Hf%2Fbtshp7oQiHh%2FHz3lHxTSCRN2pgWtSiXkAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1112&quot; height=&quot;302&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다른 어노테이션 안에 메타-어노테이션으로 @Component가 있는 것을 알 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자바 설정 파일을 이용한 Bean 등록&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1685015313319&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class SpringConfig {

    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;스프링 3.0 이후, Java 설정 파일을 사용하는 방식을 사용할 수 있다. &lt;/span&gt;&lt;b&gt;@Configuration&lt;/b&gt;은 해당 클래스가 Bean 정의를 포함하는 설정 클래스임을 나타낸다. &lt;b&gt;@Bean&lt;/b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt; 어노테이션을 사용하여 빈을 선언하고 의존성을 주입할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스프링 컨테이너&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스프링 컨테이너는 스프링 프레임워크에서 제공하는 핵심 기능으로, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;스프링에서의 객체(Bean) 생성과 라이프사이클 관리를 담당하며, 의존성 주입(D.I = Dependency Injection)을 통해 객체 간 관계를 설정하는 역할&lt;/b&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;&lt;span style=&quot;color: #000000;&quot;&gt;스프링 컨테이너는 &lt;b&gt;애플리케이션 개발 시 객체 지향적인 설계와 유연성을 제공&lt;/b&gt;하여 개발자가 비즈니스 로직에 집중할 수 있도록 돕는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스프링 컨테이너는 크게 BeanFactory와 ApplicationContext의 두 가지 형태로 제공된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BeanFactory&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BeanFactory는 스프링 컨테이너의 가장 기본적인 형태이며 최상위 인터페이스이다. 설정 파일에서 제공하는 메타데이터를 통해 빈을 생성하고, Bean 간의 의존성을 처리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BeanFactory는 ApplicationContext와는 다르게, &lt;span style=&quot;text-align: left;&quot;&gt;Bean을 필요한 시점에서 생성하여 인스턴스화하는 &lt;span style=&quot;text-align: start;&quot;&gt; '지연 초기화(Lazy Initialization)' 방식을 사용한다&lt;/span&gt;. 이 방식은, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&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;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ApplicationContext&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;ApplicationContext는 BeanFactory를 상속받아, &lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;BeanFactory의 &lt;/span&gt;모든 기능을 포함하고 있으며, 추가로 &lt;/span&gt;&lt;/span&gt;메시지 소스 처리를 통한 국제화 지원기능, 환경변수 구분 기능, &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;애플리케이션 레이어의 특정 컨텍스트 (ex WebApplicationContext)등 더 많은 기능을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uKpna/btshqTQrG7e/RHwu995S31e8Np4lh4Owb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uKpna/btshqTQrG7e/RHwu995S31e8Np4lh4Owb1/img.png&quot; data-alt=&quot;ApplicationContext는 BeanFactory를 상속받는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uKpna/btshqTQrG7e/RHwu995S31e8Np4lh4Owb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuKpna%2FbtshqTQrG7e%2FRHwu995S31e8Np4lh4Owb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;974&quot; height=&quot;381&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ApplicationContext는 BeanFactory를 상속받는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;ApplicationContext는 모든 &lt;b&gt;Bean을 컨테이너 시작 시점에 생성하고 초기화하는 '즉시 초기화(Eager Initialization)'&lt;/b&gt;를 사용한다. &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;이를 통해 런타임 중에 발생할 수 있는 문제를 미리 방지할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;만약, BeanFactory처럼 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;'지연 초기화(Lazy Initialization)'&lt;span&gt; 를 사용하고 싶으면 @Lazy 어노테이션을 사용하여 Bean을 생성해 주면 지연 초기화가 가능하다.&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;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;위와 같은 이유들로 대부분의 경우, ApplicationContext가 BeanFactory보다 권장되며, 실제로 &lt;b&gt;대부분의 스프링 기반 애플리케이션에서는 ApplicationContext를 사용&lt;/b&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;싱글톤 컨테이너&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 컨테이너는 기본적으로 Bean의 인스턴스를 하나만 생성하여 관리하는 싱글톤 컨테이너이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 컨테이너는 같은 Bean에 대한 모든 요청에 대해 하나의 인스턴스만을 재사용해서 반환하는데, 이는 메모리 사용을 최적화하여 성능 향상을 가져올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 컨테이너가 진짜 싱글톤 컨테이너인지 확인하기 위해, ApplicationContext로 스프링 컨테이너를 가져와 getBean() 메서드를 이용해 빈을 꺼내 확인해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685011086693&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
public class StudySpringApplication {

	public static void main(String[] args) {
		SpringApplication.run(StudySpringApplication.class, args);

		final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
		final MemberService memberService1 = applicationContext.getBean(&quot;memberService&quot;, MemberService.class);
		final MemberService memberService2 = applicationContext.getBean(&quot;memberService&quot;, MemberService.class);
		System.out.println(memberService1);
		System.out.println(memberService2);
		System.out.println(applicationContext.isSingleton(&quot;memberService&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;ApplicationContext로 스프링 컨테이너를 가져와&amp;nbsp; getBean() 메서드를 통하여 등록되어 있는&amp;nbsp; &quot;memberService&quot; 스프링 빈을 가져왔다. 각각 memberService1과 memberService2로 두 번의 요청에 거쳐 가져왔고, 이를 출력해 주어 주솟값을 확인해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czDxZe/btshplgs1Qn/84a8l5cBrKqyqfxztosywK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czDxZe/btshplgs1Qn/84a8l5cBrKqyqfxztosywK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czDxZe/btshplgs1Qn/84a8l5cBrKqyqfxztosywK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczDxZe%2Fbtshplgs1Qn%2F84a8l5cBrKqyqfxztosywK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;95&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Container에 등록된 Bean은 기본적으로 Singleton을 스코프로 사용하기 때문에, 같은 인스턴스를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 같은 주솟값이 출력된 것을 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, ApplicationContext에서 Singleton을 확인하기 위한 메서드로 isSingleton()을 사용할 수 있고, 확인 결과 true를 반환하여 싱글톤 컨테이너임을 확인하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스코프 설정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, Singleton이 아닌 스코프를 사용하고 싶다면 Bean을 등록할 때 스코프를 같이 설정해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bean의 스코프는 singleton, prototype, request, session, global session 등 다양한 값으로 설정할 수 있고, @Scope를 통하여 등록 설정해 주면 된다.&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;singleton: 스프링 컨테이너당 하나의 인스턴스만 생성&lt;/li&gt;
&lt;li&gt;prototype: 요청할 때마다 새로운 인스턴스를 생성&lt;/li&gt;
&lt;li&gt;request: HTTP 요청당 하나의 인스턴스를 생성. 주로 웹 환경에서 사용.&lt;/li&gt;
&lt;li&gt;session: HTTP 세션당 하나의 인스턴스를 생성. 주로 웹 환경에서 사용.&lt;/li&gt;
&lt;li&gt;global session: 전역 HTTP 세션당 하나의 인스턴스를 생성. 주로 포털 환경에서 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 위해 다음과 같이 prototype으로 스코프를 설정한 후, 위 예제를 똑같이 수행해 보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1685022423075&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class SpringConfig {

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;77&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSoQqI/btshqq9zQFw/jvUaYt1QXJIgMJ8MLWDoZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSoQqI/btshqq9zQFw/jvUaYt1QXJIgMJ8MLWDoZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSoQqI/btshqq9zQFw/jvUaYt1QXJIgMJ8MLWDoZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSoQqI%2Fbtshqq9zQFw%2FjvUaYt1QXJIgMJ8MLWDoZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;77&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;77&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 memberService를 요청했음에도 싱글톤과는 다르게 각각 다른 주솟값이 나오는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Spring</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/57</guid>
      <comments>https://olrlobt.tistory.com/57#entry57comment</comments>
      <pubDate>Thu, 25 May 2023 23:01:17 +0900</pubDate>
    </item>
    <item>
      <title>[Pose Estimation] Mediapipe 2배속 분석과 분석 결과 중간 부족한 프레임 채우기</title>
      <link>https://olrlobt.tistory.com/56</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Mediapipe 분석 시간&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 내가 진행 중인 프로젝트에서는 Mediapipe의 분석 결과를 가지고 여러 가지 작업을 진행할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Mediapipe의 경우, 실시간 영상 데이터만을 활용하여 분석 결과를 보여주게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 30초 길이의 영상의 분석 결과를 갖기 위해서는 30초간 영상을 재생하면서 데이터를 실시간으로 보내주어야만 결과 값을 얻을 수 있었다.&lt;/p&gt;
&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;사용자가 서비스를 이용할 때, 30초 영상을 사용하려면 30초를 기다려야 한다는 얘기&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Mediapipe로 2배속 영상 분석&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말한, 분석 시간을 줄일 수는 없을까 고민을 해 보았다.&lt;/p&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배속으로 분석을 진행한다 하더라도 30초 길이의 영상을 분석하는 시간은 15초로 크게 단축될 것이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Mediapipe의 자세추적 기능은 컴퓨터 성능에 따라 차이가 나지만 최대 60 FPS까지 지원하고, 내 PC에서 테스트하여도 초당 10 ~ 15 개의 Frame 추출이 가능하기 때문에 중요한 자세는 다 잡을 수 있을 것이라 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;영상을 2배속으로 만들기 위하여 FFmpeg 라이브러리를 사용하여 사용자가 업로드한 영상을 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;FFmpeg를 사용하는 방법은 아래 포스팅을 참고하자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/53&quot;&gt;https://olrlobt.tistory.com/53&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1682007768192&quot; style=&quot;color: #333333; text-align: start;&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] FFmpeg로 영상 배속 설정하기&quot; data-og-description=&quot;FFmpeg FFmpeg는 비디오, 오디오를 처리하는 오픈소스 라이브러리이다. 대표적으로 지원하는 동작으로는 파일변환, 편집, 스트리밍, 인코딩과 디코딩 등이 있고, 비디오에서 썸네일을 자동으로 뽑&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/53&quot; data-og-url=&quot;https://olrlobt.tistory.com/53&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/czyqr5/hySlH0GSb1/r8cue0OzJK1sOoq93iEisk/img.png?width=1110&amp;amp;height=666&amp;amp;face=0_0_1110_666&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://olrlobt.tistory.com/53&quot; data-source-url=&quot;https://olrlobt.tistory.com/53&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/czyqr5/hySlH0GSb1/r8cue0OzJK1sOoq93iEisk/img.png?width=1110&amp;amp;height=666&amp;amp;face=0_0_1110_666');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Spring boot] FFmpeg로 영상 배속 설정하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;FFmpeg FFmpeg는 비디오, 오디오를 처리하는 오픈소스 라이브러리이다. 대표적으로 지원하는 동작으로는 파일변환, 편집, 스트리밍, 인코딩과 디코딩 등이 있고, 비디오에서 썸네일을 자동으로 뽑&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;그리고는 먼저 원본 파일(1배속)로 Mediapipe 분석을 진행하였다.&lt;/p&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.64초 정도 길이의 짧은 영상을 사용했고, 분석은 같은 timeStamp를 갖는 frame이 나오는 부분을 기준으로 측정하였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (14).gif&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnjKuk/btsbCiCxqWh/SwWMRbNIZf7rNiR8uWwvQ0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnjKuk/btsbCiCxqWh/SwWMRbNIZf7rNiR8uWwvQ0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnjKuk/btsbCiCxqWh/SwWMRbNIZf7rNiR8uWwvQ0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bnjKuk/btsbCiCxqWh/SwWMRbNIZf7rNiR8uWwvQ0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;242&quot; height=&quot;408&quot; data-filename=&quot;ezgif.com-crop (14).gif&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkocmw/btsbANiWoaI/kYtZmuofrAZlJcNaagKYy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkocmw/btsbANiWoaI/kYtZmuofrAZlJcNaagKYy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkocmw/btsbANiWoaI/kYtZmuofrAZlJcNaagKYy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbkocmw%2FbtsbANiWoaI%2FkYtZmuofrAZlJcNaagKYy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;358&quot; height=&quot;192&quot; data-origin-width=&quot;425&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;3.64초 영상에서의 frame 은 42개가 나왔다. 초당 frame 11개 정도로, 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배속 분석 결과를 1배속 영상에서 사용하려고 하는 것이기 때문에 초당 5~6 frame 정도의 결과를 얻을 수 있다고 볼 수 있다. 즉, 0.2초마다 프레임이 전환된다는 이야기인데, 매우 부자연스럽게 끊겨 보일 것으로 예상된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서, FFmpeg를 통하여 2배속 한 영상을 Mediapipe로 포즈 분석을 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상은 절반크기인 1.81의 길이이고, 예상 프레임은 21 정도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (13).gif&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zzn3F/btsbAtkNvtd/nuRdsc5gNNPUo7Uuv8VwE1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zzn3F/btsbAtkNvtd/nuRdsc5gNNPUo7Uuv8VwE1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zzn3F/btsbAtkNvtd/nuRdsc5gNNPUo7Uuv8VwE1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/zzn3F/btsbAtkNvtd/nuRdsc5gNNPUo7Uuv8VwE1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;406&quot; data-filename=&quot;ezgif.com-crop (13).gif&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yWwFg/btsbBQlYyza/4KYoD8s2zAipNtScNwGzTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yWwFg/btsbBQlYyza/4KYoD8s2zAipNtScNwGzTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yWwFg/btsbBQlYyza/4KYoD8s2zAipNtScNwGzTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyWwFg%2FbtsbBQlYyza%2F4KYoD8s2zAipNtScNwGzTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;364&quot; height=&quot;199&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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배속 영상에서의 frame은 예상치와 근접한 24가 나왔다. 예상 frame 수 보다 조금 더 나오긴 했지만, frame이 부족하여 끊겨 보일 수밖에 없다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 끊김 문제를 고민하면서 여러 번 테스트를 진행해 보았는데, 측정 시마다 frame 수가 다르게 측정된다는 것을 알게 되었다. 예로 다시 원본 영상의 프레임을 측정하였는데 38의 결과를 보였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JMUl8/btsbBTbPtha/gKSWciwLFTXvOtqrBqlXK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JMUl8/btsbBTbPtha/gKSWciwLFTXvOtqrBqlXK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JMUl8/btsbBTbPtha/gKSWciwLFTXvOtqrBqlXK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJMUl8%2FbtsbBTbPtha%2FgKSWciwLFTXvOtqrBqlXK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;198&quot; data-origin-width=&quot;422&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 분석하는 과정에서 사용하는 requestAnimationFrame()에 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1682005899493&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function requestAnalyze(videoElement, poseModel) {
    poseModel.send({ image: videoElement });
    requestAnimationFrame(() =&amp;gt;
        requestAnalyze(videoElement, poseModel)
    );
}&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;requestAnimationFrame()은 성능의 영향을 많이 받는다. Frame의 업데이트 주기를 브라우저에서 어떤 요소든지 업데이트가 생기게 되면 등록된 콜백함수를 호출하게 되어있는데, 여기서 frame의 차이가 발생했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 같은 영상의 데이터에서 Frame 값의 차이 없는 것을 원하기 때문에, videoElement의 currentTime을 직접 조절하여 분석을 보내기로 하였다. 이 방식을 사용하면, PC의 성능 차이와는 관계없이 frame 수가 일정하게 보장되면서, 자연스레 frame 수도 증가 될 것으로 예상했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682006155252&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function requestAnalyze(videoElement, canvasCtx, poseModel, loading, loading_background) {
        let videoPre = videoElement.currentTime;
        videoElement.currentTime += 50 / 1000;

        if (videoPre === videoElement.currentTime) {
            // 분석 종료
            return;
        }

        function onTimeUpdate() {
            poseModel.send({ image: videoElement });
            requestAnimationFrame(() =&amp;gt;
                requestAnalyze(videoElement, canvasCtx, poseModel, loading, loading_background)
            );
        }

        videoElement.addEventListener('timeupdate', onTimeUpdate, { once: true });
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o2Uqh/btsbFHIdPF9/u1m929KHKASlNmGDW88iR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o2Uqh/btsbFHIdPF9/u1m929KHKASlNmGDW88iR1/img.png&quot; data-alt=&quot;왼쪽 - 2배속 영상 / 오른쪽 - 원본 1배속 영상&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o2Uqh/btsbFHIdPF9/u1m929KHKASlNmGDW88iR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo2Uqh%2FbtsbFHIdPF9%2Fu1m929KHKASlNmGDW88iR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;173&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;왼쪽 - 2배속 영상 / 오른쪽 - 원본 1배속 영상&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과, 구동 환경과는 관계없이 영상의 시간에 따라 분석 결과를 저장할 수 있게 되었고, 평균적인 frame 수가 증가하였다. 또한, 몇 번의 분석을 하든지 같은 frame 수를 보장하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 비디오의 분석 주기를 50/1000초보다 더 줄이면 더 큰 frame 수를 얻을 수 있겠지만 requestAnimationFrame()을 호출하는 것은 똑같기 때문에 시간을 줄이면 줄일수록 분석에 걸리는 시간이 늘어날 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재의 frame수도 부족하긴 하지만, 3.6초 영상에 36개의 프레임 즉 0.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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (15).gif&quot; data-origin-width=&quot;230&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DQd7m/btsbAO3fO6Z/kpDOhweTWWlHTk8tkUdoTK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DQd7m/btsbAO3fO6Z/kpDOhweTWWlHTk8tkUdoTK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DQd7m/btsbAO3fO6Z/kpDOhweTWWlHTk8tkUdoTK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/DQd7m/btsbAO3fO6Z/kpDOhweTWWlHTk8tkUdoTK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;388&quot; data-filename=&quot;ezgif.com-crop (15).gif&quot; data-origin-width=&quot;230&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 눈으로 직접 확인하고 나니, frame의 부족함이 확 와닿았다. 0.1초 정도면 솔직히 괜찮은 수치이지 않을까 잠깐 생각하기도 했었는데, 예전에 게임을 할 때 30 FPS로도 끊김을 느꼈던 때를 떠 올려보니 더 많은 frame이 필요하다는 것을 깨달았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프레임과 프레임 사이 중간 프레임 채우기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 많은 프레임을 구할 방법을 생각하다 보니, 프레임과 프레임 사이에 보정값을 넣어주면 어떨까 라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zIBJl/btsbA6CyKFh/tLIkbLacLjjKqcUlN3kYY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zIBJl/btsbA6CyKFh/tLIkbLacLjjKqcUlN3kYY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zIBJl/btsbA6CyKFh/tLIkbLacLjjKqcUlN3kYY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzIBJl%2FbtsbA6CyKFh%2FtLIkbLacLjjKqcUlN3kYY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;635&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 위와 같은 두 프레임이 있다면, 두 프레임에서 추출된 좌표값의 중간값을 이용해서 중간 프레임을 구할&amp;nbsp; 수 있다는 생각이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JI96T/btsbBUhxPuq/A0YkeGxpppqEQRaGEy8Zu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JI96T/btsbBUhxPuq/A0YkeGxpppqEQRaGEy8Zu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JI96T/btsbBUhxPuq/A0YkeGxpppqEQRaGEy8Zu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJI96T%2FbtsbBUhxPuq%2FA0YkeGxpppqEQRaGEy8Zu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;633&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;633&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;하지만, 내가 이 전에 구해놓은 데이터의 경우, 2배속 영상을 분석한 결과를 1배속 영상 속도에 맞게 매치했을 때, 0.1초에 1 Frame 정도로 예상이 가능하다. 즉, 사람의 움직임이 0.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;또한, 메이웨더는 1초에 주먹을 4~5번 휘두를 수 있다고 한다. 주먹 한 번에 0.2초라고 해도, 그 시간보다 짧은 시간 단위로 보정값을 채워주는 작업이기 때문에, 큰 오차는 발생하지 않을 것이라 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;269&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TiZZH/btsbEhQFukh/gkivqKwjefMpSeMOsKeofk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TiZZH/btsbEhQFukh/gkivqKwjefMpSeMOsKeofk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TiZZH/btsbEhQFukh/gkivqKwjefMpSeMOsKeofk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTiZZH%2FbtsbEhQFukh%2FgkivqKwjefMpSeMOsKeofk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;443&quot; height=&quot;213&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;269&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;간단하게 frame과 frame의 좌표를 이은 선분에서 1/3 지점과 2/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;로그로 출력해 본 결과, 앞서 얻은 37개의 분석 결과는 동일하며, 중간 보정작업을 통해 총 109개의 frame을 저장하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (16).gif&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2TPNy/btsbBcQmWzY/7tKbxrh9ryAAPFZk9c9TUk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2TPNy/btsbBcQmWzY/7tKbxrh9ryAAPFZk9c9TUk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2TPNy/btsbBcQmWzY/7tKbxrh9ryAAPFZk9c9TUk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/2TPNy/btsbBcQmWzY/7tKbxrh9ryAAPFZk9c9TUk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;256&quot; height=&quot;428&quot; data-filename=&quot;ezgif.com-crop (16).gif&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Frame 수가 증가함에 따라 부드러운 애니메이션을 보여준다.&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/2로 단축하면서 frame 수는 원본 영상의 약 1.5배가량으로 증가시키는 작업을 성공적으로 마무리하게 되었다.&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/56</guid>
      <comments>https://olrlobt.tistory.com/56#entry56comment</comments>
      <pubDate>Fri, 21 Apr 2023 02:40:41 +0900</pubDate>
    </item>
    <item>
      <title>[Pose Estimation] Mediapipe Pose 3D grid에 새로운 축을 적용하고, 새로운 축으로 좌표 변환하기</title>
      <link>https://olrlobt.tistory.com/55</link>
      <description>&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;저번 포스팅에서 Mediapipe 분석 결과를 3D grid로 출력하는 것을 다루었다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; 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://olrlobt.tistory.com/54&quot;&gt;https://olrlobt.tistory.com/54&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1680533473767&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;[Pose Estimation] Mediapipe Pose 분석 결과 3D grid로 렌더링하기&quot; data-og-description=&quot;토이 프로젝트 진행 중, 3D 결과 값을 보여준다면 사용자에게 더 좋은 결과를 보여 줄 수 있다고 생각하여, 3D Utils의 Grid를 이용하여 사용자에게 결과를 보여주기로 하였다. Mediapipe Media pipe는 Googl&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/54&quot; data-og-url=&quot;https://olrlobt.tistory.com/54&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/P8MMN/hyR9GuddRB/hxBUxfVma2ZRgoduy67391/img.gif?width=256&amp;amp;height=438&amp;amp;face=0_0_256_438,https://scrap.kakaocdn.net/dn/cUO3eO/hyR9CSUxFu/OkmgS1F07EaQEBcZ439cc0/img.gif?width=256&amp;amp;height=438&amp;amp;face=0_0_256_438,https://scrap.kakaocdn.net/dn/qG6du/hyR9CrPej6/WuTkBkVFpFc8aRykih7xq0/img.png?width=1214&amp;amp;height=970&amp;amp;face=0_0_1214_970&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/54&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/54&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/P8MMN/hyR9GuddRB/hxBUxfVma2ZRgoduy67391/img.gif?width=256&amp;amp;height=438&amp;amp;face=0_0_256_438,https://scrap.kakaocdn.net/dn/cUO3eO/hyR9CSUxFu/OkmgS1F07EaQEBcZ439cc0/img.gif?width=256&amp;amp;height=438&amp;amp;face=0_0_256_438,https://scrap.kakaocdn.net/dn/qG6du/hyR9CrPej6/WuTkBkVFpFc8aRykih7xq0/img.png?width=1214&amp;amp;height=970&amp;amp;face=0_0_1214_970');&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;[Pose Estimation] Mediapipe Pose 분석 결과 3D grid로 렌더링하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;토이 프로젝트 진행 중, 3D 결과 값을 보여준다면 사용자에게 더 좋은 결과를 보여 줄 수 있다고 생각하여, 3D Utils의 Grid를 이용하여 사용자에게 결과를 보여주기로 하였다. Mediapipe Media pipe는 Googl&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;PoseWolrdLandmarks가 나타내는 값&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;poseWolrdLandmarks는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Mediapipe 공식 홈페이지에서 엉덩이 중심의 점으로부터 meter 단위의 3D 좌표라고 안내하고 있다. 나는 당연히 사람 기준이라고 생각을 했고, 이 값을 이용하여 사람의 포즈를 분석할 생각이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 PoseWorldLandmarks 값을 직접 확인한 결과 사람을 기준으로 좌표를 책정한 것은 맞지만, x, y, z 축의 원점이 엉덩이 중앙 점일 뿐, 공간상의 좌표는 카메라를 기준으로 하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무슨 말인지 이해를 돕기 위해 아래 사진을 보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCzi5k/btr7TkiQTMr/D9VPyjKdkUwxR8uDLx82u1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCzi5k/btr7TkiQTMr/D9VPyjKdkUwxR8uDLx82u1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCzi5k/btr7TkiQTMr/D9VPyjKdkUwxR8uDLx82u1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCzi5k%2Fbtr7TkiQTMr%2FD9VPyjKdkUwxR8uDLx82u1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;563&quot; height=&quot;598&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;598&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 그림은 요가의 전사자세로, 앞서 올린 영상의 대각선 정면 방향을 Mediapipe로 분석한 결과이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;31번 점은 왼쪽 발을, 32번 점은 오른쪽 발을 의미하는데,&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;그 말은 즉, poseWorldLandmarks가 사람이 기준이었다면, x 좌표의 차이가 없어야 한다는 말이다.&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;즉, 엉덩이 중앙 점을 원점으로 카메라의 가로가 x축, 세로가 y축, 카메라에서 먼 쪽으로 z 축이 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;31번 점을 보면, 위 그림에서 엉덩이 중앙 점보다 왼쪽에 있기 때문에 x의 값이 음수(-),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;y 값은 위로 갈수록 음수(-) 이기 때문에 양의 값이 나왔고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;z 값은 카메라에서 가까운 쪽이 음수(-)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 확연한 차이의 이해를 돕기 위해 같은 포즈를 다른 방면에서 찍은 영상을 분석해 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4vg0y/btr7Qmn37Xl/xr6pQiXtbEa32DJkw8EuBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4vg0y/btr7Qmn37Xl/xr6pQiXtbEa32DJkw8EuBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4vg0y/btr7Qmn37Xl/xr6pQiXtbEa32DJkw8EuBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4vg0y%2Fbtr7Qmn37Xl%2Fxr6pQiXtbEa32DJkw8EuBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1566&quot; height=&quot;600&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 사진의 포즈를 보면, 이 전에 분석한 사진과 같은 자세인 것을 알 수 있다.&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;이제, 분석 결과를 보자. 두 발(31,32)의 z좌표(카메라부터 얼마나 먼 지)가 비슷하고, 엉덩이를 기준으로 오른쪽에 위치한 31번 발의 x좌표가 양수임을 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;데이터 변환&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 필요로 하는 데이터는 한 자세를 여러 방향에서 찍어도 같은 값이 나오는 데이터이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 poseWorldLandmarks는 위와 같이 좌표의 기준이 앞서 설명한 것과 같기 때문에, 그대로는 사용할 수 없었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 poseWorldLandmarks 결과를 변환하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;아이디어&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 내가 원하는 데이터가 무엇인지 생각을 해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;따라서, 기존의 축 대신, 사람을 기준으로 하는 축을 써야 할 것 같다는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 축은 앞서 말했듯, 카메라를 기준으로 x, y, z 축이 잡혀있다. 그렇게 되면 사람이 카메라에서 바라보는 방향에 따라 좌표가 다르게 책정되게 된다. 하지만, 사람을 기준으로 축을 다시 세운다면, 내가 앞으로 뻗은 손은, 항상 내 앞에 있으므로 같은 결과를 줄 것이라 생각했다.&lt;/p&gt;
&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;그 이후, X축, Y축, Z 축이 될 기준에 대해 정리를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Y축 : 어깨 중앙선과 골반 중앙 선을 이은 선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;X축 : 옆구리를 이은 선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z 축 : 배꼽이 보고 있는 선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, poseWorldLandmarks를 ajax로 서버로 보내준 이후에 처리를 진행하였다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1680534975676&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; // 어깨 중앙선과 엉덩이 중앙선을 구한다
        double[] shoulderCenter = {
                (data.get(11).get(&quot;x&quot;) + data.get(12).get(&quot;x&quot;)) / 2,
                (data.get(11).get(&quot;y&quot;) + data.get(12).get(&quot;y&quot;)) / 2,
                (data.get(11).get(&quot;z&quot;) + data.get(12).get(&quot;z&quot;)) / 2};
        double[] hipCenter = {
                (data.get(23).get(&quot;x&quot;) + data.get(24).get(&quot;x&quot;)) / 2,
                (data.get(23).get(&quot;y&quot;) + data.get(24).get(&quot;y&quot;)) / 2,
                (data.get(23).get(&quot;z&quot;) + data.get(24).get(&quot;z&quot;)) / 2};

double[] yAxis = {hipCenter[0] - shoulderCenter[0], hipCenter[1] - shoulderCenter[1],
                hipCenter[2] - shoulderCenter[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;좌 어깨 : 11 / 우 어깨 : 12 / 좌 엉덩이 : 23 / 우 엉덩이 : 24&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 Y축을 구하기 위해, 어깨 중앙점과 엉덩이 중앙점을 구하고, 벡터의 차를 이용하여 Y축을 정의하였다.&lt;/p&gt;
&lt;p data-ke-size=&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_1680535392423&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  // 옆구리 중앙선을 구합니다.
        double[] leftSideCenter = {
                (data.get(11).get(&quot;x&quot;) + data.get(23).get(&quot;x&quot;)) / 2,
                (data.get(11).get(&quot;y&quot;) + data.get(23).get(&quot;y&quot;)) / 2,
                (data.get(11).get(&quot;z&quot;) + data.get(23).get(&quot;z&quot;)) / 2};
        double[] rightSideCenter = {
                (data.get(12).get(&quot;x&quot;) + data.get(24).get(&quot;x&quot;)) / 2,
                (data.get(12).get(&quot;y&quot;) + data.get(24).get(&quot;y&quot;)) / 2,
                (data.get(12).get(&quot;z&quot;) + data.get(24).get(&quot;z&quot;)) / 2};
                
                
double[] leftToRight = {rightSideCenter[0] - leftSideCenter[0], rightSideCenter[1] - leftSideCenter[1],
                rightSideCenter[2] - leftSideCenter[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;그다음, 똑같이 옆구리의 중앙 점들을 이어, X축을 구해주려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 위 옆구리들을 이은 점은 X축이 될 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면, 옆구리의 중앙 점들을 이은 점이 항상 Y축가 수직인 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 옆구리 운동을 해 보았다면 알겠지만, 옆구리는 되게 잘 늘어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680535652289&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;00:27:10 INFO 두 벡터 사이의 각 = 94.5&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;너무 간단하게만 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1297&quot; data-origin-height=&quot;1449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xKinH/btr7PpyrLg6/6tcd5tTuKOczjhnBWukUiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xKinH/btr7PpyrLg6/6tcd5tTuKOczjhnBWukUiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xKinH/btr7PpyrLg6/6tcd5tTuKOczjhnBWukUiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxKinH%2Fbtr7PpyrLg6%2F6tcd5tTuKOczjhnBWukUiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1449&quot; data-origin-width=&quot;1297&quot; data-origin-height=&quot;1449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;이를 두고 고민을 하다 보니, Z 축이 먼저 생각이 났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z 축은 이 두 벡터와 수직이기만 하면 되니까 구할 수 있지 않을까?라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1297&quot; data-origin-height=&quot;1449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Da7gB/btr7TZlobHl/yyCkHtJvfuwjXgnyT4fodk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Da7gB/btr7TZlobHl/yyCkHtJvfuwjXgnyT4fodk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Da7gB/btr7TZlobHl/yyCkHtJvfuwjXgnyT4fodk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDa7gB%2Fbtr7TZlobHl%2FyyCkHtJvfuwjXgnyT4fodk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;322&quot; height=&quot;1449&quot; data-origin-width=&quot;1297&quot; data-origin-height=&quot;1449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그렇게 되면 Z 축과 Y축을 정의할 수 있으므로 자연스럽게 X축도 구할 수 있겠다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680536070452&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double[] zAxis = crossProduct(leftToRight, yAxis);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1680536081772&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
     * 벡터의 외적
     *
     * @return : 벡터 v1,v2와 수직인 벡터
     */
    public static double[] crossProduct(double[] v1, double[] v2) {
        double[] verticalVector = new double[3];
        verticalVector[0] = v1[1] * v2[2] - v1[2] * v2[1];
        verticalVector[1] = v1[2] * v2[0] - v1[0] * v2[2];
        verticalVector[2] = v1[0] * v2[1] - v1[1] * v2[0];
        return verticalVector;
    }&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;이렇게 옆구리를 이은 선과, Y축을 이용하여 Z 축을 정의해 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1680536579527&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double[] xAxis = crossProduct(yAxis, zAxis);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음, 같은 방식으로 Z 축과 Y축의 외적을 이용하여 X축을 정의해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 새로운 축들의 크기가 다르기 때문에, 전부 단위 벡터로 변환해 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1680537874539&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
     * 벡터를 단위 벡터로 정규화 하는 함수
     *
     * @param v : 벡터
     * @return : 정규화 벡터
     */
    public double[] normalize(double[] v) {
        double[] unitVector = new double[3];
        double magnitude = vectorSize(v);

        if (magnitude &amp;gt; 0) {
            unitVector[0] = v[0] / magnitude;
            unitVector[1] = v[1] / magnitude;
            unitVector[2] = v[2] / magnitude;
        }
        return unitVector;
    }&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;그런 다음 세 개의 축이 서로 수직임을 확인했다.&lt;/p&gt;
&lt;pre id=&quot;code_1680536672113&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;00:27:10 INFO x,y 사이의 각 = 90.0
00:27:10 INFO y,z 사이의 각 = 90.0
00:27:11 INFO z,x 사이의 각 = 90.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 축에 맞게 각 벡터를 회전해 주는 작업을 해야 한다.&lt;/p&gt;
&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;pre id=&quot;code_1680538051971&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (Map&amp;lt;String, Double&amp;gt; keyPoint : data) {

    double[] point = {keyPoint.get(&quot;x&quot;),
            keyPoint.get(&quot;y&quot;), keyPoint.get(&quot;z&quot;)};

    keyPoint.put(&quot;x&quot;, dotProduct(xAxis, point));
    keyPoint.put(&quot;y&quot;, dotProduct(yAxis, point));
    keyPoint.put(&quot;z&quot;, dotProduct(zAxis, point));
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1680538084199&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * 벡터의 내적
 */
public static double dotProduct(double[] v1, double[] v2) {
    return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1597&quot; data-origin-height=&quot;865&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3W5nR/btr7Qmhhs0z/EB5q0rRUwrm1mF6Phe6ko1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3W5nR/btr7Qmhhs0z/EB5q0rRUwrm1mF6Phe6ko1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3W5nR/btr7Qmhhs0z/EB5q0rRUwrm1mF6Phe6ko1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3W5nR%2Fbtr7Qmhhs0z%2FEB5q0rRUwrm1mF6Phe6ko1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1597&quot; height=&quot;865&quot; data-origin-width=&quot;1597&quot; data-origin-height=&quot;865&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (9).gif&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;447&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xrbvO/btr7PpSJai8/oeukXDAHcfykZSkQINlyy1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xrbvO/btr7PpSJai8/oeukXDAHcfykZSkQINlyy1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xrbvO/btr7PpSJai8/oeukXDAHcfykZSkQINlyy1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/xrbvO/btr7PpSJai8/oeukXDAHcfykZSkQINlyy1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;279&quot; height=&quot;447&quot; data-filename=&quot;ezgif.com-crop (9).gif&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;447&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;만약 사람이 앞을 보길 바란다면, zAxis를 구할 때 외적 하는 벡터의 순서를 바꾸어 주도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/55</guid>
      <comments>https://olrlobt.tistory.com/55#entry55comment</comments>
      <pubDate>Tue, 4 Apr 2023 01:35:47 +0900</pubDate>
    </item>
    <item>
      <title>[Pose Estimation] Mediapipe Pose 분석 결과 3D grid로 렌더링하기</title>
      <link>https://olrlobt.tistory.com/54</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;토이 프로젝트 진행 중, 3D 결과 값을 보여준다면 사용자에게 더 좋은 결과를 보여 줄 수 있다고 생각하여, 3D Utils의 Grid를 이용하여 사용자에게 결과를 보여주기로 하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Mediapipe&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Media pipe는 Google에서 제작한 Machine Lunning Solution으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;얼굴추적, 손추적, 객체 인식과 같은 다양한 기능들을 제공&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&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;&lt;a href=&quot;https://olrlobt.tistory.com/50&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://olrlobt.tistory.com/50&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1680459454369&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;[Pose Estimation] MediaPipe Pose / 미디어 파이프로 사람 포즈 감지하기&quot; data-og-description=&quot;Media pipe Media pipe는 Google에서 제작한 Machine Lunning Solution으로 얼굴추적, 손추적, 객체 인식과 같은 다양한 기능들을 제공한다. Media pipe에서 제공하는 기능들은 아래에서 확인 가능하며, 자세히는 &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/50&quot; data-og-url=&quot;https://olrlobt.tistory.com/50&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/br5X4s/hyR7yKEph6/1cBmUSLYYfNdikmW5AC5u1/img.gif?width=600&amp;amp;height=509&amp;amp;face=0_0_600_509,https://scrap.kakaocdn.net/dn/cMHohI/hyR7wTBEDY/MBYwNGR4MTjP83VGUxHEfK/img.gif?width=600&amp;amp;height=509&amp;amp;face=0_0_600_509,https://scrap.kakaocdn.net/dn/mIiwi/hyR9CklpzE/VKELKk7GmGMdoK5RfOJF90/img.png?width=1177&amp;amp;height=1026&amp;amp;face=0_0_1177_1026&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/50&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/50&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/br5X4s/hyR7yKEph6/1cBmUSLYYfNdikmW5AC5u1/img.gif?width=600&amp;amp;height=509&amp;amp;face=0_0_600_509,https://scrap.kakaocdn.net/dn/cMHohI/hyR7wTBEDY/MBYwNGR4MTjP83VGUxHEfK/img.gif?width=600&amp;amp;height=509&amp;amp;face=0_0_600_509,https://scrap.kakaocdn.net/dn/mIiwi/hyR9CklpzE/VKELKk7GmGMdoK5RfOJF90/img.png?width=1177&amp;amp;height=1026&amp;amp;face=0_0_1177_1026');&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;[Pose Estimation] MediaPipe Pose / 미디어 파이프로 사람 포즈 감지하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Media pipe Media pipe는 Google에서 제작한 Machine Lunning Solution으로 얼굴추적, 손추적, 객체 인식과 같은 다양한 기능들을 제공한다. Media pipe에서 제공하는 기능들은 아래에서 확인 가능하며, 자세히는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Control_utils_3d&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전 포스팅에서 사용했던, camera_utils, drawing_utils과는 별도의 util 라이브러리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;control_utils_3d는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;미디어 파이프 결과를 3D 모델로 만들어 출력하는 것에 도움을 주는 라이브러리&lt;/b&gt;&lt;/span&gt;이다. WebGL을 사용하기 때문에 브라우저에서만 동작을 하며, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;3D 모델링, 3D 렌더링, 사용자 인터랙션&lt;/b&gt;&lt;/span&gt;과 같은 기능들을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽지만 현재는 Mediapipe에서 제공된 문서는 없는 것으로 보이며, 찾을 수 있는 문서는 아래에 링크해 둔, 패키지 다운로드 사이트 밖에 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Control_utils_3d npmjs site link :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/@mediapipe/control_utils_3d&quot;&gt;https://www.npmjs.com/package/@mediapipe/control_utils_3d&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;오늘은 이 Control_utils_3d 라이브러리를 이용하여 Mediapipe의 Pose Detection 결과를 3D grid로 출력해 보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3D grid 그리기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Grid에 3D로 분석 결과를 그려주기 위해서는 당연한 이야기지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;media pipe의 Pose Detection 분석이 선행되어야 한다.&amp;nbsp;&lt;/p&gt;
&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;포즈 분석을 하게 되면, 다음과 같은 결과가 분석 결과로 제공된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uKSRu/btr7NPLppj8/JCRwlRllzB5JNZXKizDKx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uKSRu/btr7NPLppj8/JCRwlRllzB5JNZXKizDKx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uKSRu/btr7NPLppj8/JCRwlRllzB5JNZXKizDKx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuKSRu%2Fbtr7NPLppj8%2FJCRwlRllzB5JNZXKizDKx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;970&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;970&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전 포스팅에서 Canvas에 이미지를 기준으로 분석한 결과는 poseLandmarks로 출력이 된다고 했다. 만약 poseLandmarks로 3D 렌더링을 수행하게 될 경우에, 사람의 위치가 이미지를 기준으로 좌표가 책정이 되기 때문에 일관된 결과를 얻을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 3D grid를 생성하기 위해서는 poseWorldLandmarks 결과를 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poseWorldLandmarks는 3D 공간에서 추출한 랜드마크로, 이미지를 기준으로 하지 않고 감지된 사람의 엉덩이 사이의 가상의 점을 기준으로 좌표값을 반환한다. 따라서 이 결과로 3D 렌더링을 하게 될 경우, 카메라의 위치에 상관없이 일정한 값을 얻게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;LandmarkGrid 객체 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;control_utils_3d를 사용하기 위해 라이브러리를 가져온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html에서 다음 cdn 구문을 이용하여 쉽게 control_utils_3d 라이브러리 사용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;html :&lt;/h4&gt;
&lt;pre id=&quot;code_1680519611874&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@mediapipe/control_utils_3d/control_utils_3d.js&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;div class= &quot;landmark_grid_container&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;grid가 생성될 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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;js :&lt;/h4&gt;
&lt;pre id=&quot;code_1680521576130&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const landmarkContainer = document.getElementsByClassName('landmark_grid_container')[0];
const grid = new LandmarkGrid(landmarkContainer, gridOption);
    
const gridOption = {
    connectionColor: 0xCCCCCC, 		// 연결 라인의 색상
    definedColors: [ 			// 각 랜드마크의 색상
        {name: 'LEFT', value: 0xFF0000},
        {name: 'RIGHT', value: 0x0000FF},
        {name: 'LEFTCONNECTIONS', value: 0x75fbfd},
        {name: 'RIGHTCONNECTIONS', value: 0x00FFAA}],

    range: 1,		// 그리드의 범위
    fitToGrid: true,	// 그리드를 landmarkContainer에 꽉 차게 그릴 지
    labelSuffix: 'm',	// 그리드 라벨 접미사
    landmarkSize: 2,	// 랜드마크 크기
    numCellsPerAxis: 2,	// 각 축을 몇개의 셀로 나눌지
    showHidden: false,	// 숨겨진 랜드마크 표시 여부
    centered: true,	// 그리드가 landmarkContainer 중앙에 위치할 지
}&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;js에서 LanmarkGrid 객체를 생성해 준다. 첫 번째 인자로는 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;옵션에 적어놓은 주석을 참고하여 원하는 설정을 해주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;LandmarkGrid 그리기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680522110681&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pose.onResults(drawSkeleton); // 포즈 분석 콜백함수 drawSkeleton()

function drawSkeleton(results) {
	/*
    	* 이 전에 작성한 poseLandmarks 그린 코드 
         */


	if (results.poseWorldLandmarks) {
            grid.updateLandmarks(results.poseWorldLandmarks, [
                    {list: leftConnections, color: 'LEFTCONNECTIONS'},
                    {list: rightConnections, color: 'RIGHTCONNECTIONS'},
                    {list: centerConnections, color: '0xEEEEEE'}]
                , [
                    {list: leftIndices, color: 'LEFT'},
                    {list: rightIndices, color: 'RIGHT'}
                ]);

        } else {
            grid.updateLandmarks([]);
        }
}&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;poseWorldLandmarks 결과가 있을 때만 실행되게 if문을 설정해 주었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LandmarkGrid의 updateLandmarks()를 이용하여 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; updateLandmarks( 랜드마크, 연결 선 색상, 연결 점 색상 )이고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결 선 색상과 연결 점 색상은 배열의 형태로 list 속성과 color 속성을 지정해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 color 속성의 &quot;LEFTCONNECTIONS&quot;는 앞서 설정한 gridOption의 definedColor 속성의 name과 매칭된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (7).gif&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AAi7K/btr7Rro5fgY/Pkke6KVu9T8QEh5xHEYRO0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AAi7K/btr7Rro5fgY/Pkke6KVu9T8QEh5xHEYRO0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AAi7K/btr7Rro5fgY/Pkke6KVu9T8QEh5xHEYRO0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/AAi7K/btr7Rro5fgY/Pkke6KVu9T8QEh5xHEYRO0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;295&quot; height=&quot;505&quot; data-filename=&quot;ezgif.com-crop (7).gif&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3D 예상 값을 잘 추정하여, Grid에 잘 나타내주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 아래 작은 일시정지 버튼을 이용하여 회전을 멈출 수 있고, 마우스 드래그로 좌, 우 회전 또한 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/54</guid>
      <comments>https://olrlobt.tistory.com/54#entry54comment</comments>
      <pubDate>Mon, 3 Apr 2023 23:45:44 +0900</pubDate>
    </item>
    <item>
      <title>[Spring boot] FFmpeg로 영상 배속 설정하기</title>
      <link>https://olrlobt.tistory.com/53</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FFmpeg&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FFmpeg는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&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: #f6e199;&quot;&gt;&lt;b&gt;커멘드 입력으로 동작&lt;/b&gt;&lt;/span&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, FFmpeg는 다양한 운영체제에서, 다양한 프로그래밍 언어로 사용할 수 있는 라이브러리로 제공되어, 수많은 개발자와 사용자 모두에게 유용한 기능을 많이 제공해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 FFmpeg를 이용하여 &lt;b&gt;Spring boot&lt;/b&gt; 프로젝트에서,&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;공식 Github :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/FFmpeg/FFmpeg&quot;&gt;https://github.com/FFmpeg/FFmpeg&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FFmpeg 설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 스프링 부트에서 FFmpeg를 사용하기 위해서는 서버에 설치하는 방법과 API를 이용하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 API를 이용하여 간단하게 해결하면 좋겠지만, FFmpeg는 C언어로 구현이 되어 있기 때문에 JAVA에서 사용하기 위해서는 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;JNI(Java Native Interface)를 이용하여 JAVA에서 C언어의 함수를 호출할 수 있도록 해야 한다.&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPqNES/btr64k5eKfJ/TGVB2PTE8Zai9vBtvzKKJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPqNES/btr64k5eKfJ/TGVB2PTE8Zai9vBtvzKKJK/img.png&quot; data-alt=&quot;FFmpeg 구성언어 비율&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPqNES/btr64k5eKfJ/TGVB2PTE8Zai9vBtvzKKJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPqNES%2Fbtr64k5eKfJ%2FTGVB2PTE8Zai9vBtvzKKJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;332&quot; height=&quot;164&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FFmpeg 구성언어 비율&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JNI를 이용하는 방법은 꽤나 복잡하고 어렵기 때문에, 나는 비교적 간단한 서버 설치 방법을 이용하기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;다운로드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FFmpeg 공식 다운로드 사이트 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://ffmpeg.org/download.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ffmpeg.org/download.html&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1680198270806&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;Download FFmpeg&quot; data-og-description=&quot;If you find FFmpeg useful, you are welcome to contribute by donating. More downloading options Git Repositories Since FFmpeg is developed with Git, multiple repositories from developers and groups of developers are available. Release Verification All FFmpe&quot; data-og-host=&quot;ffmpeg.org&quot; data-og-source-url=&quot;https://ffmpeg.org/download.html&quot; data-og-url=&quot;https://ffmpeg.org/download.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ffmpeg.org/download.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ffmpeg.org/download.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;Download FFmpeg&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;If you find FFmpeg useful, you are welcome to contribute by donating. More downloading options Git Repositories Since FFmpeg is developed with Git, multiple repositories from developers and groups of developers are available. Release Verification All FFmpe&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ffmpeg.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;위 링크로 접속하여 다운로드를 진행한다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;1612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZKQmc/btr60gprlZd/FUNgVaKq069kDRKjrK85uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZKQmc/btr60gprlZd/FUNgVaKq069kDRKjrK85uk/img.png&quot; data-alt=&quot;FFmpeg 다운로드 페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZKQmc/btr60gprlZd/FUNgVaKq069kDRKjrK85uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZKQmc%2Fbtr60gprlZd%2FFUNgVaKq069kDRKjrK85uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;613&quot; height=&quot;1612&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;1612&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FFmpeg 다운로드 페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자의 운영 체제에 맞게 선택하면 되고, 나는 Window를 사용 중이기 때문에 윈도 버전을 다운로드하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨갛게 하이라이트 친 부분을 눌러 페이지를 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;가장 마지막 빌드 버전에서, 본인이 원하는 버전을 눌러 다운로드한다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;1247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lmkra/btr65osJUJU/naS55c3TcZIcV3A9UERj0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lmkra/btr65osJUJU/naS55c3TcZIcV3A9UERj0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lmkra/btr65osJUJU/naS55c3TcZIcV3A9UERj0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flmkra%2Fbtr65osJUJU%2FnaS55c3TcZIcV3A9UERj0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;589&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;1247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;full 버전의 경우, 다양한 기능들이 무수히 많이 들어 가 있지만 무거우며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;essentials 버전의 경우, 중요한 기능들 위주로 들어 가 있지만 가벼운 편이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인이 구현하고자 하는 기능이 essentials로 구현이 가능하다면, essentials로 받는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로컬 디스크(C 또는 D)에 압축을 풀어준다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8Pm0p/btr61mbEYTr/DeMQLvzKptXiKFfKP6cLFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8Pm0p/btr61mbEYTr/DeMQLvzKptXiKFfKP6cLFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8Pm0p/btr61mbEYTr/DeMQLvzKptXiKFfKP6cLFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8Pm0p%2Fbtr61mbEYTr%2FDeMQLvzKptXiKFfKP6cLFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;269&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;환경변수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드를 완료했으면 프로젝트에서 사용하기 편하도록 환경변수를 등록해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수를 등록하지 않더라도, 사용은 가능하지만 매 번 경로를 입력해 주어야 하므로 상당히 번거로울 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;윈도에 &quot;시스템 환경 변수 편집&quot; 검색&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;299&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf31qX/btr6YQR9W0L/93vfFnFqdXH76m3DWhyzak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf31qX/btr6YQR9W0L/93vfFnFqdXH76m3DWhyzak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf31qX/btr6YQR9W0L/93vfFnFqdXH76m3DWhyzak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf31qX%2Fbtr6YQR9W0L%2F93vfFnFqdXH76m3DWhyzak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;299&quot; height=&quot;96&quot; data-origin-width=&quot;299&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&quot;고급&quot;에서 &quot;환경 변수&quot;를 눌러 &quot;Path&quot;를 찾는다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8KJrj/btr6YP6IsOP/QcyChvF9NpgOjxfHxi8Qyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8KJrj/btr6YP6IsOP/QcyChvF9NpgOjxfHxi8Qyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8KJrj/btr6YP6IsOP/QcyChvF9NpgOjxfHxi8Qyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8KJrj%2Fbtr6YP6IsOP%2FQcyChvF9NpgOjxfHxi8Qyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;938&quot; height=&quot;532&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&quot;편집&quot;을 눌러 FFmpeg가 설치된 경로를 bin까지 등록한다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;521&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bga6LL/btr61SPbZiH/iq8MIY37DCmrU4fpy59hBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bga6LL/btr61SPbZiH/iq8MIY37DCmrU4fpy59hBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bga6LL/btr61SPbZiH/iq8MIY37DCmrU4fpy59hBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbga6LL%2Fbtr61SPbZiH%2Fiq8MIY37DCmrU4fpy59hBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;407&quot; data-origin-width=&quot;521&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 FFmpeg 설치가 완료되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, 커멘드로 FFmpeg 사용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Cmd를 열어 &quot;ffmpeg&quot;를 눌러 등록이 잘 되었는지 확인한다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biKCRq/btr65oM2X38/pW46qqFopKxaUqWfEAkk3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biKCRq/btr65oM2X38/pW46qqFopKxaUqWfEAkk3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biKCRq/btr65oM2X38/pW46qqFopKxaUqWfEAkk3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiKCRq%2Fbtr65oM2X38%2FpW46qqFopKxaUqWfEAkk3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1110&quot; height=&quot;666&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 나온다면, 정상적으로 등록이 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;동영상 배속하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot에서 FFmpeg를 사용하기 위해서는 먼저, application.properties에 경로를 등록해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;application.properties&lt;/h4&gt;
&lt;pre id=&quot;code_1680200697101&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;## ffmpeg 설정

ffmpeg.location=C:/ffmpeg/bin/ffmpeg.exe
ffprobe.location=C:/ffmpeg/bin/ffprobe.exe&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;본인 경로에 맞게 등록을 해주었으면 Class를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;FFmpeg 경로 가져오기&lt;/h4&gt;
&lt;pre id=&quot;code_1680201167920&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Value(&quot;${ffmpeg.location}&quot;)
private String ffmpegPath;
@Value(&quot;${ffprobe.location}&quot;)
private String ffprobePath;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, application.properties에 등록한 경로를 받아오기 위해 @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;h4 data-ke-size=&quot;size20&quot;&gt;파일 경로 처리&lt;/h4&gt;
&lt;pre id=&quot;code_1680201268440&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MultipartFile file

String fileName = file.getOriginalFilename();
String filePath = &quot;src/main/webapp/resources/upload/&quot; + UUID.randomUUID().toString() + fileName;
String outPath = &quot;src/main/webapp/resources/upload/&quot; + UUID.randomUUID().toString() +fileName;

File temp = new File(filePath).getCanonicalFile(); //getCanonicalFile로 정적인 경로를 생성
if(!temp.exists()) {
    temp.createNewFile();
}
file.transferTo(temp); // 파일 변경&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;filePath는 클라이언트에서 올린 경로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outPath는 2배속 영상이 저장될 경로이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서 올린 경로 filePath를 따로 설정해 주는 이유는 클라이언트에서 올린 영상이 blob으로 로딩처리가 되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Up4I/btr6YRjbAPO/C7EkNjtDpDeSukV7f7cyk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Up4I/btr6YRjbAPO/C7EkNjtDpDeSukV7f7cyk0/img.png&quot; data-alt=&quot;blob으로 로딩된 영상 경로&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Up4I/btr6YRjbAPO/C7EkNjtDpDeSukV7f7cyk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Up4I%2Fbtr6YRjbAPO%2FC7EkNjtDpDeSukV7f7cyk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;105&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;blob으로 로딩된 영상 경로&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 말해, Blob 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;이 말은 즉, 브라우저에서 생성된 경로이기 때문에, 서버에서 접근이 불가능한 경로라는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, filePath를 새롭게 생성해 주고 transferTo()를 이용하여 파일을 복사해 준 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;영상 배속&lt;/h4&gt;
&lt;pre id=&quot;code_1680202297460&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FFprobe ffprobe = new FFprobe(ffprobePath); // window에 설치된 ffprobe.exe 경로

FFmpegBuilder builder = new FFmpegBuilder().setInput(filePath) // 입력 파일 경로
        .addOutput(outPath) // 출력 파일 경로
        .setFormat(&quot;mp4&quot;) // 출력 파일 포맷
        .disableSubtitle() // 자막 끄기
        .setVideoCodec(&quot;libx264&quot;) // 비디오 코덱 설정
        .setAudioCodec(&quot;aac&quot;) // 오디오 코덱 설정
        .setVideoFrameRate(30) // 비디오 프레임 레이트 설정
        .setVideoFilter(&quot;setpts=&quot; + (1.0 / speed) + &quot;*PTS&quot;) // 비디오 속도 조절
        .done();

// FFmpeg 실행
FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(ffmpegPath), new FFprobe(ffprobePath));
executor.createJob(builder).run();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FFmpeg는 커멘드 명령어로 실행이 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FFmpegBuilder는 이 명령어를 생성하기 위한 빌더 클래스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. addOutput() 경로에 출력 파일의 경로가 들어가며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. setVideoFilter()에서 speed 값에 의해 비디오의 속도가 조절된다. 예를 들어 speed가 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;명령어 생성이 끝났다면, 명령어를 실행하기 위하여 FFmpegExecutor를 생성해 주고, run()으로 실행해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결과&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop.gif&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTYvL4/btr64mosjwi/5JDD2bbOD6kS4GuNWYkbYk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTYvL4/btr64mosjwi/5JDD2bbOD6kS4GuNWYkbYk/img.gif&quot; data-alt=&quot;1배속 영상(좌) / 2배속 영상(우)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTYvL4/btr64mosjwi/5JDD2bbOD6kS4GuNWYkbYk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bTYvL4/btr64mosjwi/5JDD2bbOD6kS4GuNWYkbYk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;404&quot; height=&quot;344&quot; data-filename=&quot;ezgif.com-crop.gif&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1배속 영상(좌) / 2배속 영상(우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;영상 길이 줄이기&lt;/h4&gt;
&lt;pre id=&quot;code_1680204128239&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FFprobe ffprobe = new FFprobe(ffprobePath);

FFmpegProbeResult probeResult = ffprobe.probe(filePath);  // FFmpegProbeResult 객체 생성
double second = probeResult.getFormat().duration;  // 영상의 총 시간

FFmpegBuilder builder = new FFmpegBuilder().setInput(filePath) 
        .addOutput(outPath) 
        .setFormat(&quot;mp4&quot;)
        .disableSubtitle()
        .setVideoCodec(&quot;libx264&quot;) 
        .setAudioCodec(&quot;aac&quot;)
        .setVideoFrameRate(30) 
        .setVideoFilter(&quot;setpts=&quot; + (1.0 / speed) + &quot;*PTS&quot;) 
        .setDuration((long)(second*1000/speed), TimeUnit.MILLISECONDS) // 영상 길이 설정
        .done();

FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(ffmpegPath), ffprobe);
executor.createJob(builder).run();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FFmpeg에는 동영상의 재생시간을 출력하는 기능도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 그 기능을 사용하여 Duration을 새로 설정해 주는 방식으로 문제를 해결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;전체코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1680201109314&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
public class VideoFileUtils {

	@Value(&quot;${ffmpeg.location}&quot;)
	private String ffmpegPath;
	@Value(&quot;${ffprobe.location}&quot;)
	private String ffprobePath;

	/**
	 * 비디오 파일을 배속해서 저장하는 함수
	 * @param file : 파일
	 * @param speed : 배속
	 * @return 
	 */
	public String changePlaybackRate(MultipartFile file, double speed) throws IOException, InterruptedException {
		String fileName = file.getOriginalFilename();
		
		String filePath = &quot;src/main/webapp/resources/upload/&quot; + UUID.randomUUID().toString() + fileName;
		String outPath = &quot;src/main/webapp/resources/upload/&quot; + UUID.randomUUID().toString() +fileName;
		
		File temp = new File(filePath).getCanonicalFile();
		if(!temp.exists()) {
			temp.createNewFile();
		}
		file.transferTo(temp);

		FFprobe ffprobe = new FFprobe(ffprobePath); // window에 설치된 ffprobe.exe 경로
		FFmpegProbeResult probeResult = ffprobe.probe(filePath); // 동영상 경로
		double second = probeResult.getFormat().duration;
		
		FFmpegBuilder builder = new FFmpegBuilder().setInput(filePath) // 입력 파일 경로
				.addOutput(outPath) // 출력 파일 경로
				.setFormat(&quot;mp4&quot;) // 출력 파일 포맷
				.disableSubtitle() // 자막 끄기
				.setVideoCodec(&quot;libx264&quot;) // 비디오 코덱 설정
				.setAudioCodec(&quot;aac&quot;) // 오디오 코덱 설정
				.setVideoFrameRate(30) // 비디오 프레임 레이트 설정
				.setVideoFilter(&quot;setpts=&quot; + (1.0 / speed) + &quot;*PTS&quot;) // 비디오 속도 조절
				.setDuration((long)(second*1000/speed), TimeUnit.MILLISECONDS)
				.done();

		// FFmpeg 실행
		FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(ffmpegPath), new FFprobe(ffprobePath));
		executor.createJob(builder).run();

		new File(filePath).delete();
		return outPath;
	}

}&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;h4 data-ke-size=&quot;size20&quot;&gt;결과&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (1).gif&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGCBm/btr6YQxQSdW/d55eW1df4lafvPu80gZcyK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGCBm/btr6YQxQSdW/d55eW1df4lafvPu80gZcyK/img.gif&quot; data-alt=&quot;1배속 영상(좌) / 2배속 영상(우)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGCBm/btr6YQxQSdW/d55eW1df4lafvPu80gZcyK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/yGCBm/btr6YQxQSdW/d55eW1df4lafvPu80gZcyK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;315&quot; data-filename=&quot;ezgif.com-crop (1).gif&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1배속 영상(좌) / 2배속 영상(우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간에 맞게 잘 종료되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/53</guid>
      <comments>https://olrlobt.tistory.com/53#entry53comment</comments>
      <pubDate>Fri, 31 Mar 2023 04:46:22 +0900</pubDate>
    </item>
    <item>
      <title>[Spring boot] @Value 어노테이션으로 application.properties의 값 가져오기</title>
      <link>https://olrlobt.tistory.com/52</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@Value&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot에서 제공하는 어노테이션 중 하나로, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;빈(Bean) 생성 시점에 값을 주입&lt;/b&gt;&lt;/span&gt;하기 위해 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 개발하면서 application.properties에 설정한 값들을 가져오기 위하여 사용되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하는 동안 변경이 자주 되는 경로와 같은 값들을 @Value로 가져와서 사용하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;사용법&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. application.properties 작성&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1679914341264&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;## 서버 포트
server.port=80


## 임의 경로 변수
ffmpeg.location=C:/ffmpeg/bin/ffmpeg.exe
ffprobe.location=C:/ffmpeg/bin/ffprobe.exe

## 임의 문자 변수
ffmpeg.text.hh=hi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서의 주요 설정, 파일경로와 같은 것들은 application.properties에서 위처럼&amp;nbsp; &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;&lt;b&gt;Key = Value&amp;nbsp; 형식&lt;/b&gt;&lt;/span&gt;으로 관리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 예시로 서버포트와 경로변수, 문자변수를 작성해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Class에 Bean 주입&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Value는 앞서 설명한 바와 같이, 빈(Bean) 생성 시점에 값을 주입하기 위해서 사용된다. 따라서 사용할 Class에 Bean 주입은 필수 적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot에서 Bean을 주입할 수 있는 대표적인 어노테이션은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Component: 스프링의 기본적인 빈 생성 어노테이션으로, 빈을 생성할 클래스에 붙인다.&lt;/li&gt;
&lt;li&gt;@Service:&amp;nbsp; 비즈니스 로직이 포함된 클래스에 붙인다.&lt;/li&gt;
&lt;li&gt;@Repository:&amp;nbsp; 데이터베이스와 관련된 작업을 수행하는 클래스에 붙인다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 &lt;span style=&quot;color: #374151;&quot;&gt;@Controller, @RestController, @Configuration를 이용하여 주입할 수 있다.&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 data-ke-size=&quot;size16&quot;&gt;또한 클래스 생성 시 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;new&lt;/b&gt;&lt;/span&gt;로 생성하게 될 경우, 스프링 컨테이너에서 관리되지 않기 때문에 @Value값이 지정되지 않는다. 따라서 &lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;@Autowired&lt;/span&gt; &lt;span style=&quot;color: #009a87;&quot;&gt;어노테이션을 이용&lt;/span&gt;&lt;/b&gt;하여, 다른 Bean과의 의존성을 자동으로 주입해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1679915295233&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 잘못된 선언 new
VideoFileUtils videoFileUtils = new VideoFileUtils();
		
videoFileUtils.media_player_time(file);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1679915476628&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 의존성 자동 주입
@Autowired
private VideoFileUtils videoFileUtils;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. @Value로 변수 값 주입&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1679915827781&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Value(&quot;${server.port}&quot;)
private String port;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Value 어노테이션에서 값을 받아올 때는 위와 같이 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.properties에서 작성한 변수 부분을 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;&quot;${}&quot;&lt;/b&gt;&lt;/span&gt;로 감싸서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 주의할 사항은 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;static 변수에는 주입이 되지 않는다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4OYeP/btr6loO1MxX/0CzX6Y7fV7KZkFxUHcanf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4OYeP/btr6loO1MxX/0CzX6Y7fV7KZkFxUHcanf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4OYeP/btr6loO1MxX/0CzX6Y7fV7KZkFxUHcanf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4OYeP%2Fbtr6loO1MxX%2F0CzX6Y7fV7KZkFxUHcanf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;382&quot; height=&quot;258&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시를 보면, 앞서 설정한 경로와 텍스트, 서버 포트가 잘 주입이 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, ffprobePath의 경우 static으로 선언이 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n6i4w/btr590afFCs/KjqFUZzIdUECC4ZHZloiD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n6i4w/btr590afFCs/KjqFUZzIdUECC4ZHZloiD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n6i4w/btr590afFCs/KjqFUZzIdUECC4ZHZloiD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn6i4w%2Fbtr590afFCs%2FKjqFUZzIdUECC4ZHZloiD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;411&quot; height=&quot;155&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static으로 선언한 값을 제외하고는 정상적으로 출력되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;@ConfigurationProperties&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #374151;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;@ConfigurationProperties 어노테이션을 이용하면, application.properties 파일에서 정의한 속성값들을 자바 클래스에 매핑하여 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;application.properties&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1679916478147&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;myapp.service.name=My Service&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MyProperties.java&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1679916539206&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@ConfigurationProperties(prefix = &quot;myapp&quot;)
public class MyProperties {

  private String serviceName;

  public String getServiceName() {
    return serviceName;
  }

  public void setServiceName(String serviceName) {
    this.serviceName = serviceName;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 &lt;span style=&quot;color: #000000;&quot;&gt;application.properties&lt;/span&gt;에 선언한 key의 prefix를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyProperties 클래스에서 &lt;span style=&quot;color: #000000;&quot;&gt;@ConfigurationProperties 어노테이션을 이용하여 매핑해 주고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서, getServiceName() 메서드를 호출하게 되면 &lt;span style=&quot;color: #000000;&quot;&gt;application.properties에 정의한 값의 Value를 가져오게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1679916738175&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;My Service&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;</description>
      <category>Spring/Spring</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/52</guid>
      <comments>https://olrlobt.tistory.com/52#entry52comment</comments>
      <pubDate>Mon, 27 Mar 2023 20:32:53 +0900</pubDate>
    </item>
    <item>
      <title>[Pose Estimation] YOLOv5, MediaPipe로 Multi Pose 구현 시도해보기</title>
      <link>https://olrlobt.tistory.com/51</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Media pipe&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Media pipe는 Google에서 제작한 Machine Lunning Solution으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;얼굴추적, 손추적, 객체 인식과 같은 다양한 기능들을 제공&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;또한, 사람의 자세 인식 Pose 추적이 가능하지만 &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;오직 한 사람의 Pose를 추정하는 기능을 제공&lt;/b&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;한 사람의 Pose를 추적하는 코드를 구현하기를 원한다면, 이 전 게시글인 아래의 포스팅을 확인하길 바란다.&lt;/p&gt;
&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://olrlobt.tistory.com/50&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://olrlobt.tistory.com/50&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679326816533&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;[Pose Estimation] MediaPipe Pose / 미디어 파이프로 사람 자세 인식 JavaScript 구현하기&quot; data-og-description=&quot;Media pipe Media pipe는 Google에서 제작한 Machine Lunning Solution으로 얼굴추적, 손추적, 객체 인식과 같은 다양한 기능들을 제공한다. Media pipe에서 제공하는 기능들은 아래에서 확인 가능하며, 자세히는 &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/50&quot; data-og-url=&quot;https://olrlobt.tistory.com/50&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/x1Gdh/hyR0o0XLYt/sM4hhaKykiQDFjMHkE2NP0/img.gif?width=600&amp;amp;height=509&amp;amp;face=0_0_600_509,https://scrap.kakaocdn.net/dn/cAF7hA/hyR0k5kxSo/MvddO9VDFnLD2MyTO4tcn0/img.png?width=1177&amp;amp;height=1026&amp;amp;face=0_0_1177_1026&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/50&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/50&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/x1Gdh/hyR0o0XLYt/sM4hhaKykiQDFjMHkE2NP0/img.gif?width=600&amp;amp;height=509&amp;amp;face=0_0_600_509,https://scrap.kakaocdn.net/dn/cAF7hA/hyR0k5kxSo/MvddO9VDFnLD2MyTO4tcn0/img.png?width=1177&amp;amp;height=1026&amp;amp;face=0_0_1177_1026');&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;[Pose Estimation] MediaPipe Pose / 미디어 파이프로 사람 자세 인식 JavaScript 구현하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Media pipe Media pipe는 Google에서 제작한 Machine Lunning Solution으로 얼굴추적, 손추적, 객체 인식과 같은 다양한 기능들을 제공한다. Media pipe에서 제공하는 기능들은 아래에서 확인 가능하며, 자세히는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MediaPipe Multi Pose&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 설명처럼, Media pipe는 오직 한 사람의 Pose 추적만 가능하다. Open Pose와 Pose Net의 경우, 멀티 포즈를 기본적으로 제공하는 것으로 알고 있다. 그럼에도 Media pipe로 시도하려는 이유는 3D 좌표 지원과 비교적 많은 자료의 양. 그리고 Media pipe 자체가 Pose Net의 상위 버전으로 알고 있는데, Pose Net만 멀티포즈를 지원한다니 왜 안 되는지 의문을 갖게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 해외 포스팅들을 찾아보면, Media pipe로 Multi Pose를 구현했다는 글들이 가끔 보이기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 도전해 보고 싶어졌다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;아이디어&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 아이디어는 Media pipe에서 아주 우수한 성능으로 잘 작동하는 Single Pose를, 각 사람 별로 수행하기만 하면 Multi Pose가 되는 것이지 않나? 에서 시작되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ML Solution 중, 객체 탐지기를 이용하여 각 사람 별로 BoundingBox를 만들고, 이를 Pose를 통하여 전송 비교하면 될 것이라 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pose 기능이 MediaPipe를 이용하기 때문에, MediaPipe에서 제공하는 객체 탐지기인 Objectron을 사용하면, 쉽게 연동되어 구현이 될 것이라 생각했지만, Objectron의 경우 학습된 물체의 가짓수가 제한되어 있고, 사람은 학습이 되어있지 않아, 사용자가 직접 학습시켜야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 나는 Objectron이 아닌 &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Tensor Flow의&lt;/span&gt; 객체 탐지를 이용하기로 하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;COCO-SSD &lt;/span&gt;사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;COCO-SSD는 객체 감지 (Object Detection) 알고리즘 중 하나로, Tensor Flow.js 라이브러리를 사용하여 웹 브라우저에서 객체 감지를 수행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tensor Flow는 Google에서 개발한 오픈소스 ML 학습 라이브러리로 다양한 러닝 모델을 구축하고 학습하는 데 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tensor Flow의 객체 탐색이 여러 명이 되는지 이미지로 간단히 테스트해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&amp;nbsp;이미지 :&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;529&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQEsCg/btr45lGL5Ot/C6QPYE2H1Os7dbShNLfRq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQEsCg/btr45lGL5Ot/C6QPYE2H1Os7dbShNLfRq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQEsCg/btr45lGL5Ot/C6QPYE2H1Os7dbShNLfRq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQEsCg%2Fbtr45lGL5Ot%2FC6QPYE2H1Os7dbShNLfRq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1106&quot; height=&quot;529&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;529&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 바운딩 박스(bbox)가 나오는 것을 확인했으며, 가장 좌측 사람의 경우 score가 낮아 감지되지 않은 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 되고, 결과 값의 bbox 역시 잘 출력되는 것을 확인해서, 바로 비디오로 넘어갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;비디오 :&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif (5).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ3ZIV/btr5sb2VwRn/HU3N6DjJ9CIL2rLkwKDDJK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ3ZIV/btr5sb2VwRn/HU3N6DjJ9CIL2rLkwKDDJK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ3ZIV/btr5sb2VwRn/HU3N6DjJ9CIL2rLkwKDDJK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bJ3ZIV/btr5sb2VwRn/HU3N6DjJ9CIL2rLkwKDDJK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;405&quot; height=&quot;462&quot; data-filename=&quot;ezgif.com-video-to-gif (5).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;684&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;내가 수행하려는 기능은 bbox 자체가 아닌, bbox 별로 사람의 포즈를 탐색하는 것이기 때문에 높은 정확도와 빠른 속도를 요구하는 bbox 탐지 기술이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Tensor Flow의 경우, 눈으로 보아도 초당 2회 정도 측정 되는 것으로 보이고, 팔이 bbox를 빠져나오는 경우가 허다하게 발생하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면, bbox별로 객체를 분리해 내더라도 신체의 일부가 잘리게 됨으로 pose 측정에서 정확한 값을 얻을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;YOLOv5 &lt;/span&gt;사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;YOLOv5는 객체 감지 (Object Detection) 알고리즘 중 하나로, &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;YOLO (You Only Look Once) 알고리즘의 다섯 번째 버전이다. 현재는 YOLOv7까지 나온 것으로 알고 있지만, 7 버전을 사용하기 위해선 모델을 JS로 변환하고 학습시키는 과정을 따로 진행해야 하기 때문에, JS버전이 오픈되어 있는 v5를 사용하였다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&amp;nbsp;비디오 :&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif (8).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yfIJx/btr5sgXo7ij/NglY8nlyXqXYzdvnLViyWk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yfIJx/btr5sgXo7ij/NglY8nlyXqXYzdvnLViyWk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yfIJx/btr5sgXo7ij/NglY8nlyXqXYzdvnLViyWk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/yfIJx/btr5sgXo7ij/NglY8nlyXqXYzdvnLViyWk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;413&quot; height=&quot;292&quot; data-filename=&quot;ezgif.com-video-to-gif (8).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLOv5를 사용하여 객체 탐지를 진행하였다. 보이는 것과 같이 COCO-SSD 보다 속도와 정확성 면에서 월등히 우수한 면을 보여준다. 이 정도 속도로 탐지만 가능하더라도, Pose 측정에는 무리가 없을 것으로 판단하여, YOLOv5로 멀티포즈를 시도해 보기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;멀티포즈 구현&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이미지 :&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비디오로 멀티포즈를 구현하기에 앞서, 내가 생각한 방법이 안 될 수도 있다고 생각했기 때문에, 이미지로 먼저 실험을 진행해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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. 먼저 YOLOv5에서 구분된 객체에서 Person만을 찾아낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이 Person 객체를 BBox 별로 나누어 Canvas에 이미지화시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 이미지화시킨 Canvas를 Pose 분석을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XA67q/btr5ozxbndx/9hcxoGTjwpzjqQTzCt3kik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XA67q/btr5ozxbndx/9hcxoGTjwpzjqQTzCt3kik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XA67q/btr5ozxbndx/9hcxoGTjwpzjqQTzCt3kik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXA67q%2Fbtr5ozxbndx%2F9hcxoGTjwpzjqQTzCt3kik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;833&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 위의 그림에서 맨 왼쪽만 DOG로 인식이 되었고 나머지는 Person으로 잘 인식되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 BBox가 그려지는 함수에서 person만 찾을 수 있게 조건 식을 걸고, Tensor 객체로 주어진 BBox 좌표를 이용하여 이미지를 등분해 주었다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/briecd/btr5oqAARLH/01aFev1FKEOk1fhCvouXKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/briecd/btr5oqAARLH/01aFev1FKEOk1fhCvouXKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/briecd/btr5oqAARLH/01aFev1FKEOk1fhCvouXKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbriecd%2Fbtr5oqAARLH%2F01aFev1FKEOk1fhCvouXKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;393&quot; height=&quot;252&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 이미지가 잘 나오는 것을 확인했고, 이를 Media Pipe의 Pose를 이용하여 포즈 분석을 실시하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s4azY/btr5nGJ4YvU/xO8HfwSKHOgETRDjLdtG6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s4azY/btr5nGJ4YvU/xO8HfwSKHOgETRDjLdtG6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s4azY/btr5nGJ4YvU/xO8HfwSKHOgETRDjLdtG6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs4azY%2Fbtr5nGJ4YvU%2FxO8HfwSKHOgETRDjLdtG6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;417&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;417&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동영상:&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, 이미지에서 실행한 방법을 동영상에서 그대로 실행에 옮겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person을 구분해 내서 bbox로 출력하는 작업을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (3).gif&quot; data-origin-width=&quot;308&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biZ4YC/btr7fnvCGBQ/hLfkaju6fRgpg0wyRKGKL1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biZ4YC/btr7fnvCGBQ/hLfkaju6fRgpg0wyRKGKL1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biZ4YC/btr7fnvCGBQ/hLfkaju6fRgpg0wyRKGKL1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/biZ4YC/btr7fnvCGBQ/hLfkaju6fRgpg0wyRKGKL1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;308&quot; height=&quot;321&quot; data-filename=&quot;ezgif.com-crop (3).gif&quot; data-origin-width=&quot;308&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;tensor flow가 yolo를 이용하여 객체를 선정할 때, 순서를 지정해 주지 못하였다. ( 아마 랜덤인 듯하다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 탐지되는 순서대로 뿌려주는 문제가 있었고,&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;여기서 Pose 분석까지 진행하게 되면 더 느려지게 될 테니만, 확인 차 구현을 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-crop (5).gif&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V8svQ/btr7whfsGwt/H90EWs5KBWLFbefFqRUPuK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V8svQ/btr7whfsGwt/H90EWs5KBWLFbefFqRUPuK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V8svQ/btr7whfsGwt/H90EWs5KBWLFbefFqRUPuK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/V8svQ/btr7whfsGwt/H90EWs5KBWLFbefFqRUPuK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;311&quot; height=&quot;326&quot; data-filename=&quot;ezgif.com-crop (5).gif&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속도가 많이 더뎌질 것으로 예상했지만, 속도는 비슷하게 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 즉, 객체를 탐지하여 bbox를 만드는 작업의 속도를 높인다면, 실시간성이 올라간다는 이야기다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 속도를 높이려면 다른 탐지기를 써야 하는데 JS에서의 구현이 쉽지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 영상에서 확인할 수 있듯이, 사람이 겹치게 되거나, 객체탐지 단계에서 사람의 일부분이 잘린다면, Pose 분석이 제대로 이루어지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로서, Multipose를 지원하는 모델을 사용하거나, 학습을 시키는 방법이 아닌 이상, Multipose를 구현하기 어렵다는 것을 알게 되었다. 토이프로젝트에 Multipose 기능을 활용하고 싶은데, 다른 방안을 찾아봐야겠다.&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/51</guid>
      <comments>https://olrlobt.tistory.com/51#entry51comment</comments>
      <pubDate>Thu, 23 Mar 2023 02:47:13 +0900</pubDate>
    </item>
    <item>
      <title>[Pose Estimation] MediaPipe Pose / 미디어 파이프로 사람 포즈 감지하기</title>
      <link>https://olrlobt.tistory.com/50</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c35X55/btr3U2UcQCl/u8NZp1G4zY0KH1DcIU5Fr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c35X55/btr3U2UcQCl/u8NZp1G4zY0KH1DcIU5Fr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c35X55/btr3U2UcQCl/u8NZp1G4zY0KH1DcIU5Fr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc35X55%2Fbtr3U2UcQCl%2Fu8NZp1G4zY0KH1DcIU5Fr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;343&quot; height=&quot;64&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Media pipe&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Media pipe는 Google에서 제작한 Machine Lunning Solution으로 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;얼굴추적, 손추적, 객체 인식과 같은 다양한 기능들을 제공&lt;/b&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;
&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;Media pipe에서 제공하는 기능들은 아래에서 확인 가능하며, 자세히는 공식 홈페이지를 참고하길 바란다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2212&quot; data-origin-height=&quot;952&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kEewz/btr3LFlMrcI/KC72e9SHQweIAA2vK7NDa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kEewz/btr3LFlMrcI/KC72e9SHQweIAA2vK7NDa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kEewz/btr3LFlMrcI/KC72e9SHQweIAA2vK7NDa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkEewz%2Fbtr3LFlMrcI%2FKC72e9SHQweIAA2vK7NDa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1009&quot; height=&quot;434&quot; data-origin-width=&quot;2212&quot; data-origin-height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://google.github.io/mediapipe/&quot;&gt;https://google.github.io/mediapipe/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678807099585&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;Home&quot; data-og-description=&quot;Cross-platform, customizable ML solutions for live and streaming media.&quot; data-og-host=&quot;google.github.io&quot; data-og-source-url=&quot;https://google.github.io/mediapipe/&quot; data-og-url=&quot;https://google.github.io/mediapipe/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://google.github.io/mediapipe/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://google.github.io/mediapipe/&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;Home&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Cross-platform, customizable ML solutions for live and streaming media.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;google.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 많은 기능들 중 내가 사용할 것은 Pose기능이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Pose 기능은 사람의 자세를 탐색하는 기술로 머신러닝으로 학습한 모델을 이용하여, 이미지, 동영상, 실시간 동영상 에서의 사람의 자세를 탐색한다. 반환 값은 좌표 값이며, 이를 통하여 canvas에 사람의 skeleton을 찍거나, grid를 이용하여 3D 화면에서의 렌더링이 가능하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif (2).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI0KIn/btr3IEgBF5R/NXMz5GyjqDylYyAElNheEK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI0KIn/btr3IEgBF5R/NXMz5GyjqDylYyAElNheEK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI0KIn/btr3IEgBF5R/NXMz5GyjqDylYyAElNheEK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bI0KIn/btr3IEgBF5R/NXMz5GyjqDylYyAElNheEK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;509&quot; data-filename=&quot;ezgif.com-video-to-gif (2).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자세한 Pose Estimation에 대하여 더 알고 싶다면, 이 전 포스팅을 참고하길 바라고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;간단한 Pose 테스트와 Hands 추적 테스트 도 가능하다.&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;&lt;a href=&quot;https://olrlobt.tistory.com/49&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://olrlobt.tistory.com/49&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678807313855&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;[API] 다양한 Pose Estimation API 비교와 정리&quot; data-og-description=&quot;Pose Estimation Pose estimation은 인공지능 및 컴퓨터 비전 기술을 사용하여 이미지나 비디오에서 인간의 포즈(자세)를 감지하고 추정하는 기술이다. 이미지 예 : 동영상 예 : 예를 들어, 얼마 전 올렸던&quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/49&quot; data-og-url=&quot;https://olrlobt.tistory.com/49&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bOA2A6/hyRVbWxLzc/IQLkFbrt5wQT8CBsexuot0/img.gif?width=320&amp;amp;height=576&amp;amp;face=0_0_320_576,https://scrap.kakaocdn.net/dn/bp147e/hyRVf5GxQK/NTkv6c58OfOSa7iTPJbEHk/img.gif?width=320&amp;amp;height=576&amp;amp;face=0_0_320_576,https://scrap.kakaocdn.net/dn/dK3qgw/hyRU525vEi/3HdYk6SjkyaqKng6glK2fK/img.png?width=1440&amp;amp;height=956&amp;amp;face=0_0_1440_956&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/49&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/49&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bOA2A6/hyRVbWxLzc/IQLkFbrt5wQT8CBsexuot0/img.gif?width=320&amp;amp;height=576&amp;amp;face=0_0_320_576,https://scrap.kakaocdn.net/dn/bp147e/hyRVf5GxQK/NTkv6c58OfOSa7iTPJbEHk/img.gif?width=320&amp;amp;height=576&amp;amp;face=0_0_320_576,https://scrap.kakaocdn.net/dn/dK3qgw/hyRU525vEi/3HdYk6SjkyaqKng6glK2fK/img.png?width=1440&amp;amp;height=956&amp;amp;face=0_0_1440_956');&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;[API] 다양한 Pose Estimation API 비교와 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Pose Estimation Pose estimation은 인공지능 및 컴퓨터 비전 기술을 사용하여 이미지나 비디오에서 인간의 포즈(자세)를 감지하고 추정하는 기술이다. 이미지 예 : 동영상 예 : 예를 들어, 얼마 전 올렸던&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Media pipe JavaScript 구현&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 현재, Spring 토이 프로젝트로 Pose estimation을 주제로 하고 있어서, 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Import&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1678810882570&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@mediapipe/pose/pose.js&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Media pipe의 Pose Detection 모델 호출&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1678807867589&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const pose = new Pose({
	locateFile: (file) =&amp;gt; {
		return `https://cdn.jsdelivr.net/npm/@mediapipe/pose/${file}`;
	}
});&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Pose 모델을 호출하여, 변수 pose로 선언한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;간단하게, &quot;&lt;a href=&quot;https://cdn.jsdelivr.net/npm/@mediapipe/pose/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://cdn.jsdelivr.net/npm/@mediapipe/pose/&lt;/a&gt;&quot; 패키지에서 Pose 객체를 가져왔으며, locateFile 함수를 통하여 경로에서 필요한 파일들을 가져오게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Pose Detection 설정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1678807940862&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pose.setOptions({
		upperBodyOnly: true,
		modelComplexity: 1,
		smoothLandmarks: true,
		enableSegmentation: false,
		minDetectionConfidence: 0.5,
		minTrackingConfidence: 0.5
});&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;upperBodyOnly&lt;/b&gt;: (기본값 false)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 상체만 탐색할지 여부를 설정한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- true로 설정하면, 화면에 상체만 나왔을 때는 상체만 탐색하지만, 전신이 다 나오면 전신을 다 탐색한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- false로 설정하면, 화면에 상체만 나와도 전신을 다 탐색하려 하고, 전신이 다 나오면 전신을 다 탐색한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 결론적으로, 전신을 다 탐색할 필요가 없어서 성능적 이득을 보려면 true로 해주자.&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;&lt;b&gt;modelComplexity&lt;/b&gt;: (기본값 1)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 모델의 복잡도 ( 0,1,2)의 값을 갖는다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 값이 높을수록 정확도는 높아지고 속도는 느려진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;smoothLandmarks&lt;/b&gt;: (기본값 true)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 추적 결과를 부드럽게 만든다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- false 설정이 결과는 더 정확하지만, 덜 부드러울 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;enableSegmentation&lt;/b&gt;: (기본값 false)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 비디오 세그멘테이션 사용 여부.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 이 기능을 이용하여 비디오에서 전경과 배경을 분리할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;minDetectionConfidence:&lt;/b&gt; (기본값 0.5)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 물체를 감지하는 최소 점수이다. (0~1)의 값을 갖는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 값이 높을수록 정확도는 높아지지만, 더 많은 노이즈가 발생한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;minTrackingConfidence:&lt;/b&gt; (기본값 0.5)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 추적할 물체의 신뢰도 (0~1)의 값을 갖는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 값이 높을수록 정확도는 높아지지만, 더 많은 추적 실패가 발생한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 외에도 많은 옵션들이 있지만, 기본적으로는 이 정도만 알면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Pose Detection 콜백 함수 설정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1678809577359&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pose.onResults((results) =&amp;gt; {
	console.log(results);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onResults 메서드를 통하여 콜백 함수를 설정한다. onResults의 경우, Pose의 Detection이 발생할 때마다 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 코드의 경우 위와 같이 작성하고, 조금 더 복잡도가 올라갈 경우나 재호출 할 경우 따로 함수를 선언해 아래와 같이 선언해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678809591493&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pose.onResults(onPose);

function onPose(results) {
	console.log(results);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Pose Detection 실행&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1678810217850&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pose.send({ 
	image: user_video 
});&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;send() 메서드의 image 속성에 분석할 대상 (이미지, 비디오, 실시간 영상)을 입력해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 상세사항은 아래를 참고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 이미지&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1678813100030&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pose.send({ image: user_image });&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;user_image 변수에 html의 img 태그를 연결해 주고 분석을 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 비디오&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1678813091730&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function processVideo() {
	pose.send({ image: user_video });
	
	requestAnimationFrame(processVideo);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user_video 변수에 html의 video 태그를 연결해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;send의 image 속성 명에서 알 수 있듯이 image는 video에서 한 프레임을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 processVideo 함수를 재귀 호출하여 video를 프레임 별로 send 해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 requestAnimationFrame 함수는 애니메이션에서 Frame을 60 FPS로 조절하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;requestAnimationFrame 없이 재귀 호출을 하게 된다면, 1초에 60보다 많은 Frame을 send 하게 되어, 호출 과부하가 발생할 수 있다.&amp;nbsp;&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_1678814033743&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;requestAnimationFrame(processVideo,{
	  maxFPS: 30,
	  skipFrames: 2
	})
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, Frame 수를 줄이고 싶다면,&amp;nbsp; 위와 같이 호출할 수 있고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 maxFPS는 최대 FPS를,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;skipFrames는 넘길 프레임 수를 조절한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 maxFPS 30 , skipFrames 2 라면, 최대 FPS는 30을 넘기지 않고, Frame을 2개당 하나를 분석하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 실시간 영상&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1678812349522&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678810564308&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;camera = new Camera(user_video, {
		onFrame: async () =&amp;gt; {
			await pose.send({ image: user_video });
		},
		width: 1280,
		height: 720
	});
	camera.start();&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;먼저 mediapipe의 카메라 객체를 생성해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user_video의 경우 html의 video 엘리먼트이고, onFrame은 camera 객체가 프레임을 가져올 때마다 실행된다. 즉, 비디오 분석 에서의 requestAnimationFrame()의 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNF07c/btr3SY5R4zE/k6Ug8H9zDwXK23XQD7gKL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNF07c/btr3SY5R4zE/k6Ug8H9zDwXK23XQD7gKL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNF07c/btr3SY5R4zE/k6Ug8H9zDwXK23XQD7gKL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNF07c%2Fbtr3SY5R4zE%2Fk6Ug8H9zDwXK23XQD7gKL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;649&quot; height=&quot;1026&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 33개의 키 포인트를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poseLandmarks에서는 image 기준에서의 x, y, z, 좌표와 visibility 점수를,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poseWorldLandmarks에서는 사람 기준에서의 x, y, z 좌표와 visibility 점수를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;visibility점수는 0~1 사이의 값으로, 신뢰도를 의미하며, 1에 가까울수록 신뢰도가 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Canvas로 Skeleton 그리기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;콜백 함수 작성&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1678815314304&quot; class=&quot;pf&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pose.onResults(onPose);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onResult의 콜백 함수에 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&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_1678815342325&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678815328704&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const canvasElement = document.getElementsByClassName('output_canvas')[0];
const canvasCtx = canvasElement.getContext('2d');

function onPose(results) {
//	console.log(results);

    canvasCtx.save(); 	// 캔버스 설정 저장
    canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height); // 캔버스 초기화
    
    // 캔버스에 이미지 넣기
    canvasCtx.drawImage(results.image, 0, 0, canvasElement.width, canvasElement.height);

    drawLandmarks(canvasCtx, results.poseLandmarks, {	// 랜드마크 표시
        color: '#FF0000', lineWidth: 2
    });
    drawConnectors(canvasCtx, results.poseLandmarks, POSE_CONNECTIONS,{	// 연결 선 표시
            color: '#0000FF', lineWidth: 3
        });
    canvasCtx.restore();	// 캔버스 설정 불러오기
}&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;clearRect()로 캔버스를 초기화한 후,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;drawLandmarks()와 drawConnectors()로 스켈레톤을 그려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 save()와 restore()가 마지막과 처음에 있는 이유는, 캔버스의 초기 설정값을 저장하기 위해서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무것도 작성되지 않은 상태에서 save()를 해 주지 않으면, 캔버스를 원래 설정으로 되돌릴 수 없다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif (3).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1dReL/btr3XWFWs3t/hD1jfsJ8E9kvxrriMU3SHk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1dReL/btr3XWFWs3t/hD1jfsJ8E9kvxrriMU3SHk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1dReL/btr3XWFWs3t/hD1jfsJ8E9kvxrriMU3SHk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/1dReL/btr3XWFWs3t/hD1jfsJ8E9kvxrriMU3SHk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;675&quot; data-filename=&quot;ezgif.com-video-to-gif (3).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Canvas Skeleton 좌, 우 구분하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메서드를 잘 이해하고 있다면, 간단하게 좌, 우를 구분하여 나타낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678816309814&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 좌측 키 값 // 우측 키 값
const leftIndices = [1, 2, 3, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31];
const rightIndices = [4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32];
const leftConnections = [	// 좌측 연결선
  [11,13],[13,15],[15,21],[15,17],[15,19],[17,19],
  [11,23],[23,25],[25,27],[27,29],[27,31],[29,31]
];
const rightConnections = [	// 우측 연결선
  [12,14],[14,16],[16,22],[16,18],[16,20],[18,20],
  [12,24],[24,26],[26,28],[28,30],[28,32],[30,32]
];
const centerConnections = [	// 중앙 연결선
  [11,12],[23,24]
];

function onPose(results) {
	//console.log(results);
	const keyPoint = results.poseLandmarks;
	let leftKeyPoint = [];	// 좌측 키포인트
	let rightKeyPoint = [];	// 우측 키포인트
    
	if (keyPoint != null) {
		canvasCtx.save();
		canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
		canvasCtx.drawImage(results.image, 0, 0, canvasElement.width, canvasElement.height);
		for (let i = 0; i &amp;lt; keyPoint.length; i++) {	// 키포인트 구분
			if (leftIndices.includes(i)) {
				leftKeyPoint.push(keyPoint[i]);
			} else {
				rightKeyPoint.push(keyPoint[i]);
			}
		}

		drawLandmarks(canvasCtx, leftKeyPoint, {
			color: '#FF0000', lineWidth: 2
		});
		drawLandmarks(canvasCtx, rightKeyPoint, {
			color: '#0000FF', lineWidth: 2
		});
		drawConnectors(canvasCtx, keyPoint, leftConnections,{
				color: '#00FFFF', lineWidth: 3
			});
		drawConnectors(canvasCtx, keyPoint, rightConnections,{
				color: '#00FF00', lineWidth: 3
			});
		drawConnectors(canvasCtx, keyPoint, centerConnections,{
			color: '#EEEEEE', lineWidth: 3
		});
		canvasCtx.restore();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif (4).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LK30j/btr3LHxbQcN/aGj5k03OPWd7rWSvk8rbhk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LK30j/btr3LHxbQcN/aGj5k03OPWd7rWSvk8rbhk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LK30j/btr3LHxbQcN/aGj5k03OPWd7rWSvk8rbhk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/LK30j/btr3LHxbQcN/aGj5k03OPWd7rWSvk8rbhk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;675&quot; data-filename=&quot;ezgif.com-video-to-gif (4).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/50</guid>
      <comments>https://olrlobt.tistory.com/50#entry50comment</comments>
      <pubDate>Wed, 15 Mar 2023 02:57:11 +0900</pubDate>
    </item>
    <item>
      <title>[Pose Estimation] 다양한 Pose Estimation API 비교와 정리</title>
      <link>https://olrlobt.tistory.com/49</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Pose Estimation&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;b&gt;Pose estimation&lt;/b&gt;은 인공지능 및 컴퓨터 비전 기술을 사용하여&lt;b&gt; &lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;이미지나 비디오에서 인간의 포즈(자세)를 감지하고 추정하는 기술&lt;/b&gt;&lt;/span&gt;이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 예 :&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by3Lxo/btr3uDV6qFu/aGae57BadEZxAWgeMnR63K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by3Lxo/btr3uDV6qFu/aGae57BadEZxAWgeMnR63K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by3Lxo/btr3uDV6qFu/aGae57BadEZxAWgeMnR63K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby3Lxo%2Fbtr3uDV6qFu%2FaGae57BadEZxAWgeMnR63K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;956&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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: #374151; text-align: start;&quot;&gt;동영상 예 :&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif (1).gif&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWRm3C/btr3JXrQ7pO/LY0eIJczd6JZpyCyprSvj0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWRm3C/btr3JXrQ7pO/LY0eIJczd6JZpyCyprSvj0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWRm3C/btr3JXrQ7pO/LY0eIJczd6JZpyCyprSvj0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dWRm3C/btr3JXrQ7pO/LY0eIJczd6JZpyCyprSvj0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;576&quot; data-filename=&quot;ezgif.com-video-to-gif (1).gif&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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: #374151; text-align: start;&quot;&gt;예를 들어, 얼마 전 올렸던 kakao pose API도 여기에 속한다.&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: #374151; text-align: start;&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/46&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://olrlobt.tistory.com/46&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678723581018&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] 카카오 포즈 / RestTemplate으로 Kakao Pose API 호출하기&quot; data-og-description=&quot;카카오 포즈 Kakao Pose API 카카오 포즈(Pose) API는 이미지나 영상을 분석해 사람의 자세를 추출하는 기능을 제공한다. 이미지에서 사람들을 찾고 사람의 코, 눈, 귀, 어깨, 팔꿈치, 손목, 골반, 무릎, &quot; data-og-host=&quot;olrlobt.tistory.com&quot; data-og-source-url=&quot;https://olrlobt.tistory.com/46&quot; data-og-url=&quot;https://olrlobt.tistory.com/46&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/p6a7Y/hyRU8q3b6H/ti8K8dfrMOUkKsXmZ4j9nk/img.png?width=661&amp;amp;height=381&amp;amp;face=0_0_661_381,https://scrap.kakaocdn.net/dn/c5wSya/hyRVerh6Qc/myfvELqq49KP5t0UUooTYK/img.png?width=661&amp;amp;height=381&amp;amp;face=0_0_661_381,https://scrap.kakaocdn.net/dn/oBYP6/hyRU2xBfh2/gGsX1p9pQFotWUiY9MotsK/img.png?width=1090&amp;amp;height=834&amp;amp;face=0_0_1090_834&quot;&gt;&lt;a href=&quot;https://olrlobt.tistory.com/46&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olrlobt.tistory.com/46&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/p6a7Y/hyRU8q3b6H/ti8K8dfrMOUkKsXmZ4j9nk/img.png?width=661&amp;amp;height=381&amp;amp;face=0_0_661_381,https://scrap.kakaocdn.net/dn/c5wSya/hyRVerh6Qc/myfvELqq49KP5t0UUooTYK/img.png?width=661&amp;amp;height=381&amp;amp;face=0_0_661_381,https://scrap.kakaocdn.net/dn/oBYP6/hyRU2xBfh2/gGsX1p9pQFotWUiY9MotsK/img.png?width=1090&amp;amp;height=834&amp;amp;face=0_0_1090_834');&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] 카카오 포즈 / RestTemplate으로 Kakao Pose API 호출하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오 포즈 Kakao Pose API 카카오 포즈(Pose) API는 이미지나 영상을 분석해 사람의 자세를 추출하는 기능을 제공한다. 이미지에서 사람들을 찾고 사람의 코, 눈, 귀, 어깨, 팔꿈치, 손목, 골반, 무릎,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olrlobt.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;Pose Estimation의 대표적인 API로는 국내의 Kakao Pose API, Ncloud Pose Estimation API 가 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해외의 Media Pipe, TensorFlow, Move net, Pose net 등이 있다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Pose Estimation 분석&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Pose Net&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제작사: Google&lt;/li&gt;
&lt;li&gt;2D 지원 여부: O&lt;/li&gt;
&lt;li&gt;3D 지원 여부: X&lt;/li&gt;
&lt;li&gt;멀티포즈 지원 여부: O&lt;/li&gt;
&lt;li&gt;지원 언어 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;JavaScript&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;TensorFlow.js&lt;/b&gt;&lt;/span&gt;를 기반으로 한다&lt;/li&gt;
&lt;li&gt;다른 Pose Estimation에 비해 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;성능이 전체적으로 떨어지는 편&lt;/b&gt;&lt;/span&gt;이다&amp;nbsp;&lt;/li&gt;
&lt;li&gt;BodyPix와 결합하여 인간의 실루엣을 추출하는 기능도 제공한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Pose Net Test :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot;&gt;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678727653658&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://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-description=&quot;&quot; data-og-host=&quot;storage.googleapis.com&quot; data-og-source-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&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://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&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;storage.googleapis.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Blaze Pose&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제작사: Google&lt;/li&gt;
&lt;li&gt;2D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;2D 포즈 추정 : O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;3D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;멀티포즈 지원 여부: X&lt;/li&gt;
&lt;li&gt;지원 언어 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;Python, JavaScript&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;MediaPipe&lt;/b&gt;&lt;/span&gt; 프레임워크에서 사용되는 딥러닝 기반 포즈 추정 모델&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Pose Net의 상위 버전&lt;/b&gt;&lt;/span&gt;이다&lt;/li&gt;
&lt;li&gt;상당히 높은 정확성을 보여준다&lt;/li&gt;
&lt;li&gt;비교적 모델의 크기가 무겁기 때문에, 실시간성이 약간 떨어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공식 홈페이지 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://google.github.io/mediapipe/solutions/pose.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://google.github.io/mediapipe/solutions/pose.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678726824447&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;Pose&quot; data-og-description=&quot;Cross-platform, customizable ML solutions for live and streaming media.&quot; data-og-host=&quot;google.github.io&quot; data-og-source-url=&quot;https://google.github.io/mediapipe/solutions/pose.html&quot; data-og-url=&quot;https://google.github.io/mediapipe/solutions/pose.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dfgwRq/hyRU3cdXwm/skaqtVZRDXVgxkwE6F2Wk1/img.png?width=1418&amp;amp;height=742&amp;amp;face=0_0_1418_742,https://scrap.kakaocdn.net/dn/g3GfO/hyRU2RXEwa/iTzj4KxTLcSZzZASXEs7X1/img.png?width=772&amp;amp;height=438&amp;amp;face=0_0_772_438,https://scrap.kakaocdn.net/dn/QGoi2/hyRVah8Dg4/6C4NSaOqGAtKngKpxVvHx0/img.png?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256&quot;&gt;&lt;a href=&quot;https://google.github.io/mediapipe/solutions/pose.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://google.github.io/mediapipe/solutions/pose.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dfgwRq/hyRU3cdXwm/skaqtVZRDXVgxkwE6F2Wk1/img.png?width=1418&amp;amp;height=742&amp;amp;face=0_0_1418_742,https://scrap.kakaocdn.net/dn/g3GfO/hyRU2RXEwa/iTzj4KxTLcSZzZASXEs7X1/img.png?width=772&amp;amp;height=438&amp;amp;face=0_0_772_438,https://scrap.kakaocdn.net/dn/QGoi2/hyRVah8Dg4/6C4NSaOqGAtKngKpxVvHx0/img.png?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256');&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;Pose&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Cross-platform, customizable ML solutions for live and streaming media.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;google.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Blaze Pos Test :&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot;&gt;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678727794981&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://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-description=&quot;&quot; data-og-host=&quot;storage.googleapis.com&quot; data-og-source-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&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://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&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;storage.googleapis.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Move Net&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제작사: Google&lt;/li&gt;
&lt;li&gt;2D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;3D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;멀티포즈 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;지원 언어 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;TensorFlow&lt;/b&gt;&lt;/span&gt;, Python, JavaScript&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;MediaPipe&lt;/b&gt; &lt;/span&gt;프레임워크에서 사용되는 딥러닝 기반 포즈 추정 모델, &lt;b&gt;TensorFlow&lt;/b&gt; 지원&lt;/li&gt;
&lt;li&gt;다른 Pose Estimation과는 다르게 움직임을 중점으로 제작되었다.&lt;/li&gt;
&lt;li&gt;또한, 다른 Pose Estimation과는 다르게 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;모바일 기기에서도 실시간으로 동작하게 경량화되었다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;높은 정확도와 저전력 소비를 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공식 홈페이지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.tensorflow.org/hub/tutorials/movenet?hl=ko&quot;&gt;https://www.tensorflow.org/hub/tutorials/movenet?hl=ko&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1678726763002&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;MoveNet: 매우 빠르고 정확한 포즈 감지 모델. &amp;nbsp;|&amp;nbsp; TensorFlow Hub&quot; data-og-description=&quot;MoveNet: 매우 빠르고 정확한 포즈 감지 모델. 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. MoveNet은 신체의 17개 주요 부위를 감지하는 매우 빠르고 정확한 &quot; data-og-host=&quot;www.tensorflow.org&quot; data-og-source-url=&quot;https://www.tensorflow.org/hub/tutorials/movenet?hl=ko&quot; data-og-url=&quot;https://www.tensorflow.org/hub/tutorials/movenet?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c25Asz/hyRU6NyGyZ/i25ogG4fwknPD38YIAitvk/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/nOXVN/hyRVhhhtpP/5OC4vEV4l2LcYHkCKUAcfk/img.png?width=404&amp;amp;height=405&amp;amp;face=0_0_404_405&quot;&gt;&lt;a href=&quot;https://www.tensorflow.org/hub/tutorials/movenet?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.tensorflow.org/hub/tutorials/movenet?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c25Asz/hyRU6NyGyZ/i25ogG4fwknPD38YIAitvk/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/nOXVN/hyRVhhhtpP/5OC4vEV4l2LcYHkCKUAcfk/img.png?width=404&amp;amp;height=405&amp;amp;face=0_0_404_405');&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;MoveNet: 매우 빠르고 정확한 포즈 감지 모델. &amp;nbsp;|&amp;nbsp; TensorFlow Hub&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MoveNet: 매우 빠르고 정확한 포즈 감지 모델. 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. MoveNet은 신체의 17개 주요 부위를 감지하는 매우 빠르고 정확한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.tensorflow.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Move Net Test :&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot;&gt;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678727807281&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://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-description=&quot;&quot; data-og-host=&quot;storage.googleapis.com&quot; data-og-source-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&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://storage.googleapis.com/tfjs-models/demos/pose-detection/index.html?model=movenet&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;storage.googleapis.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. MediaPipe Pose&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제작사: Google&lt;/li&gt;
&lt;li&gt;2D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;2D 포즈 추정 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;3D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;멀티포즈 지원 여부: X&lt;/li&gt;
&lt;li&gt;지원 언어 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;Python, C++, JavaScript&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;Google에서 개발한 오픈소스 라이브러리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;비디오 및 이미지에서 3D 포즈와 트래킹, 페이셜 랜드마크 검출 등 다양한 기능을 제공&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;MediaPipe Pose는 높은 정확도와 실시간성을 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Media pipe 손 탐지 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://codepen.io/mediapipe/pen/RwGWYJw&quot;&gt;https://codepen.io/mediapipe/pen/RwGWYJw&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678727709386&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;MediaPipe - Hands&quot; data-og-description=&quot;...&quot; data-og-host=&quot;codepen.io&quot; data-og-source-url=&quot;https://codepen.io/mediapipe/pen/RwGWYJw&quot; data-og-url=&quot;https://codepen.io/mediapipe/details/RwGWYJw&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/NXaOe/hyRVfjrDac/bJIzcfya5kDMvIcY5m64wK/img.png?width=1146&amp;amp;height=644&amp;amp;face=359_173_632_470,https://scrap.kakaocdn.net/dn/c0Uvge/hyRVcUzyz0/uUgObZbe0LCUqSP3hWLj3k/img.png?width=1146&amp;amp;height=644&amp;amp;face=359_173_632_470&quot;&gt;&lt;a href=&quot;https://codepen.io/mediapipe/pen/RwGWYJw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://codepen.io/mediapipe/pen/RwGWYJw&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/NXaOe/hyRVfjrDac/bJIzcfya5kDMvIcY5m64wK/img.png?width=1146&amp;amp;height=644&amp;amp;face=359_173_632_470,https://scrap.kakaocdn.net/dn/c0Uvge/hyRVcUzyz0/uUgObZbe0LCUqSP3hWLj3k/img.png?width=1146&amp;amp;height=644&amp;amp;face=359_173_632_470');&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;MediaPipe - Hands&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codepen.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Media pipe 포즈 탐지 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://codepen.io/mediapipe/pen/jOMbvxw&quot;&gt;https://codepen.io/mediapipe/pen/jOMbvxw&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678727711633&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;MediaPipe - Pose&quot; data-og-description=&quot;...&quot; data-og-host=&quot;codepen.io&quot; data-og-source-url=&quot;https://codepen.io/mediapipe/pen/jOMbvxw&quot; data-og-url=&quot;https://codepen.io/mediapipe/details/jOMbvxw&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oo6hB/hyRU9XPQmh/g2qBMq03K9AaRSKqyGqxdK/img.png?width=1213&amp;amp;height=682&amp;amp;face=525_109_676_273,https://scrap.kakaocdn.net/dn/u2t2x/hyRVctuFkr/xyoK87keZC5uFlakW8mYm0/img.png?width=1213&amp;amp;height=682&amp;amp;face=525_109_676_273&quot;&gt;&lt;a href=&quot;https://codepen.io/mediapipe/pen/jOMbvxw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://codepen.io/mediapipe/pen/jOMbvxw&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oo6hB/hyRU9XPQmh/g2qBMq03K9AaRSKqyGqxdK/img.png?width=1213&amp;amp;height=682&amp;amp;face=525_109_676_273,https://scrap.kakaocdn.net/dn/u2t2x/hyRVctuFkr/xyoK87keZC5uFlakW8mYm0/img.png?width=1213&amp;amp;height=682&amp;amp;face=525_109_676_273');&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;MediaPipe - Pose&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codepen.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. Open Pose&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제작사: Carnegie Mellon University&lt;/li&gt;
&lt;li&gt;2D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;2D 포즈 추정 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;3D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;멀티포즈 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;지원 언어 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;C++, Python, MATLAB, Java, JavaScript&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인간의 포즈를 비디오나 이미지에서 감지하기 위한 오픈소스 라이브러리&lt;/li&gt;
&lt;li&gt;OpenPose는 딥러닝 모델을 기반으로 함&lt;/li&gt;
&lt;li&gt;각 관절의 위치와 방향을 추정하는 것 외에도 손가락 추적, 얼굴 감지 등 다양한 기능을 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. Kakao Pose&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제작사: Kakao Brain&lt;/li&gt;
&lt;li&gt;2D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;2D 포즈 추정 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;3D 지원 여부: X&lt;/li&gt;
&lt;li&gt;멀티포즈 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;지원 언어 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;Python, REST api&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kakao Pose는 단일 또는 다중 인물의 포즈를 추정할 수 있으며, 높은 정확도를 보여준다.&lt;/li&gt;
&lt;li&gt;또한, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;편리한 API와 예제 코드를 제공하여 사용자 편의성이 높다&lt;/b&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;이미지 분석의 경우, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;최대 용량이 2MB이며,&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;긴 변의 길이가 최대 2048 pixel, 최소 320 pixel이다&lt;span style=&quot;background-color: #dddddd; color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;동영상의 경우 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;최대 50MB, 영상 길이 최대 30초까지 무료 분석&lt;/b&gt;&lt;/span&gt;이 제공된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developers.kakao.com/docs/latest/ko/pose/common&quot;&gt;https://developers.kakao.com/docs/latest/ko/pose/common&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678727832200&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/pose/common&quot; data-og-url=&quot;https://developers.kakao.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AXNHm/hyRU8YVUKX/lxpU6Rlx2xAw5dKj5sxbF1/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/tAIPu/hyRU8EDb4n/CklT3gkk5NIQ1KjikhfA6k/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000,https://scrap.kakaocdn.net/dn/bi3La6/hyRU8LpBae/Fx3XVy09NuxmXp5knwC450/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/pose/common&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.kakao.com/docs/latest/ko/pose/common&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AXNHm/hyRU8YVUKX/lxpU6Rlx2xAw5dKj5sxbF1/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/tAIPu/hyRU8EDb4n/CklT3gkk5NIQ1KjikhfA6k/img.png?width=3840&amp;amp;height=1000&amp;amp;face=0_0_3840_1000,https://scrap.kakaocdn.net/dn/bi3La6/hyRU8LpBae/Fx3XVy09NuxmXp5knwC450/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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7. Ncloud Pose&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제작사: Naver Cloud&lt;/li&gt;
&lt;li&gt;2D 지원 여부: &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;2D 포즈 추정 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;O&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;3D 지원 여부: X&lt;/li&gt;
&lt;li&gt;멀티포즈 지원 여부: X&lt;/li&gt;
&lt;li&gt;지원 언어 : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: left;&quot;&gt;Python, JAVA, PHP , C#&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Naver Cloud에서 제공하는 AI 기술 중 하나&lt;/li&gt;
&lt;li&gt;API 호출을 통해 손쉽게 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;최대 300KB 이미지 데이터를 지원&lt;/b&gt;&lt;/span&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://api.ncloud-docs.com/docs/ai-naver-poseestimation-pose&quot;&gt;https://api.ncloud-docs.com/docs/ai-naver-poseestimation-pose&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678727833111&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;pose estimation (포즈 인식)&quot; data-og-description=&quot; &quot; data-og-host=&quot;api.ncloud-docs.com&quot; data-og-source-url=&quot;https://api.ncloud-docs.com/docs/ai-naver-poseestimation-pose&quot; data-og-url=&quot;https://api.ncloud-docs.com/docs/ai-naver-poseestimation-pose&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://api.ncloud-docs.com/docs/ai-naver-poseestimation-pose&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://api.ncloud-docs.com/docs/ai-naver-poseestimation-pose&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;pose estimation (포즈 인식)&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;api.ncloud-docs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;블로그 상단에 링크 해 놓은 Pose net, Move net, Blaze Pose 테스트와, Media pipe의 포즈 테스트, Hands 테스트를 통해 한 번씩 테스트 하고 API를 선택하길 바란다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;나는 프로젝트에 적용할 API로 실시간성, 3D지원 여부, Multi Pose 지원 여부를 보고 선택을 하였다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Media pipe의 경우 상당히 좋은 성능을 자랑하지만 Multi Pose를 지원하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만, 방법이 아주 없는 것은 아닌 것 같고, 이를 활용할 방법을 모색중에 있다.&lt;/p&gt;</description>
      <category>Spring/Project</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/49</guid>
      <comments>https://olrlobt.tistory.com/49#entry49comment</comments>
      <pubDate>Tue, 14 Mar 2023 02:53:07 +0900</pubDate>
    </item>
    <item>
      <title>[개발 환경] 노트북 삼성 이온1 RAM 추가하기</title>
      <link>https://olrlobt.tistory.com/48</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전까지 아무 불편함 없이 삼성 이온 1을 개발용 노트북으로 잘 쓰고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 국비에서 더블 모니터를 맛보고 나니, 집에도 더블 모니터를 설치하게 되었고, Spring과 카톡, 구글만 같이 쓰더라도 버벅거림 현상이 나타나며 답답함을 느끼게 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 관리자(Ctrl + Shift+ esc)의 성능 확인 탭에서 메모리가 부족하다는 것을 깨닫고, RAM을 추가하기로 한다&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;노트북 RAM포트 지원 여부 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼성 이온 1이라고 모두 다 RAM을 추가할 수 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 찾아본 바로는 15인치 버전만 가능하고 13인치 버전은 램 추가 포트 자체가 없는 것으로 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시스템 검색 - 시스템 정보 - 시스템 요약 - 베이스보드 제품/ 시스템 모델&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y5qOd/btr2GuMfiLl/odRbWfLY6NHrqWo0mzQwa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y5qOd/btr2GuMfiLl/odRbWfLY6NHrqWo0mzQwa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y5qOd/btr2GuMfiLl/odRbWfLY6NHrqWo0mzQwa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy5qOd%2Fbtr2GuMfiLl%2FodRbWfLY6NHrqWo0mzQwa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;480&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 노트북의 경우, 모델명이 NT950 XCJ-X59이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 필요한 부분은 NT950 XCJ-X59&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NT는 노트북,&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;&lt;b&gt;50은 디스플레이 크기,&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;X는 터치디스플레이 여부,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C는 CPU의 세대,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;J는 이온,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;X는 외장 그래픽,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5는 CPU종류 i5,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;9는 메모리 용량&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;앞의 숫자가 5여야만, 추가 램 포트가 존재하고, 뒤의 번호로 램 용량을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( 1의 경우 16GB라고 하며, 나머지의 경우 8GB 인 것으로 추정된다.)&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RAM 선택&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원 가능 여부를 확인했으니, 알맞은 RAM을 선택하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAM이 어떤 것이 있었나 몰랐던 나는, 간단한 검색을 통해 알맞은 기종을 선택하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼성 RAM이 가장 가성비가 좋고 훌륭하다고 하여 다음 제품을 선택하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ram.png&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N4S5a/btr2NkoK01K/GZ0PmCp7x1Sn06hK9rdgGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N4S5a/btr2NkoK01K/GZ0PmCp7x1Sn06hK9rdgGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N4S5a/btr2NkoK01K/GZ0PmCp7x1Sn06hK9rdgGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN4S5a%2Fbtr2NkoK01K%2FGZ0PmCp7x1Sn06hK9rdgGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;430&quot; height=&quot;567&quot; data-filename=&quot;ram.png&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAM 선택 시, 노트북용인지, &lt;b&gt;노트북 내장 RAM 용량과 같은지&lt;/b&gt;를 꼭 확인해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내장이 8GB인데 16GB를 착용할 경우, 굉장히 비효율적이라고 하니 8,8 또는 16,16으로 맞춰주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 고민했던 RAM 종류는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DDR4 2666과 DDR4 3200 두 가지이다. 내장 RAM의 경우 2666이지만, 두 가지 모두 가격은 비슷했다. 성능 또한 내장이 2666이기 때문에 3200을 끼더라도 2666으로 맞추어진다.&lt;/p&gt;
&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;차이가 있다면 중고로 팔 때 3200이 값을 더 칠 수 있다는 것뿐이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 얼마 차이 안 나는 것 같고, 같은 RAM을 껴 주는 것이 성능이 아주 조금 더 잘 나온다고 하기에 2666으로 샀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RAM 장착&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;준비물 :&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트북, RAM , 정밀 드라이버, &lt;s&gt;안 쓰는 신용카드&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;뒤판 제거&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Oo6B3/btr2PSLHYSj/gixeRKJQKbccWTSV1f3DMK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Oo6B3/btr2PSLHYSj/gixeRKJQKbccWTSV1f3DMK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Oo6B3/btr2PSLHYSj/gixeRKJQKbccWTSV1f3DMK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOo6B3%2Fbtr2PSLHYSj%2FgixeRKJQKbccWTSV1f3DMK%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;340&quot; height=&quot;453&quot; data-filename=&quot;KakaoTalk_20230308_183407386.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 노트북을 분해해 주어야 한다.&lt;/p&gt;
&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_02.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvTyZ8/btr2OrHXEui/16tL0EOFp1cjTbkeDDclP1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvTyZ8/btr2OrHXEui/16tL0EOFp1cjTbkeDDclP1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvTyZ8/btr2OrHXEui/16tL0EOFp1cjTbkeDDclP1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvTyZ8%2Fbtr2OrHXEui%2F16tL0EOFp1cjTbkeDDclP1%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;344&quot; height=&quot;459&quot; data-filename=&quot;KakaoTalk_20230308_183407386_02.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;어느 블로그에서는 샤오미 드라이버를 사야 한다는데, 그럴 필요까진 없어 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_03.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfSS22/btr2O1hOKWM/gaw9U8H8otjEK3zL5Gmlp0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfSS22/btr2O1hOKWM/gaw9U8H8otjEK3zL5Gmlp0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfSS22/btr2O1hOKWM/gaw9U8H8otjEK3zL5Gmlp0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfSS22%2Fbtr2O1hOKWM%2Fgaw9U8H8otjEK3zL5Gmlp0%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;351&quot; height=&quot;4032&quot; data-filename=&quot;KakaoTalk_20230308_183407386_03.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 군데를 모두 제거해 주었으면 뒤판을 제거해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤판의 경우, 삼성의 마감 덕분에 쉽게 분리되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 블로그 글에서, 안 쓰는 &lt;b&gt;신용카드로 들어주었다는 말을 보았는데, 절대 되지 않았다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;신용카드만 상할 뿐이었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 하신 건지 잘 모르겠지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;다이소 정밀 드라이버의 두 번째 줄, 가운데 일자 드라이버를 사용&lt;/b&gt;&lt;/span&gt;하여 간편하게 분리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_04.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YMBWt/btr2QLFGlzF/Lr2YIAI2BFe8fFkBQkPlw1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YMBWt/btr2QLFGlzF/Lr2YIAI2BFe8fFkBQkPlw1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YMBWt/btr2QLFGlzF/Lr2YIAI2BFe8fFkBQkPlw1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYMBWt%2Fbtr2QLFGlzF%2FLr2YIAI2BFe8fFkBQkPlw1%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;377&quot; height=&quot;503&quot; data-filename=&quot;KakaoTalk_20230308_183407386_04.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 공략할 부분은, &lt;b&gt;힌지 부분&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트북에 약간의 흠집 나 자국이 날 수 있지만, 나는 사실 별 상관하지 않고 쑥 밀어 넣어 들어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 세게 들지 말고 살짝만 들면, 상체와 뒤판을 연결 지어 주는 부분이 들린다는 느낌이 들 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 상태로 냅 두어도 다시 끼어지거나 하지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_05.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pmvJW/btr2Pymi2Yo/A1nVLgNy6iAQADkpdmPsGk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pmvJW/btr2Pymi2Yo/A1nVLgNy6iAQADkpdmPsGk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pmvJW/btr2Pymi2Yo/A1nVLgNy6iAQADkpdmPsGk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpmvJW%2Fbtr2Pymi2Yo%2FA1nVLgNy6iAQADkpdmPsGk%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;381&quot; height=&quot;508&quot; data-filename=&quot;KakaoTalk_20230308_183407386_05.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서, 조금 앞쪽 부분으로 다시 드라이버를 끼워 들어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 반복하다 보면 가장 안 뜯어지는 부분인 힌지 부분이 남을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_06.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2ZmTn/btr2D2bHOMv/cKrlwid2ht42TFc4rXkPh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2ZmTn/btr2D2bHOMv/cKrlwid2ht42TFc4rXkPh0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2ZmTn/btr2D2bHOMv/cKrlwid2ht42TFc4rXkPh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2ZmTn%2Fbtr2D2bHOMv%2FcKrlwid2ht42TFc4rXkPh0%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;370&quot; height=&quot;4032&quot; data-filename=&quot;KakaoTalk_20230308_183407386_06.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_KakaoTalk_20230308_183407386_07.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccXZGE/btr2OQU3lqy/YjHwKo8RVCAfZkHq1a2Os1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccXZGE/btr2OQU3lqy/YjHwKo8RVCAfZkHq1a2Os1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccXZGE/btr2OQU3lqy/YjHwKo8RVCAfZkHq1a2Os1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccXZGE%2Fbtr2OQU3lqy%2FYjHwKo8RVCAfZkHq1a2Os1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;2250&quot; data-filename=&quot;edited_KakaoTalk_20230308_183407386_07.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;배터리 제거&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;램을 장착하기에 앞서, 배터리를 제거해 주어야 한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배터리를 제거하지 않고 RAM을 장착하면 쇼트가 날 수도 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;b&gt;간단하게 톡 들어 올리면 빠진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누르거나, 전선이 끊어질까 걱정하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;살살 들어 올리면 빠진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_08.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NEFsC/btr2D6E1Vhg/tKvK4LZkVZBtkjmtTkcG1k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NEFsC/btr2D6E1Vhg/tKvK4LZkVZBtkjmtTkcG1k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NEFsC/btr2D6E1Vhg/tKvK4LZkVZBtkjmtTkcG1k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNEFsC%2Fbtr2D6E1Vhg%2FtKvK4LZkVZBtkjmtTkcG1k%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;499&quot; height=&quot;4032&quot; data-filename=&quot;KakaoTalk_20230308_183407386_08.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RAM 장착&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 RAM을 장착해 줄 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_KakaoTalk_20230308_183407386_12.jpg&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;1125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSlLZS/btr2NiqYzhG/t2ugDkOEuaHISjxLubEolk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSlLZS/btr2NiqYzhG/t2ugDkOEuaHISjxLubEolk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSlLZS/btr2NiqYzhG/t2ugDkOEuaHISjxLubEolk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSlLZS%2Fbtr2NiqYzhG%2Ft2ugDkOEuaHISjxLubEolk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;390&quot; height=&quot;195&quot; data-filename=&quot;edited_KakaoTalk_20230308_183407386_12.jpg&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;1125&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배송 온 RAM을 꺼내준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤판 배터리 우측 상단에 다음 사진과 같이 감싸져 있는 부분이 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_14.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2rzRF/btr2D5e2NNE/10ZNyNZAW38qXgRht5EVn0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2rzRF/btr2D5e2NNE/10ZNyNZAW38qXgRht5EVn0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2rzRF/btr2D5e2NNE/10ZNyNZAW38qXgRht5EVn0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2rzRF%2Fbtr2D5e2NNE%2F10ZNyNZAW38qXgRht5EVn0%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;384&quot; height=&quot;512&quot; data-filename=&quot;KakaoTalk_20230308_183407386_14.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다시피, Adapter와 Battery를 제거하라고 쓰여있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 제거를 해 주었으니 걱정 말고 분해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_11.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqrBco/btr2D23SxjK/XgrNOk9lTjTkJUMDptFkC1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqrBco/btr2D23SxjK/XgrNOk9lTjTkJUMDptFkC1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqrBco/btr2D23SxjK/XgrNOk9lTjTkJUMDptFkC1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqrBco%2Fbtr2D23SxjK%2FXgrNOk9lTjTkJUMDptFkC1%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;408&quot; height=&quot;544&quot; data-filename=&quot;KakaoTalk_20230308_183407386_11.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분해를 해 준 후에는 위 사진과 같이 &lt;b&gt;아랫부분을 먼저&lt;/b&gt; 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음, 손가락이 위치해 있는 부분을 눌러주면, &lt;b&gt;딸깍 소리&lt;/b&gt;가 난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 까지 하면 RAM 추가 완료.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SSD 추가 위치&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20230308_183407386_15.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDb5nk/btr2QKtgAJA/vkKUReKHINcz0ReGru92kk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDb5nk/btr2QKtgAJA/vkKUReKHINcz0ReGru92kk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDb5nk/btr2QKtgAJA/vkKUReKHINcz0ReGru92kk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDb5nk%2Fbtr2QKtgAJA%2FvkKUReKHINcz0ReGru92kk%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;404&quot; height=&quot;4032&quot; data-filename=&quot;KakaoTalk_20230308_183407386_15.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 SSD까지 업그레이드할 계획이라면 배터리 오른쪽에 위치한 부분에 꽂아주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 SSD까진 필요하지 않아 구매하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;조립&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조립은 분해의 역순이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전히 조립하기 전에, 노트북을 켜 SAMSUNG 로고가 나올 때 F2 키를 눌러 BIOS 설정에 진입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BIOS 설정에서 RAM이 잘 인식되어 있는지 확인하고 노트북을 종료시킨 후, 다시 조립을 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 나는 BIOS 설정이 안 떠서 그냥 시스템 정보에서 확인하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7EKaZ/btr2Gts4MXX/VikOOkk0RdFqFWk3Eipr90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7EKaZ/btr2Gts4MXX/VikOOkk0RdFqFWk3Eipr90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7EKaZ/btr2Gts4MXX/VikOOkk0RdFqFWk3Eipr90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7EKaZ%2Fbtr2Gts4MXX%2FVikOOkk0RdFqFWk3Eipr90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;609&quot; height=&quot;410&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 인식되지 않으면, 다시 분해를 하여야 하므로 진행하는 절차이다. 생략해도 상관없다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;완료&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 SPRING과 함께 여러 가지를 함께 돌려도 RAM이 부족하지 않는 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;메모리 사용률.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VsGFd/btr2D8iACHO/TN3stxYzeS8o16l4iEmqsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VsGFd/btr2D8iACHO/TN3stxYzeS8o16l4iEmqsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VsGFd/btr2D8iACHO/TN3stxYzeS8o16l4iEmqsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVsGFd%2Fbtr2D8iACHO%2FTN3stxYzeS8o16l4iEmqsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;856&quot; data-filename=&quot;메모리 사용률.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>else/개발환경</category>
      <author>olrlobt</author>
      <guid isPermaLink="true">https://olrlobt.tistory.com/48</guid>
      <comments>https://olrlobt.tistory.com/48#entry48comment</comments>
      <pubDate>Wed, 8 Mar 2023 19:41:43 +0900</pubDate>
    </item>
  </channel>
</rss>