<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개 x발</title>
    <link>https://programmingjun.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 12 Aug 2026 15:20:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>쭈녁</managingEditor>
    <item>
      <title>엘리스 리크루팅 위크 (드디어 취뽀)</title>
      <link>https://programmingjun.tistory.com/102</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;b&gt; 결국결국결국*100&lt;/b&gt;&amp;nbsp; 취뽀하게 되었다. 아직 미생이고 가야할 길이 너무 멀고 길지만 백엔드 개발자를 꿈꾸며 긴 시간동안 했던 준비를 떠올리며 엘리스 트랙에서 주신 도움도 홍보차 포스팅 하고싶어서 글을 써본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;취업한지 3달이나 지났지만 이제 씀&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;&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-origin-width=&quot;1081&quot; data-origin-height=&quot;1780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ODecD/btsJvezKzgy/R1CJx7t6lY8PagZUfUqrnk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ODecD/btsJvezKzgy/R1CJx7t6lY8PagZUfUqrnk/img.jpg&quot; data-alt=&quot;이미 모집 끝난 트랙입니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ODecD/btsJvezKzgy/R1CJx7t6lY8PagZUfUqrnk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FODecD%2FbtsJvezKzgy%2FR1CJx7t6lY8PagZUfUqrnk%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;458&quot; height=&quot;754&quot; data-origin-width=&quot;1081&quot; data-origin-height=&quot;1780&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미 모집 끝난 트랙입니다&lt;/figcaption&gt;
&lt;/figure&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;아주 작은 스타트업 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;취업하게 되었다. 생성형 AI 를 기반으로 한 서비스를 모니터링하고 자연어로 시스템 관리를 지원하는 서비스를 만들고 있다. 포지션은 백엔드 파트이다.&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;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;h3 data-ke-size=&quot;size23&quot;&gt;이전 개발 관련 전공 혹은 업무를 했었는지&lt;/h3&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;하지만 개발이라는 직무를 선택하고 기술을 배운 것은 내 인생에 꽤나 중요한 선택이 되지 않을까 생각하고 성향과 매우 잘맞는 직종을 잘 찾았다고 본인은 생각한다.&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;엘리스 Cloud트랙 1기를 지원하신 계기와 엘리스를 선택한 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 블로그 포스팅에도 올렸듯 1년 6개월간의 Java/Spring 독학을 한 후 혼자 취업을 도전했었다. 이때 취업의 벽을 느끼고 의지도 많이 약해져 있을 때 우연찮게 엘리스의 광고 글을 보게 되어 지원했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 독학 시절 오프라인에서 같이 스터디하던 동료가 엘리스의 Node 코스를 이수하고 이후 취업까지 한 동료가 있어 이미 엘리스에 대해 어느정도 인지하고 있었고 당시에는 Java/Spring을 기반으로한 트랙이 없어 참여하지 않았었는데 우연치 않게 광고 글을 보고 지원하게 되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&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;그리고 플랫폼의 경우 IDEA 와 같은 개발 툴을 쓰고 있는 입장에서 하나하나 타이핑 해서 쓰는 코딩이 불편하긴 했지만 기본적인 학습을 하고 넘어갈 수 있도록 체계가 잡혀 있었던 것 같다.&lt;/p&gt;
&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-origin-width=&quot;1146&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBLzAw/btsJtw9Ydhy/SfeBzdyS8pWG8CSTCpGYXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBLzAw/btsJtw9Ydhy/SfeBzdyS8pWG8CSTCpGYXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBLzAw/btsJtw9Ydhy/SfeBzdyS8pWG8CSTCpGYXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBLzAw%2FbtsJtw9Ydhy%2FSfeBzdyS8pWG8CSTCpGYXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;844&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;수료&amp;nbsp;후&amp;nbsp;관심&amp;nbsp;있는&amp;nbsp;직무나&amp;nbsp;기업이&amp;nbsp;있었나요? &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수료후 기업에 대해서 딱히 생각해본 것은 없는것 같고 빨리 취업을 해야겠다 생각이 많았던것 같다. 직무의 경우는 백엔드만 공부했었기 때문에 직무는 백엔드로 가야 가장 경쟁력이 있을꺼라고 생각했다. 사실 경쟁력 때문에 백엔드를 생각한것이지 막상 현업에 와보니 Spring 뿐 아니라 Nest 도 배워야겠다 생각하고 계속 공부하고 있고 이를 넘어 React도 공부해서 전체적으로 흐름을 알고 다재다능하게 경쟁력을 키워야겠다는 생각이 많이 드는것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리크르팅 위크란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엘리스 트랙을 수료한 수료생들을 대상으로 &lt;b&gt;수료식&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1707&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PzuyG/btsJucC1QPQ/yOXdyZl1hXxyNwi3dtY56k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PzuyG/btsJucC1QPQ/yOXdyZl1hXxyNwi3dtY56k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PzuyG/btsJucC1QPQ/yOXdyZl1hXxyNwi3dtY56k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPzuyG%2FbtsJucC1QPQ%2FyOXdyZl1hXxyNwi3dtY56k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1280&quot; data-origin-width=&quot;1707&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;/figure&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;예전에 엘리스에서 진행한 취업 설명회를 가서 들었던 코치님의 말씀이 기억이 난다.&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 style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리크루팅 위크를 추천하고 싶은 분은?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업 지원을 많이 하고 있는데 뜻대로 잘 안되는 분들 그리고 내가 목표하는 기업이 따로 없다 라고 하면 참석해서 개발 관련해서 어떤 비즈니스들이 있고 어떤 기술을 알아야 하는지 현업자와 이야기 해 볼수 있는 좋은 기회라고 생각한다. 현업자와 커피챗하면서 내가 어떻게 공부하고 요즘 트랜드는 어떤지를 묻을 수도 있다. 물론 참여한 기업에서 모두 의미있고 좋은 답변을 주시리란 보장은 없지만 하다 못해 내가 취업할 직종에 어떤 분야가 있고 어떤 기술이 필요하고 이런것들을 알아가기에도 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리크루팅 위크를 통해서 배운 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리크루팅 위크 때 지금 회사의 사수님을 만났는데 운좋게 개발 경험도 많으시고 스타트업에서 CTO를 적지 않은 기간 맡고 계신 분이었다. 당일 커피챗에 내가 수료를 하게 되면 어떤 기술을 더 배우는게 좋을지 그리고 당시 생각하던 공부 방향을 말씀드리고 현업자 분들은 어떻게 생각하시는지 물어 보았다. 그날 커피챗만으로도 뭘 더 공부해야할지, 기업의 크기, 형태에 따라 다르겠지만 현업에 계신분들은 어떤 능력을 갖고 있는 인재를 원하는지 알게 되는 시간이였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엘리스트랙에서 배운 내용 중 현업에서 도움되는 것&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크, 클라우드, 컨테이너 개념이 많이 약했는데 엘리스트랙에서 배운 내용이 밑 바탕이 되어 지금은 그 위에 차곡차곡 쌓아갈 수 있게 된것 같다. (아예 모르고 왔으면 난리날 뻔)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엘리스트랙 과정을 돌아보면 (힘들었던 것 / 좋았던 것)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힘들었던 기억은 마지막 프로젝트였던 것 같다. 좀비처럼 집에 박혀서 코딩만 했던것 같다.. ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트러블, 교육의 불만 이런 문제가 아닌 진짜 그냥 육체적으로 힘들었다 ㅋㅋㅋㅋㅋㅋㅋㅋ&lt;/p&gt;
&lt;p data-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;엘리스트랙을&amp;nbsp;추천한다면&amp;nbsp;어떤&amp;nbsp;분들께&amp;nbsp;추천하고&amp;nbsp;싶나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 열정을 불어넣고 싶고 주변에 같이 공부하는 누군가 있으면 좋겠다 하시는분들에게 추천합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글을 마치며 얼마전에 우연히 더딩 크루거 현상을 알게 되었는데 이 말이 너무 좋다. 알면 알수록 더 모른다는 뜻이다. 내가 뭘 모르는지 알아야 그걸 알아갈 수 있다. 혼자 우물안에 있으면 내가 무엇을 모르는지 알 수 없다. 세상엔 고수가 많고 고수가 아니더라도 나보다 특정 부분에서 뛰어난 사람은 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가와 함께 공부하고 싶다, 내가 뭘 모르는지 모르겠다, 누군가에게 물어보면서 공부하고 싶다. 라는 생각이 있는 분들이 의지(의지가 제일 중요)만 가지고 엘리스트랙에 참가해보면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&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_1725625766972&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;성장이 실감나는 IT 부트캠프, 엘리스트랙&quot; data-og-description=&quot;현업 개발자들이 추천하는 국비지원 부트캠프, 프론트엔드, 백엔드, 웹 프로그래밍, 인공지능(AI), 데이터분석, 사물인터넷(IoT)까지! 개발자 성장 플랫폼 엘리스트랙에서 시작하세요&quot; data-og-host=&quot;elice.training&quot; data-og-source-url=&quot;https://elice.training/&quot; data-og-url=&quot;https://elice.training/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bt3ZiE/hyWZf6JWC1/Cmq8yx7Ls9CxyXe7mbjZQK/img.jpg?width=1402&amp;amp;height=1262&amp;amp;face=536_339_1366_717,https://scrap.kakaocdn.net/dn/bG42aW/hyWZadi3b4/s9jTl9WkArcn6QkvW288F0/img.png?width=2402&amp;amp;height=1262&amp;amp;face=806_855_903_960,https://scrap.kakaocdn.net/dn/bn2Xa3/hyWZlFTJvM/qrbKgdshne3epBkeNwItak/img.png?width=2402&amp;amp;height=1262&amp;amp;face=0_0_2402_1262&quot;&gt;&lt;a href=&quot;https://elice.training/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://elice.training/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bt3ZiE/hyWZf6JWC1/Cmq8yx7Ls9CxyXe7mbjZQK/img.jpg?width=1402&amp;amp;height=1262&amp;amp;face=536_339_1366_717,https://scrap.kakaocdn.net/dn/bG42aW/hyWZadi3b4/s9jTl9WkArcn6QkvW288F0/img.png?width=2402&amp;amp;height=1262&amp;amp;face=806_855_903_960,https://scrap.kakaocdn.net/dn/bn2Xa3/hyWZlFTJvM/qrbKgdshne3epBkeNwItak/img.png?width=2402&amp;amp;height=1262&amp;amp;face=0_0_2402_1262');&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;성장이 실감나는 IT 부트캠프, 엘리스트랙&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현업 개발자들이 추천하는 국비지원 부트캠프, 프론트엔드, 백엔드, 웹 프로그래밍, 인공지능(AI), 데이터분석, 사물인터넷(IoT)까지! 개발자 성장 플랫폼 엘리스트랙에서 시작하세요&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;elice.training&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>엘리스클라우드트랙</category>
      <category>엘리스트랙</category>
      <category>엘리스트랙 백엔드</category>
      <category>엘리스트랙 백엔드 후기</category>
      <category>엘리스트랙 취업 후기</category>
      <category>엘리스트랙 후기</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/102</guid>
      <comments>https://programmingjun.tistory.com/102#entry102comment</comments>
      <pubDate>Fri, 6 Sep 2024 21:50:18 +0900</pubDate>
    </item>
    <item>
      <title>엘리스 클라우드 트랙을 마치며</title>
      <link>https://programmingjun.tistory.com/101</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;엘리스 Cloud트랙에 들어온 계기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엘리스 트랙에 참여하기 전 나는 1년 반동안 혼자 Java 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;예전 오프라인 스터디에 SW 트랙을 수료하고 취업한 스터디원이 있었고 엘리스 교육과정을 추천한 적이 있었다. 그 당시에는 내가 공부하던 Java , Spring 과정이 없어 참여하지 못했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우연히 SNS를 통해 Java, Spring 클라우드 1기 모집글을 접하여 신청하게 되었고 열심히 달려 수료까지 하게 되었다.&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;Cloud트랙에서 학습 후기&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 트랙을 진행하면서 가장 좋았던 부분은&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째로 학습에 대한 방향성이 생겼다. 독학으로는 쉽게 이해가 가지 않았던 부분을 현업에 있는 코치님들이 알려주시고, 실무에서 쓰이는 예시를 들을 수 있는 등 어떤 것을 학습해야 하고 왜 학습해야 하는지 방향성을 잡을 수 있어 좋았다.&lt;/li&gt;
&lt;li&gt;두 번째로는 학습에 대한 열정을 다시 가질 수 있게 되었던 부분이 좋았다. 지속적인 구직 실패로 의지가 꺾여있던 나에게 하나씩 공부하며 해나가는 과정에서 자신감을 다시 얻을 수 있었고 열심히 공부해 나가는 것에 흥미를 느끼게 된 것 같다.&lt;/li&gt;
&lt;li&gt;세 번째로는 여러 레이서들과 함께 공부하면서 같이 공부할 사람들을 만들 수 있었던 점이 좋았다. 독학을 오래 하면서 여러 스터디도 전전긍긍해 보고 여러 사이드 프로젝트도 찾아보고 했으나 쉽게 찾아내거나 설령 들어간다 하더라도 쉽게 유지하지 못하는 경우가 많았는데 클라우드 트랙에 참여하면서 함께 공부할 수 있는 사람들이 많이 생긴 거 같다.&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1707&quot; data-origin-height=&quot;795&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vyuxr/btsHOfnBztK/uurdBgCkK1mBkVz9zjCnVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vyuxr/btsHOfnBztK/uurdBgCkK1mBkVz9zjCnVK/img.png&quot; data-alt=&quot;실시간 온라인 강의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vyuxr/btsHOfnBztK/uurdBgCkK1mBkVz9zjCnVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvyuxr%2FbtsHOfnBztK%2FuurdBgCkK1mBkVz9zjCnVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1707&quot; height=&quot;795&quot; data-origin-width=&quot;1707&quot; data-origin-height=&quot;795&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실시간 온라인 강의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3개의&amp;nbsp;프로젝트&amp;nbsp;내용&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로젝트는 1차 / 2차 / 3차로 나뉘어 진행되었다.&lt;/h4&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;1달간 학습한&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; Springframwark의 기본 기능을 적용해보는 프로젝트였다. thymeleaf를 사용하여 MVC모델로&amp;nbsp; 개인 프로젝트를 만들었으며&amp;nbsp;&amp;nbsp;Spring의 IOC와 DI 개념을 이해하고 Bean Container를 통한 생명 주기 관리를 이해할 수 있었다. 감사하게도 프로젝트 우수상을 수상할 수 있었다.&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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2차 프로젝트&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;SringSecurity, AOP와 같은 서비스의 완성도를 높일 수 있는 기능에 대한 학습을 한 후 클라우드 트랙의 인원들과 함께 프로젝트를 진행하였다. RestAPI를 기반으로 하는 기능 구현에 초점이 있었으며, 프트도 일부 만들어야 하는 어려움이 있었지만 네트워크 전체적인 흐름을 이해하는데 도움이 되었다.&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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;3차 프로젝트&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;엘리스 SW, AI 트랙을 수료한 수료생들과 함께 협업하여 진행하는 프로젝트였다. 2차 프로젝트 이후 학습한 AWS 와 Docker 등 클라우드 서비스에 필요한 기술들을 기반으로 프론트엔드와 협업하여 자유 주제 프로젝트를 진행하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;AWS 서비스를 활용할 수 있었고 프론트엔드와 백엔드의 업무를 분배해 주어 시간을 더 효율적으로 배분할 수 있었다. 때문에 CI/CD 기본적인 구축을 해볼 수 있었고 서버 모니터링 등 프로젝트 완성도를 좀 더 높일 수 있었다.&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 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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMbOkb/btsHPw9t5mX/EsdkOJ76ub9l53K8wo81kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMbOkb/btsHPw9t5mX/EsdkOJ76ub9l53K8wo81kK/img.png&quot; data-alt=&quot;3차 프로젝트 발표자료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMbOkb/btsHPw9t5mX/EsdkOJ76ub9l53K8wo81kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMbOkb%2FbtsHPw9t5mX%2FEsdkOJ76ub9l53K8wo81kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3차 프로젝트 발표자료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7b8Uc/btsHNoS5g7p/NN6iygZCsSyDRvt8q7JMWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7b8Uc/btsHNoS5g7p/NN6iygZCsSyDRvt8q7JMWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7b8Uc/btsHNoS5g7p/NN6iygZCsSyDRvt8q7JMWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7b8Uc%2FbtsHNoS5g7p%2FNN6iygZCsSyDRvt8q7JMWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;231&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&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;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b12y87/btsHO3tbtjZ/AQI7RPOIfhZbsoAm9kjUz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b12y87/btsHO3tbtjZ/AQI7RPOIfhZbsoAm9kjUz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b12y87/btsHO3tbtjZ/AQI7RPOIfhZbsoAm9kjUz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb12y87%2FbtsHO3tbtjZ%2FAQI7RPOIfhZbsoAm9kjUz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;231&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엘리스 플랫폼 사용 후기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엘리스 트랙을 진행하면서 가장 좋았던 부분은 실시간 강의를 통한 학습이었다. 실시간으로 현업자와 소통하며 피드백을 받을 수 있는 상황이 매우 좋았다.&lt;/li&gt;
&lt;li&gt;또한 엘리스 플랫폼의 강의들을 무료로 접근해서 볼 수 있다는 점이 좋았다. 물론 강의별로 더 추가적으로 알고 싶으면 본인이 직접 다른 경로를 통해 구매하여 강의를 사거나 해야 하지만 강의의 질이 양호하고 트랙 진행 중에는 클라우드 트랙의 강의뿐 아닌 알고리즘 강의도 제공받을 수 있었다.&lt;/li&gt;
&lt;li&gt;수료한 지금 시점에는 SLQD, 쿠버네틱스, AWS, AI 강의등 다양한 강의를 무상으로 제공 받을 수 있고 심지어 수료 후 6개월 동안 취득한 자격증에 대한 응시료 환급까지 해주는 배려를 해주어 만족도가 매우 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영 매니지먼트&lt;/h3&gt;
&lt;p data-ke-size=&quot;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;출결 관리 일화로 출첵 시간에 못 일어난 적이 있었는데 &lt;s&gt;직접 전화까지 해주셔서 출석을 찍어주셨다는.. ㅋㅋ&lt;/s&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;엘리스랩&amp;nbsp;이용&amp;nbsp;후기&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;h3 data-ke-size=&quot;size23&quot;&gt;트랙을 마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6개월 트랙을 시작하면서 처음엔 그동안 공부를 오래 했는데 배우는 것들이 의미가 없어 시간 낭비가 되지 않을까? 내가 이 6개월 과정을 끝까지 할 수 있을까?라는 의구심이 많았다. 현재 트랙을 마치고 회고해 보았을 때 과거에 나에게 &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;굳이 최적의 타이밍을 따지자면 Java 언어를 학습한 후 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;&lt;a href=&quot;https://elice.training/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://elice.training/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1717503573552&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;현업 개발자들이 추천하는 국비지원 부트캠프, 프론트엔드, 백엔드, 웹 프로그래밍, 인공지능(AI), 데이터분석, 사물인터넷(IoT)까지! 개발자 성장 플랫폼 엘리스트랙에서 시작하세요&quot; data-og-host=&quot;elice.training&quot; data-og-source-url=&quot;https://elice.training/&quot; data-og-url=&quot;https://elice.training/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cVYx9C/hyWgYRy4zU/rR0FW7Exrtj0zhYOPOtHa1/img.png?width=2402&amp;amp;height=1262&amp;amp;face=0_0_2402_1262,https://scrap.kakaocdn.net/dn/KiOsh/hyWg6vgE4l/UAQspv7swkKDzlGJfihSR0/img.png?width=2402&amp;amp;height=1262&amp;amp;face=806_855_903_960&quot;&gt;&lt;a href=&quot;https://elice.training/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://elice.training/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cVYx9C/hyWgYRy4zU/rR0FW7Exrtj0zhYOPOtHa1/img.png?width=2402&amp;amp;height=1262&amp;amp;face=0_0_2402_1262,https://scrap.kakaocdn.net/dn/KiOsh/hyWg6vgE4l/UAQspv7swkKDzlGJfihSR0/img.png?width=2402&amp;amp;height=1262&amp;amp;face=806_855_903_960');&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;현업 개발자들이 추천하는 국비지원 부트캠프, 프론트엔드, 백엔드, 웹 프로그래밍, 인공지능(AI), 데이터분석, 사물인터넷(IoT)까지! 개발자 성장 플랫폼 엘리스트랙에서 시작하세요&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;elice.training&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>백엔드 국비지원</category>
      <category>백엔드개발자</category>
      <category>백엔드부트캠프</category>
      <category>엘리스 cloud트랙</category>
      <category>엘리스클라우드트랙</category>
      <category>엘리스트랙</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/101</guid>
      <comments>https://programmingjun.tistory.com/101#entry101comment</comments>
      <pubDate>Tue, 4 Jun 2024 21:22:32 +0900</pubDate>
    </item>
    <item>
      <title>sequelize 사용법(1) - 모델(Entity)설정</title>
      <link>https://programmingjun.tistory.com/100</link>
      <description>&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;sequelize 는 node.js의 ORM 라이브러리중 하나로 여러 &lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;RDBMS를 지원하고 Promise 기반으로 구현되었기 때문에 비동기 로직을 편리하게 작성할 수 있게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;sequelize 설정 (model/index.js)&lt;/h3&gt;
&lt;pre id=&quot;code_1717078355760&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];
const db = {};
const sequelize = new Sequelize(config.database, config.username, config.password, config);

db.User = require('./user')(sequelize, Sequelize)
db.Post = require('./post')(sequelize, Sequelize)
db.Hashtag = require('./hashtag')(sequelize, Sequelize)
db.Comment = require('./comment')(sequelize, Sequelize)
db.Image = require('./image')(sequelize, Sequelize)

Object.keys(db).forEach(modelName =&amp;gt; {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;&lt;/code&gt;&lt;/pre&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. env 파일에 정의한 DB의 커넥션 정보를 가져와서 Sequelize의 객체 생성시 인자로 부여한다 (커넥션 연결)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. db라는 객체를 만든 후 모델(Entity)로 정의한 js를 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. db안에 들어온 모델을 루프돌면서 연관관계를 맺어준다.&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;Model&lt;/h2&gt;
&lt;pre id=&quot;code_1717079855399&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = (sequelize, DataTypes) =&amp;gt; {
    const User =  sequelize.define('User',{
        email: {
            type: DataTypes.STRING(30),
            allowNull: false,
            unique: true,
        },
        nickname : {
            type: DataTypes.STRING(20),
            allowNull: false,
        },
        password: {
            type: DataTypes.STRING(100),
            allowNull: false,
        },
    },{
        charset: 'utf8',
        collate: 'utf8_general_ci'
    });
    User.associate = (db) =&amp;gt; {
        db.User.hasMany(db.Post,{as: 'Posts'});
        db.User.hasMany(db.Comment);
        db.User.belongsToMany(db.Post, {through: 'Heart', as: 'Hearted'})
        db.User.belongsToMany(db.User, {through: 'Follow', as: 'Followings', foreignKey: 'FollowerId'})
        db.User.belongsToMany(db.User, {through: 'Follow', as: 'Followers', foreignKey: 'FollowingId'})
    };
    return User;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. model.export 함수는 스프링의 auto-ddl-create와 같은 역할을 하는 함수이며 difine함수의 두번째 인자로 컬럼의 데이터 타입과 옵션을 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. id 필드 같은 경우 데이터베이스의 PK 전략을 따른다.&lt;/p&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. associate 함수 같은 경우 Entity간 연관관계를 정의하는 함수이다.&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;1:N 연관관계&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;hasMany 는 해당 모델에 1:N 구조의 연관관계를 지정하는것을 말하며 Spring JPA의 @OneToMany와 유사하다.&lt;/li&gt;
&lt;li&gt;as를 사용하여 리스트에 대한 별칭을 지정할 수 있으며 as가 생략된 경우 복수형으로 바뀌어 정의된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;N:1 연관관계&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;belongsTo 로 정의 하며 N:1 구조로 연관관계를 맺는다. Spring JPA의 @ManyToOne 과 유사하며&lt;/li&gt;
&lt;li&gt;N:1 연관관계의 특성에 맞게 FK를 갖도록 테이블이 구성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;N:N 연관관계&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;belongsToMany 로 정의하며 N:N의 구조로 연관관계가 구성된다. Spring JPA의 @ManyToMany와 유사하며&lt;/li&gt;
&lt;li&gt;자동으로 중간테이블이 생성된다. 테이블의 명칭도 지정할 수 있으며 FK의 지정도 가능하다.&lt;/li&gt;
&lt;li&gt;의미 있게 사용하려면 중간테이블 모델을 따로 빼는게 맞을 것 같다.(JPA에서는 그렇게 사용했었음)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>express</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/100</guid>
      <comments>https://programmingjun.tistory.com/100#entry100comment</comments>
      <pubDate>Thu, 30 May 2024 23:51:00 +0900</pubDate>
    </item>
    <item>
      <title>await 와 async</title>
      <link>https://programmingjun.tistory.com/99</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;컴파일 언어인 java와 그 프레임워크 spring을 기반으로 공부했던터라 자바스크립트에 대한 이해가 부족했었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node.js 로 백엔드 예제를 만들다가 만난 경험을 적어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 싱글 스레드 기반의 인터프리터 언어이다. 때문에 동시성을 갖고 작업을 하기 위해서는 비동기 방식이 쓰이게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 일어난다는 뜻을 가지고 있고 어떤 요청이 들어왔을 때 응답이 한자리에서 동시에 일어난다는 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한가지 작업을 하면 결과가 반환 될 때까지 다른 작업이 대기한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비동기&lt;/h3&gt;
&lt;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;이때 사용되는 대표적인 방법이 콜백함수와 Promise객체가 있다.&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_1716901783019&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;getAllSum(function(x){
    getSum(x, function(y){
    	getPow(y){
        	return y*y;
        }
    });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로미스 예시&lt;/h4&gt;
&lt;pre id=&quot;code_1716901908637&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fetch('http://localhost:3060/api')
    .then(response =&amp;gt; response.json())
    .then(json =&amp;gt; ....)
    .catch(error =&amp;gt; constol.error(error));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제에 대한 해결로 await async가 탄생하였다.&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;await / async 예시&lt;/h4&gt;
&lt;pre id=&quot;code_1716902003710&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function getData() {
    const response = await fetch('https://example.com/api');
    const data = await response.json();
    const response2 = await fetch(`https://example.com/api/${data.id}`);
    const data2 = await response2.json();
    console.log(data2);
}

getData();&lt;/code&gt;&lt;/pre&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 키워드 앞에 async를 붙혀주고 비동기로 처리되는 작업에 await을 붙여 해당 작업의 결과를 기다려 다른 작업에서 참조가 가능하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async/await 는 내부적으로는 Promise를 사용해서 비동기를 처리하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 작성 부분의 유지보수 위해 문법적 편의성만 제공해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부하게 된 계기로 직면했던 트러블 슈팅을 간단하게 적어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기존 코드&lt;/h4&gt;
&lt;pre id=&quot;code_1716902223706&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return req.login(user, async(loginErr)=&amp;gt;{
            if(loginErr){
                console.error(loginErr);
                return next(loginErr);
            }
            const userResponse = User.findOne({
                where:{ id: user.id },
                attributes: {
                    exclude: ['password']
                },
                include: [{
                    model: Post,
                    as: 'Posts',
                },{
                    model: User,
                    as: 'Followings',
                },{
                    model: User,
                    as: 'Followers',
                }]
            });
            return res.status(200).json(userResponse);
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User.findOne()을 통해 User DB를 조회하여 올 때 await을 적용시켜 주지 않아 아래와 같이 참조하지 못하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;오류&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1613&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL28Un/btsHEwKvbrN/k1RLnh936Su0KGig1Oi4e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL28Un/btsHEwKvbrN/k1RLnh936Su0KGig1Oi4e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL28Un/btsHEwKvbrN/k1RLnh936Su0KGig1Oi4e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL28Un%2FbtsHEwKvbrN%2Fk1RLnh936Su0KGig1Oi4e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1613&quot; height=&quot;698&quot; data-origin-width=&quot;1613&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 만났을 때 쿼리도 정상적으로 잘 나가서 한동안 찾지 못하였지만 await 가 붙지 않아 비동기 처리에 문제가 있었음을 찾을 수 있었다.&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;수정 (await 키워드 추가)&lt;/h4&gt;
&lt;pre id=&quot;code_1716902447726&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return req.login(user, async(loginErr)=&amp;gt;{
            if(loginErr){
                console.error(loginErr);
                return next(loginErr);
            }
            //수정한 부분
            const userResponse = await User.findOne({
                where:{ id: user.id },
                attributes: {
                    exclude: ['password']
                },
                include: [{
                    model: Post,
                    as: 'Posts',
                },{
                    model: User,
                    as: 'Followings',
                },{
                    model: User,
                    as: 'Followers',
                }]
            });
            return res.status(200).json(userResponse);
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결과&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1601&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rVrHa/btsHF2BslNo/NS3EQtD4URvJ6654rTNJUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rVrHa/btsHF2BslNo/NS3EQtD4URvJ6654rTNJUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rVrHa/btsHF2BslNo/NS3EQtD4URvJ6654rTNJUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrVrHa%2FbtsHF2BslNo%2FNS3EQtD4URvJ6654rTNJUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1601&quot; height=&quot;747&quot; data-origin-width=&quot;1601&quot; data-origin-height=&quot;747&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>JavaScript</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/99</guid>
      <comments>https://programmingjun.tistory.com/99#entry99comment</comments>
      <pubDate>Tue, 28 May 2024 22:22:54 +0900</pubDate>
    </item>
    <item>
      <title>React Hook</title>
      <link>https://programmingjun.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;React를 공부하면서 알게된 기능을 간단하게 정리해본다. 이후 상세하게 파고들어봐야겠다.&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;React가 지원하는 Hook&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;u&gt;&lt;b&gt;useState()&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value에 해당하는 값을 관리할 수 있고 setter를 호출하여 value의 값을 변한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 value에 객체를 선언하여 전체적으로 변경할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1716467081576&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; import {useState} from 'react';

 const [value , setter] = useState({초기화 값});&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;u&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;useEffect()&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;useEffect는 렌더링 이후에 실행되고, 특정 조건을 만족하면 호출하여 실행하도록 설계한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716469233236&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useEffect } from 'react';

useEffect(()=&amp;gt;{
        if(postAdded){
            setText('');        
        }
}, [postAdded])&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;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;컴포넌트의 성능을 최적화 하는데 사용되는 훅&lt;/span&gt;&lt;/h3&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;u&gt;&lt;b&gt;useMemo()&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장하여 동일한 계산의 반복 수행을 막고 메모리를 효율적으로 쓰게하는 기능.&lt;/span&gt;&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;객체도 저장 가능함으로 styled-component처럼 컴포넌트 최적화에도 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716469455475&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useMemo } from 'react';

const style = useMemo(() =&amp;gt; (
	{ marginBotton:'20px', border:'1px colid #d9d9d9', padding:'20px'}
   	),[])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&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: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;함수를 저장하는 &lt;u&gt;&lt;b&gt;useCallback() &lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;useCallback()&lt;/span&gt;&amp;nbsp; 또한 메모이제이션 기법으로 컴포넌트 성능을 최적화 시켜주는 도구이다.useMemo()와 다른 점은 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;useCallback은 인자로 전달한 콜백 함수 그 자체를 메모이제이션 하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716469565157&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useCallback } from 'react';

const onSubmit = useCallback(()=&amp;gt; {
	if(password !== passwordCheck){
        return setPasswordError(true);
    }
    if(!term){
        return setTermError(ture);
    }
},[email,password, passwordCheck, term]);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;DOM 요소에 접근하기 위해 사용하는 &lt;u&gt;&lt;b&gt;useRef()&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;useRef는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;저장공간 또는 DOM요소에 접근하기 위해 사용되는 React Hook&lt;/b&gt;이다. Ref는 reference를 의미한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트의 querySelector와 유사.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;input 태그의 요소를 참조해 오기 위해 사용한 예시&lt;/p&gt;
&lt;pre id=&quot;code_1716469674552&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useRef } from 'react';

    const imageInput = useRef();
    
    const onClickImageUpload = useCallback(()=&amp;gt;{
        imageInput.current.click();
    },[imageInput.current]);
    
    
    //...
    &amp;lt;input type='file' multiple hidden ref={imageInput}/&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>React</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/98</guid>
      <comments>https://programmingjun.tistory.com/98#entry98comment</comments>
      <pubDate>Thu, 23 May 2024 22:10:24 +0900</pubDate>
    </item>
    <item>
      <title>@FeignClient 요청 및 에러 핸들링</title>
      <link>https://programmingjun.tistory.com/97</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;b&gt;기본 세팅 이전 포스팅 참고&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmingjun.tistory.com/96&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://programmingjun.tistory.com/96&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716287308703&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;@FeignClient 기본 세팅&quot; data-og-description=&quot;프로젝트에서 외부 api에 요청을 보내야할 경우들이 있어 FeinClient를 사용해보았다. 이 과정중에 발생한 에러를 해결하며 공부하게된 내용들을 정리해본다.&amp;nbsp;의존성 관리해당 의존성은 Springboot 3.&quot; data-og-host=&quot;programmingjun.tistory.com&quot; data-og-source-url=&quot;https://programmingjun.tistory.com/96&quot; data-og-url=&quot;https://programmingjun.tistory.com/96&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6bKni/hyV58BxJVp/iDyhQBJau7f6vvVgky1VcK/img.png?width=800&amp;amp;height=408&amp;amp;face=0_0_800_408,https://scrap.kakaocdn.net/dn/UMjVy/hyV6f1J0wQ/veZfmgk40VeTK1W85BBQq1/img.png?width=800&amp;amp;height=408&amp;amp;face=0_0_800_408,https://scrap.kakaocdn.net/dn/ymkS6/hyV90V9UsI/ZInnkkP2zYjhcb0Axe1dYK/img.png?width=1733&amp;amp;height=803&amp;amp;face=0_0_1733_803&quot;&gt;&lt;a href=&quot;https://programmingjun.tistory.com/96&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmingjun.tistory.com/96&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6bKni/hyV58BxJVp/iDyhQBJau7f6vvVgky1VcK/img.png?width=800&amp;amp;height=408&amp;amp;face=0_0_800_408,https://scrap.kakaocdn.net/dn/UMjVy/hyV6f1J0wQ/veZfmgk40VeTK1W85BBQq1/img.png?width=800&amp;amp;height=408&amp;amp;face=0_0_800_408,https://scrap.kakaocdn.net/dn/ymkS6/hyV90V9UsI/ZInnkkP2zYjhcb0Axe1dYK/img.png?width=1733&amp;amp;height=803&amp;amp;face=0_0_1733_803');&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;@FeignClient 기본 세팅&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트에서 외부 api에 요청을 보내야할 경우들이 있어 FeinClient를 사용해보았다. 이 과정중에 발생한 에러를 해결하며 공부하게된 내용들을 정리해본다.&amp;nbsp;의존성 관리해당 의존성은 Springboot 3.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmingjun.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;전역, 부분적으로 request를 핸들링하기 위해서 Configuragion을 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 전역적인 요청에 header를 세팅한다거나 특정 외부 api를 호출할 때 동일하게 header를 세팅해야 할 경우&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;전역 RequestInterceptor 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Configuration을 통해 전역의 요청을 설정할 수 있다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class GlobalFeignConfig {
    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -&amp;gt; requestTemplate.header(&quot;global-config-header&quot;, &quot;GlobalFeignConfig&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;631&quot; data-origin-height=&quot;825&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjTfBR/btsHxCpbAos/Pno31pZFAihGDv3DefrsMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjTfBR/btsHxCpbAos/Pno31pZFAihGDv3DefrsMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjTfBR/btsHxCpbAos/Pno31pZFAihGDv3DefrsMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjTfBR%2FbtsHxCpbAos%2FPno31pZFAihGDv3DefrsMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;825&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;825&quot;/&gt;&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;부분적 RequestInterceptor 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 특정 외부 api에만 요청 설정을 해야 할 경우 아래와 같이 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Configuration 을 사용하지 않은 이유는 전역으로 설정할 것이 아닌 특정 환경에서만 활용 가능하도록 하기 위함이다&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class FeignClientConfig {
    Map&amp;lt;String, Collection&amp;lt;String&amp;gt;&amp;gt; headers = new HashMap&amp;lt;&amp;gt;();
    @Bean
    public RequestInterceptor requestInterceptor() {
        headers.put(&quot;global-config-header&quot;, List.of(&quot;GlobalFeignConfig&quot;));
        headers.put(&quot;custom-config-header1&quot;, List.of(&quot;FeignClientConfig1&quot;));
        headers.put(&quot;custom-config-header2&quot;, List.of(&quot;FeignClientConfig2&quot;));

        return requestTemplate -&amp;gt; requestTemplate.headers(headers);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@FeignClient 어노테이션에서 configuration 옵션에 설정을 지정하면 해당 RequestInterceptor를 적용시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 인터셉터의 설정이 아닌 옵션에 부여한 인터셉터가 적용된다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;example&quot;, url = &quot;http://localhost:8282&quot;, configuration = FeignClientConfig.class)
public interface FeignClientService {
    @GetMapping(value = &quot;/test&quot;)
    Result getInfo(
            @RequestHeader(value = &quot;Authorization&quot;, required = false) String token,
            @RequestParam(name = &quot;name&quot;, required = false)String name,
            @RequestParam(name = &quot;age&quot;, required = false) Integer age,
            @RequestParam(name = &quot;status&quot; , required = false)Integer status);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;738&quot; data-origin-height=&quot;865&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXql2E/btsHwb7nZDb/REH4KxeKkYydwkFH4RK1FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXql2E/btsHwb7nZDb/REH4KxeKkYydwkFH4RK1FK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXql2E/btsHwb7nZDb/REH4KxeKkYydwkFH4RK1FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXql2E%2FbtsHwb7nZDb%2FREH4KxeKkYydwkFH4RK1FK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;865&quot; data-origin-width=&quot;738&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;요청 핸들링 결론 : 전역적으로 @Configuration를 붙여 전역적으로 핸들링하고 , 부분적으로 요청 핸들링 해야 하는 경우 @Configuration이 붙지 않은 설정을 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러 핸들링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커스텀 에러 세팅&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;에러 코드&lt;/h4&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@AllArgsConstructor
@Builder
@Getter
public class ErrorCode {
    private final HttpStatus httpStatus;
    private final String message;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;커스텀 익셉션&lt;/h4&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@AllArgsConstructor
@Getter
public class CustomException extends RuntimeException{
    private ErrorCode errorCode;

    @Override
    public String getMessage() {
        return errorCode.getMessage();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;에러 반환 계층&lt;/h4&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class ErrorResponse {
    private int status;
    private String code;
    private String message;

    // 커스텀에서 사용시
    public static ResponseEntity&amp;lt;ErrorResponse&amp;gt; ofResponse(ErrorCode e) {
        return ResponseEntity
                .status(e.getHttpStatus())
                .body(ErrorResponse.builder()
                        .status(e.getHttpStatus().value())
                        .code(e.getHttpStatus().name())
                        .message(e.getMessage())
                        .build()
                );
    }
    // 이외 익셉션
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RestControllerAdvice
@Order(1)
@Slf4j
public class ControllerAdvice {

    @ExceptionHandler(CustomException.class)
    public ResponseEntity&amp;lt;ErrorResponse&amp;gt; handleCustomException(CustomException e) {
        return ErrorResponse.ofResponse(e.getErrorCode());
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;@FeignClient 설정 추가&lt;/h4&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class GlobalFeignConfig {
    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -&amp;gt; requestTemplate.header(&quot;global-config-header&quot;, &quot;GlobalFeignConfig&quot;);
    }
    // 애러 디코더 Bean 설정
    @Bean
    public ErrorDecoder errorDecoder() {
        return new CustomFeignErrorDecoder();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ErrorDecoder 구현&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 api 호출 시 애러가 발생하면 디코딩해주는 구현체 애러 핸들링을 어떻게 할지는 커스텀하는 방식에 따라 다를 것 같다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Slf4j
public class CustomFeignErrorDecoder implements ErrorDecoder {

    @SneakyThrows
    @Override
    public Exception decode(String s, Response response) {
        //HttpStatus
        HttpStatus responseStatus = HttpStatus.valueOf(response.status());

        //메시지 파싱
        String requestUrl = response.request().url();
        Response.Body body = response.body();
        JSONParser jp = new JSONParser();
        JSONObject parseBody = (JSONObject) jp.parse(new InputStreamReader(body.asInputStream()));
        String message = &quot;[URL] : &quot; + requestUrl + &quot; [Body] :&quot; + parseBody.toString();

        if (responseStatus.is5xxServerError()) {
            //5xx 애러 핸들링
            return new CustomException(ErrorCode.builder().httpStatus(responseStatus).message(message).build());
        } else if (responseStatus.is4xxClientError()) {
            //4xx 애러 핸들링
            return new CustomException(ErrorCode.builder().httpStatus(responseStatus).message(message).build());
        } else {
            return new Exception(&quot;Generic exception&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터로 status를 받아 그 상태를 그대로 반환하게 설정한 외부 api 애플리케이션에서 404를 반환하였고 다시 받아서 애러 핸들링 한 모습니다.&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;404 발생 시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1707&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cidfel/btsHxCvUTCx/0frfhUKFg4KXYHo7rgHuXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cidfel/btsHxCvUTCx/0frfhUKFg4KXYHo7rgHuXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cidfel/btsHxCvUTCx/0frfhUKFg4KXYHo7rgHuXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcidfel%2FbtsHxCvUTCx%2F0frfhUKFg4KXYHo7rgHuXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1707&quot; height=&quot;786&quot; data-origin-width=&quot;1707&quot; data-origin-height=&quot;786&quot;/&gt;&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;400 발생 시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be2cHE/btsHv5TRTHU/3waJOXFJVXCEhDRQHTRZr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be2cHE/btsHv5TRTHU/3waJOXFJVXCEhDRQHTRZr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be2cHE/btsHv5TRTHU/3waJOXFJVXCEhDRQHTRZr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe2cHE%2FbtsHv5TRTHU%2F3waJOXFJVXCEhDRQHTRZr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;720&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Spring</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/97</guid>
      <comments>https://programmingjun.tistory.com/97#entry97comment</comments>
      <pubDate>Wed, 22 May 2024 21:35:13 +0900</pubDate>
    </item>
    <item>
      <title>@FeignClient 기본 세팅</title>
      <link>https://programmingjun.tistory.com/96</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 외부 api에 요청을 보내야할 경우들이 있어 FeinClient를 사용해보았다. 이 과정중에 발생한 에러를 해결하며 공부하게된 내용들을 정리해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의존성 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 의존성은 Springboot 3.2.4에 해당하는 의존성이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openfeign 라이브러리는 spring cloud 기반의 라이브러리이다. 때문에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;spring cloud&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;color: #333333; text-align: start;&quot;&gt;spring cloud&lt;span&gt;는&amp;nbsp;&lt;/span&gt;&lt;/span&gt;spring 과 호환성이 중요함으로 버전관리에 신경써야한다. 아래 공식 문서로 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://spring.io/projects/spring-cloud&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://spring.io/projects/spring-cloud&lt;/a&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;ext {
    set('springCloudVersion', &quot;2023.0.0&quot;)
}

dependencyManagement {
    imports {
       mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}

dependencies {
	implementation &quot;org.springframework.cloud:spring-cloud-starter-openfeign&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FeignClient 기술은 RestTemplet과 흡사하지만 직접 API호출 환경을 설정하여 사용하던 RestTemplet 과는 다르게 선언적 방식과 추상화된 인터페이스를 제공함으로써 외부 API 호출이 가능하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 세팅&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 클레스에 @EnableFeignClients 를 추가해주어 하위 패키지에 존재하는 @FeignClient 어노테이션을 찾아 구현체를 만듭니다. (Spring 의 Component와 ComponentScan의 관계와 유사)&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableFeignClients
public class Feign2Application {

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

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@FeignClient 인터페이스&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;@FeignClient 어노테이션 내부의 url 옵션은 호출할 api의 &lt;b&gt;도메인 or IP 주소값&lt;/b&gt;이 입력되어야한다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RequestMapping + @GetMapping 의 경우 두 경로값을 합쳐 EndPoint를 찾아 가는 점과 다르게 url에 접근할 도메인 or 서버의 주소를 기입해주어야한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;테스트 환경 세팅&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. FeignClient 환경(&lt;b&gt;8080&lt;/b&gt; 포트)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-1 FeignClient 인터페이스&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;example&quot;, url = &quot;http://localhost:8282&quot;)
public interface FeignClientService {
    @GetMapping(value = &quot;/test&quot;)
    Result getInfo(
            @RequestHeader(value = &quot;Authorization&quot;, required = false) String token,
            @RequestParam(name = &quot;name&quot;, required = false)String name,
            @RequestParam(name = &quot;age&quot;, required = false) Integer age,
            @RequestParam(name = &quot;status&quot; , required = false)Integer status);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;1-2 FeignClient&lt;span&gt; &lt;/span&gt;&lt;/span&gt;컨트롤러&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RestController
@RequiredArgsConstructor
@Slf4j
public class FeignClientController {
    private final FeignClientService feignClientService;

    @GetMapping(&quot;/feign&quot;)
    public ResponseEntity&amp;lt;Result&amp;gt; feignClientTest(
            @RequestHeader(HttpHeaders.AUTHORIZATION) String header,
            @RequestParam(name = &quot;name&quot;, required = false) String name,
            @RequestParam(name = &quot;age&quot;, required = false) Integer age,
            @RequestParam(name = &quot;status&quot;, required = false) Integer status) {
        log.info(&quot;feign 호출&quot;);
        Result info = feignClientService.getInfo(header, name, age, status);
        return ResponseEntity.ok(info);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 외부 api 환경 세팅 (&lt;b&gt;8282&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;포트)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;받은 요청을 바로 body로 돌려주는 세팅 + 애러 발생 테스트를 위한 HttpStatus를 파라미터 세팅&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;livescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Slf4j
@RestController
public class TestController {

    @GetMapping(&quot;/test&quot;)
    public ResponseEntity&amp;lt;Result&amp;gt; getInfo(HttpServletRequest request , @RequestParam(name = &quot;status&quot;, required = false, defaultValue = &quot;200&quot;) int status) {
        Result result = new Result();
        request.getHeaderNames().asIterator().forEachRemaining((i) -&amp;gt; result.headerResult.add(i + &quot; : &quot; + request.getHeader(i)));
        request.getParameterNames().asIterator().forEachRemaining((i) -&amp;gt; result.parameterResult.add(i + &quot; : &quot; + request.getParameter(i)));
        HttpStatus httpStatus = HttpStatus.valueOf(status);
        return ResponseEntity.status(httpStatus).body(result);
    }
    @Data
    static class Result {
        List&amp;lt;String&amp;gt; headerResult = new ArrayList&amp;lt;&amp;gt;();
        List&amp;lt;String&amp;gt; parameterResult = new ArrayList&amp;lt;&amp;gt;();
        String body;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요청 및 응답&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;8080 포트의 애플리케이션을 통해 8282의 응답을 받아온 결과를 볼 수 있음&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;1733&quot; data-origin-height=&quot;803&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehWJbd/btsHxsfYNnP/0oDkSP5p8ASKbD7wThQdG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehWJbd/btsHxsfYNnP/0oDkSP5p8ASKbD7wThQdG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehWJbd/btsHxsfYNnP/0oDkSP5p8ASKbD7wThQdG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehWJbd%2FbtsHxsfYNnP%2F0oDkSP5p8ASKbD7wThQdG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1733&quot; height=&quot;803&quot; data-origin-width=&quot;1733&quot; data-origin-height=&quot;803&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;잘못 된 사용 예&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 도메인의 api를 호출할 때 아래와 같이 하나의 인터페이스에 몰아 넣고 EndPoint를 합치려 하였으나 애러가 발생했다.&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;google-oauth2-client&quot;, url = &quot;https://&quot;)
public interface GoogleOauth2Client {
    @PostMapping(&quot;oauth2.googleapis.com/token&quot;)
    OAuthTokenResponse getToken(@RequestParam(name = &quot;grant_type&quot;) String grantType,
                                @RequestParam(value = &quot;client_id&quot;) String clientId,
                                @RequestParam(value = &quot;client_secret&quot;) String clientSecret,
                                @RequestParam(value = &quot;redirect_uri&quot;) String redirectUri,
                                @RequestParam(value = &quot;code&quot;) String code);
    
    @GetMapping(value = &quot;www.googleapis.com/oauth2/v2/userinfo&quot;)
    GoogleUserInfo getUserInfo(@RequestHeader(value = HttpHeaders.AUTHORIZATION) String token);

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;span style=&quot;color: #1a5490;&quot;&gt;수정&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 아래와 같이 도메인별로 인터페이스를 나누었다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;google-oauth2-token-client&quot;, url = &quot;https://oauth2.googleapis.com&quot;)
public interface GoogleOauth2TokenClient {
    @PostMapping(&quot;/token&quot;)
    OAuthTokenResponse getToken(@RequestParam(name = &quot;grant_type&quot;) String grantType,
                                @RequestParam(value = &quot;client_id&quot;) String clientId,
                                @RequestParam(value = &quot;client_secret&quot;) String clientSecret,
                                @RequestParam(value = &quot;redirect_uri&quot;) String redirectUri,
                                @RequestParam(value = &quot;code&quot;) String code);

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;google-oauth2-info-client&quot;, url = &quot;https://www.googleapis.com&quot;)
public interface GoogleOauth2InfoClient {
    @GetMapping(value = &quot;/oauth2/v2/userinfo&quot;)
    GoogleUserInfo getUserInfo(@RequestHeader(value = HttpHeaders.AUTHORIZATION) String token);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 해당 기술을 사용하였을 때 &lt;b&gt;전역&lt;/b&gt;, &lt;b&gt;부분적&lt;/b&gt;으로 header등을 컨트롤 하는 방법과 외부 api 호출한 후 애러핸들링 (외부 api 요청이 실패하면 500 서버 애러로 나옴) 을 다룰 예정이다.&lt;/p&gt;</description>
      <category>Spring</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/96</guid>
      <comments>https://programmingjun.tistory.com/96#entry96comment</comments>
      <pubDate>Tue, 21 May 2024 19:18:29 +0900</pubDate>
    </item>
    <item>
      <title>리액트 LifeCycle API</title>
      <link>https://programmingjun.tistory.com/95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에는 요소의 라이프사이클에 따라 사용할 수 있도록 LifeCycle API를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 API를 통해 부모 자식관계의 요소들 사이에서 state와 props를 관리를 수월하게 해 준다.&lt;/p&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 백엔드를 근간에 두고 있어 SpringFramwork에 비유하면서 공부하여 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링에도 라이프사이클에 의해 관리되도록 하는 어노테이션들이 있다. 리액트도 매우 비슷한 기능을 하는 API들을 만들어 놓은 것으로 이해되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SpringBoot에서 라이프사이클에 해당하는 기능들&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Bean Life Cylce에 해당하는&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@PostConstruct&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@PreDestroy&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Bean의 생명주기와는 관련 있진 않지만 엔티티의 생명주기에 대응하도록 만들어진&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@PostPersist&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@PostUpdate&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@PreRemove&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;애러 핸들링을 위한&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RestControllerAdvice&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등과 비교하며 공부하니 이해가 편했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&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;LifeCycle&amp;nbsp;API&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;constructor(props) : 생성자 호출 시 함수 실행&lt;/h4&gt;
&lt;pre id=&quot;code_1715695087346&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;constructor(props) {
    super(props);
    console.log(&quot;constructor&quot;);
  }
  
/* 콘솔 로그
constructor
*/&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;componentDidMount(): 컴포넌트가 마운트 되고 실행되는 함수&lt;/h4&gt;
&lt;pre id=&quot;code_1715695189788&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;constructor(props) {
    super(props);
    console.log(&quot;constructor&quot;);
  }
  componentDidMount() {
    console.log(&quot;componentDidMount&quot;);
    console.log(this.myDiv.getBoundingClientRect());
  }
  
 /*콘솔 로그
 	constructor
 	componentDidMount
	&amp;gt; DOMRect&amp;nbsp;{내용}
 */&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;br /&gt;getDerivedStateFromProps() : 요소가 변경되었을 때 실행되는 함수 -&amp;gt; 생성 시에도 호출됨&lt;/h4&gt;
&lt;pre id=&quot;code_1715695412528&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*컴포넌트 JS 코드 일부*/
  static getDerivedStateFromProps(props, state) {
    if (state.value !== props.value) {
      return {
        value: props.value,
      };
    }
    return null;
  }
  
  /*
  
  */&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1715695700737&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*클릭 함수*/
	handleClick = () =&amp;gt; {
		this.setState({ counter: this.state.counter + 1 });
	};
  
  
/*컴포넌트의 값을 업데이트*/
	{this.state.counter &amp;lt; 10 &amp;amp;&amp;amp; &amp;lt;Mycomponent value={this.state.counter} /&amp;gt;}
	&amp;lt;button onClick={this.handleClick}&amp;gt;Click here&amp;lt;/button&amp;gt;&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;shouldComponentUpdate() : 컴포넌트를 업데이트할 조건을 설정(막을 때 사용)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전값과 같다면 업데이트하지 마라 등의 최적화가 가능하게 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1715695864291&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 받은값이 10이면 업데이트 하지 않음 */
  shouldComponentUpdate(nextProps, nextState) {
    if (this.state.value === 10) {
      return false;
    }
    return true;
  }&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;getSnapshopBeforeUpdate()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 업데이트가 되어서 DOM에 반영되기 직전에 호출되는 함수 DOM의 상태를 return 시켜서 받아오는 등의 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시)&lt;/b&gt; 업데이트되기 전의 scroll 위치 or 인덱스 등의 값을 주어 상태를 유지하는 등 활용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;componentDidUpdate()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 업데이트되고 수행될 함수를 정의&lt;/p&gt;
&lt;pre id=&quot;code_1715696224256&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  componentDidUpdate(prevProps, prevState) {
    if (this.props.value !== prevProps.value) {
      console.log(&quot;value 가 변함&quot;, this.props.value);
    }
  }
  /*
  value 가 변함 6
  value 가 변함 7
  ...
  */&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;componentWillUnmount()&lt;/h4&gt;
&lt;pre id=&quot;code_1715696333192&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  componentWillUnmount() {
    console.log(&quot;good bye&quot;);
  }
  /* 함수 로직 등으로 컴포넌트가 사라지면
	good bye
  */&lt;/code&gt;&lt;/pre&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;componentDidCatch()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 사용되는 부모 계층에서 함수를 작성해야 함&lt;/p&gt;
&lt;pre id=&quot;code_1715696436940&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  componentDidCatch(error, info) {
    console.log(error, info);
  }&lt;/code&gt;&lt;/pre&gt;</description>
      <category>React</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/95</guid>
      <comments>https://programmingjun.tistory.com/95#entry95comment</comments>
      <pubDate>Tue, 14 May 2024 23:36:25 +0900</pubDate>
    </item>
    <item>
      <title>Spring 외부설정 2 (Profile 사용 예)</title>
      <link>https://programmingjun.tistory.com/94</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;267&quot;&gt;&lt;a href=&quot;https://elice.training/track/cloud?utm_source=cloud1&amp;amp;utm_medium=blog&amp;amp;utm_campaign=challenge&amp;amp;utm_content=3LwP6guVX&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GidG2/btsHa0rX5bM/OrNvlVFRCgGLb6OS2UHfs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGidG2%2FbtsHa0rX5bM%2FOrNvlVFRCgGLb6OS2UHfs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1067&quot; height=&quot;267&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;외부 설정 Profile 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;교육 과정 마지막 프로젝트에서 Profile을 적용시키고 실행 시점에 값을 주어 실행시킬만한 좋은 케이스가 생겨서 적용해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 프로젝트에 프로메테우스 그라파나를 설정하여 애플리케이션에 대한 모니터링을 실행하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 모니터링은 로컬 환경에서 실행시킬 때 불필요하고 배포 서버에서 실행될 때 필요한 상황이다. 따라서 배포 시에 VM옵션에 profile=deploy를 주어 배포 시에만 실행되도록 지정하였다.&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;모니터링 외부 설정 profile 지정&lt;/h4&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#그라파나 설정
---
spring:
  config:
    activate:
      on-profile: deploy

management:
  info:
    java:
      enabled: true
    os:
      enabled: true
    env:
      enabled: true
  server:
#    port: 8080
    port: 9292
  endpoint:
#    shutdown:
#      enabled: true
    health:
      #      show-details: always
      show-components: always
  endpoints:
    web:
      exposure:
        include: &quot;*&quot;
#/info? ?????? ??
info:
  app:
    name: iyjy
    company: elice

server:
  tomcat:
    mbeanregistry:
      enabled: true&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해당 프로필에 작동할 클래스&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bean을 등록할 config에서도 특정 프로필에서만 작동하도록 하였다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
@Profile(&quot;deploy&quot;)
public class MeterAspectConfig {


    @Bean
    public CountedAspect countedAspect(MeterRegistry registry) {
        return new CountedAspect(registry);
    }

    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 배포 서버에서 실행시점에 아래와 같이 VM 옵션으로 실행시킬 프로필을 명시하여 특정 상황에만 모니터링 로그를 수집하도록 하였다&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_1714922023445&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nohup java -jar -Dspring.profiles.active=deploy demo-0.0.1-SNAPSHOT.jar &amp;amp;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 로컬에서 default 프로필로 실행했을 때엔 수집 세팅이 되지 않고 배포시 프로필 적용하였을 때는 수집 세팅이 되도록 수정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;1067&quot; data-origin-height=&quot;267&quot;&gt;&lt;a href=&quot;https://elice.training/track/cloud?utm_source=cloud1&amp;amp;utm_medium=blog&amp;amp;utm_campaign=challenge&amp;amp;utm_content=3LwP6guVX&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c333YV/btsHcJhQNUX/mCjNkdEVXB6eXI7LmsuSk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc333YV%2FbtsHcJhQNUX%2FmCjNkdEVXB6eXI7LmsuSk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1067&quot; height=&quot;267&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring</category>
      <category>개발자국비지원</category>
      <category>개발자부트캠프</category>
      <category>국비지원부트캠프</category>
      <category>백엔드</category>
      <category>백엔드학원</category>
      <category>엘리스트랙</category>
      <category>엘리스트랙후기</category>
      <category>온라인코딩부트캠프</category>
      <category>온라인코딩학원</category>
      <category>자바스프링</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/94</guid>
      <comments>https://programmingjun.tistory.com/94#entry94comment</comments>
      <pubDate>Mon, 6 May 2024 00:17:25 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot 커스텀 메트릭 (프로메테우스/그라파나)</title>
      <link>https://programmingjun.tistory.com/93</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;267&quot;&gt;&lt;a href=&quot;https://elice.training/track/cloud?utm_source=cloud1&amp;amp;utm_medium=blog&amp;amp;utm_campaign=challenge&amp;amp;utm_content=3LwP6guVX&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRlNLt/btsHbATLTpf/9rZ6KI6IOu2Fgx7QuZuYWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRlNLt%2FbtsHbATLTpf%2F9rZ6KI6IOu2Fgx7QuZuYWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1067&quot; height=&quot;267&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&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;이 경우에도 마이크로미터를 통한 추상화를 통하여 여러 다른 형식의 포맷을 표준화하였다.&amp;nbsp;&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;671&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9KG7X/btsHcR1gKht/uekfE4Eioazz0TDzhBXqEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9KG7X/btsHcR1gKht/uekfE4Eioazz0TDzhBXqEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9KG7X/btsHcR1gKht/uekfE4Eioazz0TDzhBXqEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9KG7X%2FbtsHcR1gKht%2FuekfE4Eioazz0TDzhBXqEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;605&quot; data-origin-width=&quot;671&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로미터와 액츄에이터가 제공하는 메트릭에는 매우 다양한 정보들이 있다. 전체적인 애플리케이션의 health 체크 및 시스템에 대한 메트릭은 기본적으로 제공해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 애플리케이션의 비즈니스 로직에서의 의미 있는 지표를 뽑아내고 싶다면 어떻게 해야 할까??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-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;카운트 예제 1)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- registry를 의존하여 로직 안에서 직접 생성&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
public class OrderServiceV1 implements OrderService {
    private final MeterRegistry registry;
    private AtomicInteger stock = new AtomicInteger(100);

    @Override
    public void order() {
        log.info(&quot;주문&quot;);
        stock.decrementAndGet();
        Counter.builder(&quot;my.order&quot;)
                .tag(&quot;class&quot;, this.getClass().getName())
                .tag(&quot;method&quot;, &quot;order&quot;)
                .description(&quot;order&quot;)
                .register(registry).increment();
    }

    @Override
    public void cancel() {
        log.info(&quot;취소&quot;);
        stock.incrementAndGet();
        Counter.builder(&quot;my.order&quot;)
                .tag(&quot;class&quot;, this.getClass().getName())
                .tag(&quot;method&quot;, &quot;cancel&quot;)
                .description(&quot;order&quot;)
                .register(registry).increment();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;카운터 예제 2)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위 방식은 서비스 로직과 다른 기능을 하는(모니터 로그를 수집하는) 기능이 로직안에 포함되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 마이크로미터의 AOP 방식으로 관점 분리할 수 있으며 Bean을 생성해주어야 한다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class OrderConfigV2 {
    @Bean
    public CountedAspect countedAspect(MeterRegistry registry) {
        return new CountedAspect(registry);
    }
    @Bean
    OrderService orderService() {
        return new OrderServiceV2();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
public class OrderServiceV2 implements OrderService {
    private AtomicInteger stock = new AtomicInteger(100);

    @Counted(&quot;my.order&quot;)
    @Override
    public void order() {
        log.info(&quot;주문&quot;);
        stock.decrementAndGet();
    }

    @Counted(&quot;my.order&quot;)
    @Override
    public void cancel() {
        log.info(&quot;취소&quot;);
        stock.incrementAndGet();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;타이머 예제 1)&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- registry를 의존하여 로직 안에서 직접 생성&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
public class OrderServiceV3 implements OrderService {
    private final MeterRegistry registry;
    private AtomicInteger stock = new AtomicInteger(100);

    @Override
    public void order() {
        Timer timer = Timer.builder(&quot;my.order&quot;)
                .tag(&quot;class&quot;, this.getClass().getName())
                .tag(&quot;method&quot;, &quot;order&quot;)
                .description(&quot;order&quot;)
                .register(registry);
        timer.record(() -&amp;gt; {
            log.info(&quot;주문&quot;);
            stock.decrementAndGet();
            sleep(500);
        });
    }


    @Override
    public void cancel() {
        Timer timer = Timer.builder(&quot;my.order&quot;)
                .tag(&quot;class&quot;, this.getClass().getName())
                .tag(&quot;method&quot;, &quot;cancel&quot;)
                .description(&quot;order&quot;)
                .register(registry);
        timer.record(() -&amp;gt; {
            log.info(&quot;취소&quot;);
            stock.incrementAndGet();
            sleep(200);
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;타이머 예제 2)&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 카운터 예제와 동일&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class OrderConfigV4 {
    @Bean
    public TimedAspect countedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }
    @Bean
    OrderService orderService() {
        return new OrderServiceV4();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Timed(value = &quot;my.order&quot;)
@RequiredArgsConstructor
public class OrderServiceV4 implements OrderService {
    private AtomicInteger stock = new AtomicInteger(100);

    @Override
    public void order() {
            log.info(&quot;주문&quot;);
            stock.decrementAndGet();
            sleep(500);
    }


    @Override
    public void cancel() {
            log.info(&quot;취소&quot;);
            stock.incrementAndGet();
            sleep(200);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 진행 중인 프로젝트에 적용하였을 때 정의한 이름의 메트릭이 생성되지 않아 진짜 몇 시간 동안 삽질을 했는데 혹시나 해서 api를 하나 찍어보니 메트릭이 생성되었다. config와 어노테이션으로 정의만 하는 것이 아니라 실제 호출 되었을 때 DB에 정보가 쌓여야 메트릭 정보도 확인이 가능하였다.... &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;&lt;b&gt;마지막으로...&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;김영한 강사님의 로드맵을 끝 마치며 정말 항상 감사하고 마지막 말씀이 너무 감동이라 (&lt;s&gt;T인데도 불구하고&lt;/s&gt;) 모든 로드맵 강의에 별점을 5점 박을수 밖에 없었다... 더욱 정진하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고자료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;김영한&lt;/b&gt; : 스프링&amp;nbsp;부트&amp;nbsp;-&amp;nbsp;핵심&amp;nbsp;원리와&amp;nbsp;활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1714921097166&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;김영한 | 실무에 필요한 스프링 부트는 이 강의 하나로 모두 정리해드립니다., 백엔드 개발자를 위한 스프링 부트 끝판왕!&amp;nbsp;실무에 필요한 내용을 모두 담았습니다.&amp;nbsp; [임베딩 영상] 김영한의 스&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%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cVScsQ/hyVZn6a1hM/DGGVwIKajkQGQ4YUJlEhFk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/bWSg66/hyVZssSepo/aob28DbHKPMfIbgK8ukmBk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/wWZVM/hyVZsl7fu6/Ib9Xxo6mHlNxaKUyTqR8Wk/img.png?width=1200&amp;amp;height=740&amp;amp;face=984_526_1051_599&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cVScsQ/hyVZn6a1hM/DGGVwIKajkQGQ4YUJlEhFk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/bWSg66/hyVZssSepo/aob28DbHKPMfIbgK8ukmBk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/wWZVM/hyVZsl7fu6/Ib9Xxo6mHlNxaKUyTqR8Wk/img.png?width=1200&amp;amp;height=740&amp;amp;face=984_526_1051_599');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 부트 - 핵심 원리와 활용 | 김영한 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;김영한 | 실무에 필요한 스프링 부트는 이 강의 하나로 모두 정리해드립니다., 백엔드 개발자를 위한 스프링 부트 끝판왕!&amp;nbsp;실무에 필요한 내용을 모두 담았습니다.&amp;nbsp; [임베딩 영상] 김영한의 스&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;267&quot;&gt;&lt;a href=&quot;https://elice.training/track/cloud?utm_source=cloud1&amp;amp;utm_medium=blog&amp;amp;utm_campaign=challenge&amp;amp;utm_content=3LwP6guVX&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bawCg2/btsHccEQMHO/BXbwm6ua8uAoQHwI43rHgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbawCg2%2FbtsHccEQMHO%2FBXbwm6ua8uAoQHwI43rHgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1067&quot; height=&quot;267&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring</category>
      <category>개발자국비지원</category>
      <category>개발자부트캠프</category>
      <category>국비지원부트캠프</category>
      <category>백엔드</category>
      <category>백엔드학원</category>
      <category>엘리스트랙</category>
      <category>엘리스트랙후기</category>
      <category>온라인코딩부트캠프</category>
      <category>온라인코딩학원</category>
      <category>자바스프링</category>
      <author>쭈녁</author>
      <guid isPermaLink="true">https://programmingjun.tistory.com/93</guid>
      <comments>https://programmingjun.tistory.com/93#entry93comment</comments>
      <pubDate>Mon, 6 May 2024 00:00:50 +0900</pubDate>
    </item>
  </channel>
</rss>