<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>웹프로그램 이야기</title>
    <link>https://jydlove.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 11:48:07 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>devguru</managingEditor>
    <image>
      <title>웹프로그램 이야기</title>
      <url>https://tistory1.daumcdn.net/tistory/2241263/attach/a97d2d514d9a4f0e8bd30dd77143709b</url>
      <link>https://jydlove.tistory.com</link>
    </image>
    <item>
      <title>속초에서 먹고 걷고 쉬다</title>
      <link>https://jydlove.tistory.com/103</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;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;속초 여행 가이드&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  속초 가성비 &amp;amp; 럭셔리 숙소 TOP 5&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.  &amp;zwj;♀️ 체스터톤스 속초 호텔 &amp;ndash; 가성비 끝판왕 신축 호텔&lt;/h3&gt;
&lt;p class=&quot;hotel-location&quot; data-ke-size=&quot;size16&quot;&gt;  위치: 속초 청초호 인근, 엑스포로 109&lt;/p&gt;
&lt;p class=&quot;feature&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt; 신축 호텔다운 깔끔함, 사계절 온천수 수영장과 천연 온천 사우나까지! 키즈 테마 객실은 어린 자녀가 있는 가족 여행자에게 인기 만점.&lt;/p&gt;
&lt;p class=&quot;pros&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt; 다양한 객실 타입 &amp;amp; 키즈 어메니티, 천연 온천 사우나 + 온천수 수영장&lt;/p&gt;
&lt;p class=&quot;cons&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt; 조식이 다소 부실하다는 후기, 객실 내 취사 불가&lt;/p&gt;
&lt;p class=&quot;recommendation&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 대상:&lt;/b&gt; 온천 즐기며 알뜰하게 속초 여행하고 싶은 가족 여행객&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.   더 블루테라 호텔 &amp;ndash; 29층 인피니티 풀에서 바다를!&lt;/h3&gt;
&lt;p class=&quot;hotel-location&quot; data-ke-size=&quot;size16&quot;&gt;  위치: 속초 바다 뷰 프리미엄 호텔&lt;/p&gt;
&lt;p class=&quot;feature&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt; 전 객실 테라스 + 오션뷰, 29층 인피니티 풀, 신축급 최신 시설. 피트니스, 드럼 세탁기, 살균 비대까지 꼼꼼하게 갖춰져 있어요.&lt;/p&gt;
&lt;p class=&quot;pros&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt; 29층에서 즐기는 인피니티 풀, 전 객실 오션뷰 테라스, 다양한 패밀리 객실, 최신 편의시설&lt;/p&gt;
&lt;p class=&quot;cons&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt; 가격대가 다소 높을 수 있음&lt;/p&gt;
&lt;p class=&quot;recommendation&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 대상:&lt;/b&gt; 뷰와 시설 모두 포기 못하는 프리미엄 여행자&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.  ️ 마리나 호텔 &amp;ndash; 도심 속 루프탑 온수풀 힐링 스팟&lt;/h3&gt;
&lt;p class=&quot;hotel-location&quot; data-ke-size=&quot;size16&quot;&gt;  위치: 속초 도심 &amp;amp; 바다를 동시에&lt;/p&gt;
&lt;p class=&quot;feature&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt; 모던한 인테리어, 통창 뷰, 시몬스 프리미엄 침대까지! 도심에서 가까운 루프탑 온수풀도 인기 포인트입니다.&lt;/p&gt;
&lt;p class=&quot;pros&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt; 유리면 루프탑 온수풀, 통창으로 보는 바다 뷰, 뷔페식 조식 &amp;amp; 프리미엄 침대&lt;/p&gt;
&lt;p class=&quot;cons&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt; 중상급 가격대&lt;/p&gt;
&lt;p class=&quot;recommendation&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 대상:&lt;/b&gt; 안락한 휴식과 감성 뷰를 중시하는 커플/중상급 여행자&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.   롯데 리조트 속초 &amp;ndash; 워터파크+오션뷰 풀까지 한 방에!&lt;/h3&gt;
&lt;p class=&quot;hotel-location&quot; data-ke-size=&quot;size16&quot;&gt;  위치: 강원도 속초, 바다 바로 앞&lt;/p&gt;
&lt;p class=&quot;feature&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt; 호텔 &amp;amp; 콘도 선택 가능! 워터파크, 인피니티 풀, 루프탑 바, 키즈 카페까지! 아이들과 함께라면 이만한 곳 없습니다.&lt;/p&gt;
&lt;p class=&quot;pros&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt; 파노라마 오션뷰 인피니티 풀, 콘도 타입 숙소는 취사 가능, 다양한 부대시설 (루프탑 바, 키즈 카페 등)&lt;/p&gt;
&lt;p class=&quot;cons&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt; 가격대 다소 높음&lt;/p&gt;
&lt;p class=&quot;recommendation&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 대상:&lt;/b&gt; 가족 단위, 편의시설 많은 고급 숙소 원하는 여행객&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. ✨ 카시아 속초 &amp;ndash; 2023년 신상 프리미어 리조트&lt;/h3&gt;
&lt;p class=&quot;hotel-location&quot; data-ke-size=&quot;size16&quot;&gt;  위치: 오션 프런트, 2023년 오픈&lt;/p&gt;
&lt;p class=&quot;feature&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt; 야외 수영장, 노천탕, 사우나, 키즈 카페까지 풀옵션! 모든 객실이 오션뷰 + 주방, 욕조까지 있어 가족 여행에 최적화.&lt;/p&gt;
&lt;p class=&quot;pros&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt; 신상 호텔! 최신 인테리어, 탁 트인 바다 전망 + 노천탕, 전 객실 주방 &amp;amp; 욕조 구비, 3~6인 패밀리 스위트룸&lt;/p&gt;
&lt;p class=&quot;cons&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt; 가격대 높음 (슈페리어룸 기준)&lt;/p&gt;
&lt;p class=&quot;recommendation&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 대상:&lt;/b&gt; 신축 숙소 &amp;amp; 프리미엄 가족 여행 선호자&lt;/p&gt;
&lt;div class=&quot;section-separator&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p class=&quot;intro-text&quot; data-ke-size=&quot;size16&quot;&gt;숙소에서 편안하게 몸을 녹이고 난 후, 속초 여행의 또 다른 즐거움은 바로 다양한 현지 맛집 탐방입니다. 바다와 자연이 선사하는 신선한 재료를 맛볼 준비 되셨나요?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  속초 맛집 탐방 &amp;ndash; 숙소 근처 현지인 추천 맛집 총정리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  해산물 요리를 원한다면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이모의 식당 (영랑동)&lt;/b&gt;&lt;br /&gt;가오리찜, 생선 모듬, 대구머리찜 등 다양한 해산물 요리를 즐길 수 있는 현지인 추천 맛집.&lt;br /&gt;&lt;i&gt;  해산물 마니아, 가격 부담 없는 여행자에게 추천&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모녀 가리비 (대포동)&lt;/b&gt;&lt;br /&gt;신선한 가리비 구이와 특이한 누룽지 오징어 순대 메뉴가 인기.&lt;br /&gt;&lt;i&gt;  신선하고 이색적인 해산물을 즐기고 싶은 미식가에게 추천&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://naver.me/GtURcBxo&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;북한 횟집 (대포동)&lt;/b&gt;&lt;br /&gt;대게 코스 요리와 홍게 세트 등 게 요리에 특화된 전문 횟집.&lt;br /&gt;&lt;i&gt;  게 요리를 선호하는 방문객에게 이상적&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;청초수 물회 속초 본점 (조양동)&lt;/b&gt;&lt;br /&gt;청초수, 해전, 오징어 물회 등 다양한 물회 메뉴가 인기인 물회 전문점.&lt;br /&gt;&lt;i&gt;  신선한 물회를 맛보고 싶은 사람에게 추천&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;속초 항아리 물회 (조양동)&lt;/b&gt;&lt;br /&gt;항아리 물회, 해삼&amp;middot;전복 물회 등 고급 해산물 요리가 강점.&lt;br /&gt;&lt;i&gt;  고급 해산물 물회를 즐기고 싶은 분께 적합&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;봉포머구리집 &lt;/b&gt;&lt;b&gt;(영랑동)&lt;/b&gt;&lt;br /&gt;전복 해산물 물회 전문점으로, 메뉴가 다양하고 신선함이 강점.&lt;br /&gt;&lt;i&gt;  해산물 물회를 좋아하는 분에게 딱&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  합리적인 가격의 식사를 원한다면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;88 생선구이 (중앙동)&lt;/b&gt;&lt;br /&gt;모듬 생선구이 전문, 합리적인 가격과 쉬지 않는 영업으로 인기.&lt;br /&gt;&lt;i&gt;  가성비 좋은 생선구이 맛집을 찾는 방문객에게 추천&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;김영애할머니순두부 본점 (노학동)&lt;/b&gt;&lt;br /&gt;국산콩을 사용한 전통 순두부 전문점, 매일 영업.&lt;br /&gt;&lt;i&gt;  전통 순두부를 즐기고 싶은 예산-conscious 여행객에게 추천&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  닭 요리, 닭강정이 당긴다면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;만석 닭강정 본점 (조양동)&lt;/b&gt;&lt;br /&gt;보통맛, 매운맛, 후라이드 등 다양한 닭강정 메뉴로 포장용 간식에 제격.&lt;br /&gt;&lt;i&gt;  간편한 간식 또는 포장용 닭강정을 찾는 분에게 적합&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가보오토종닭 (노학동)&lt;/b&gt;&lt;br /&gt;토종닭 요리와 함께 해물 돌자장, 짬뽕, 탕수육 등 중식 메뉴도 함께 제공하는 특이한 조합.&lt;br /&gt;&lt;i&gt;  닭 요리와 중국 요리를 한자리에서 즐기고 싶은 분에게 추천&lt;/i&gt;&lt;/p&gt;</description>
      <category>국내여행/강원도</category>
      <category>속초여행 #속초맛집 #속초해산물맛집 #속초물회 #속초횟집 #속초로컬맛집 #속초닭강정 #속초카페 #속초디저트 #속초여행코스 #속초가볼만한곳 #강원도여행 #강원도맛집 #국내여행지추천 #여행블로그 #맛집블로그 #현지인맛집추천 #속초1박2일 #감성여행 #여행기록</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/103</guid>
      <comments>https://jydlove.tistory.com/103#entry103comment</comments>
      <pubDate>Mon, 19 May 2025 01:01:53 +0900</pubDate>
    </item>
    <item>
      <title>&amp;quot;삼척 숙소 추천｜뷰, 위치, 가격 삼박자 갖춘 '용화호텔' 후기 대공개&amp;quot;</title>
      <link>https://jydlove.tistory.com/102</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;삼척 용화호텔 솔직후기｜오션뷰 객실부터 관광지까지 완벽정리  &lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lIWAE/btsN18ba6ne/ACpIUlvgZhyO8vt318wn7K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lIWAE/btsN18ba6ne/ACpIUlvgZhyO8vt318wn7K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lIWAE/btsN18ba6ne/ACpIUlvgZhyO8vt318wn7K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlIWAE%2FbtsN18ba6ne%2FACpIUlvgZhyO8vt318wn7K%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;1920&quot; height=&quot;740&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분, 여행 어디로 떠날지 고민 중이신가요?&lt;br /&gt;이번 여름 혹은 봄캉스, 가을 바다 여행지로 강력 추천드리는 곳!&lt;br /&gt;바로 강원도 삼척의 숨은 보석, &lt;b&gt;&amp;lsquo;용화호텔&amp;rsquo;&lt;/b&gt;입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;✔️ 삼척 오션뷰 숙소 찾는다면?&lt;br /&gt;✔️ 가족 여행, 커플 여행, 단체 MT 고민이라면?&lt;br /&gt;  이 글 끝까지 읽어보세요!&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  삼척 용화호텔 위치 &amp;amp; 기본 정보&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;주소:&lt;/b&gt; 강원도 삼척시 근덕면 용화해변길 170&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주차:&lt;/b&gt; 무료 주차 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;입실/퇴실:&lt;/b&gt; 15:00 / 11:00&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주변:&lt;/b&gt; 용화해변 도보 1분, 장호항 7분 거리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예약:&lt;/b&gt; 여기어때 / 야놀자 / 전화 예약&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✔️ 무엇보다 최고의 장점? 단연 가격 대비 &amp;lsquo;뷰&amp;rsquo;와 &amp;lsquo;숙소 컨디션&amp;rsquo;!&lt;/b&gt;&lt;br /&gt;근처 고가 호텔 대비 절반 가격에 &lt;span style=&quot;color: blue;&quot;&gt;오션뷰 + 넓은 객실 + 청결함&lt;/span&gt;을 모두 갖췄습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 객실 정보 &amp;ndash; 타입별 상세 안내&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  스탠다드룸 (2인)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더블 또는 트윈 침대&lt;/li&gt;
&lt;li&gt;에어컨, TV, 냉장고, 샤워실&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오션뷰 업그레이드 가능&lt;/b&gt; (소액 추가)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;i&gt;가성비 최고의 객실로, 커플이나 혼자 여행 오는 분들께 인기!&lt;/i&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  오션뷰룸 (커플 &amp;amp; 감성여행)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;동해 일출 직관 가능한 탁 트인 통유리창&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;모든 인테리어가 깔끔하고 심플&lt;/li&gt;
&lt;li&gt;사진 맛집 룸으로 SNS 업로드하기 딱!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; &amp;zwj; &amp;zwj;  패밀리룸 (3~4인 가족)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;온돌 + 침대 복합 구조&lt;/li&gt;
&lt;li&gt;아이들도 뛰어놀 수 있는 넓은 실내&lt;/li&gt;
&lt;li&gt;수건, 어메니티, 생수 등 기본 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  단체룸 (10인 이상)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;넓은 온돌룸 + 다수의 화장실&lt;/li&gt;
&lt;li&gt;취사 가능 + 바비큐장 연계&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단체 MT / 워크숍에 최적&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  공통 편의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전 객실 와이파이 무료&lt;/li&gt;
&lt;li&gt;냉장고, 생수, 수건 넉넉히 구비&lt;/li&gt;
&lt;li&gt;금연 객실&lt;/li&gt;
&lt;li&gt;도보 3분 거리 편의점, 식당 밀집&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  관리가 잘 되어 타 호텔 대비 청결도에서 높은 점수!&lt;/b&gt;&lt;br /&gt;비슷한 가격대 숙소보다 훨씬 &lt;span style=&quot;color: green;&quot;&gt;넓고 깔끔한 컨디션&lt;/span&gt;이 장점입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  주변 관광지 추천&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ 장호항 &amp;amp; 장호해변 (도보 7분)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국의 나폴리라 불리는 곳!&lt;br /&gt;스노클링과 투명카약 등 해양 체험도 가능&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ 삼척 해양레일바이크 (차로 10분)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동해 바다를 따라 달리는 철길 자전거 &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ 죽서루 (차로 20분)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조선시대 누각과 멋진 풍경이 어우러진 감성 여행지&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  실제 이용 후기&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&amp;ldquo;뷰 최고! 바다 바로 앞이라 아침부터 힐링 제대로 했어요&amp;rdquo;&lt;br /&gt;&amp;ldquo;단체로 다녀왔는데 깨끗하고 넓어서 모두 만족했어요&amp;rdquo;&lt;br /&gt;&amp;ldquo;삼척에서 이 가격에 오션뷰면 가성비 끝판왕입니다&amp;rdquo;&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;성수기 전 미리 예약 시 최대 30% 할인&lt;/li&gt;
&lt;li&gt;전화 예약 시 뷰 좋은 방 요청 가능&lt;/li&gt;
&lt;li&gt;단체 예약은 별도 문의 시 혜택 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;비슷한 위치의 프랜차이즈 호텔 대비 객실 넓이, 뷰, 가격, 모두 우위!&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  카카오맵 위치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://map.kakao.com/?urlX=1011776&amp;amp;urlY=1058566&amp;amp;urlLevel=4&amp;amp;map_type=TYPE_MAP&amp;amp;map_hybrid=false&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;img style=&quot;border: 1px solid #ccc;&quot; src=&quot;https://map2.daum.net/map/mapservice?FORMAT=PNG&amp;amp;SCALE=5&amp;amp;MX=1011776&amp;amp;MY=1058566&amp;amp;S=0&amp;amp;IW=504&amp;amp;IH=310&amp;amp;LANG=0&amp;amp;COORDSTM=WCONGNAMUL&amp;amp;logo=kakao_logo&quot; width=&quot;504&quot; height=&quot;310&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;hide&quot; style=&quot;overflow: hidden; padding: 7px 11px; border: 1px solid #dfdfdf; border-color: rgba(0,0,0,.1); border-radius: 0 0 2px 2px; background-color: #f9f9f9; width: 482px;&quot;&gt;&lt;b&gt;&lt;img src=&quot;//t1.daumcdn.net/localimg/localimages/07/2018/pc/common/logo_kakaomap.png&quot; alt=&quot;카카오맵&quot; width=&quot;72&quot; height=&quot;16&quot; /&gt;&lt;/b&gt;
&lt;div style=&quot;float: right; position: relative;&quot;&gt;&lt;a style=&quot;font-size: 12px; text-decoration: none; float: left; height: 15px; padding-top: 1px; line-height: 15px; color: #000;&quot; href=&quot;https://map.kakao.com/?urlX=1011776&amp;amp;urlY=1058566&amp;amp;urlLevel=4&amp;amp;map_type=TYPE_MAP&amp;amp;map_hybrid=false&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;지도 크게 보기&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 관련 키워드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼척 용화호텔 후기, 삼척 숙소 추천, 삼척 오션뷰 호텔,&lt;br /&gt;용화해변 호텔, 장호항 근처 숙소, 삼척 가족여행 숙소,&lt;br /&gt;삼척 단체 숙소, 강원도 바다 숙소 추천&lt;/p&gt;</description>
      <category>국내여행/강원도</category>
      <category>강원도숙소추천</category>
      <category>동해여행</category>
      <category>삼척가볼만한곳</category>
      <category>삼척바다</category>
      <category>삼척숙소</category>
      <category>삼척여행</category>
      <category>삼척오션뷰</category>
      <category>삼척호텔</category>
      <category>용화호텔</category>
      <category>장호항숙소</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/102</guid>
      <comments>https://jydlove.tistory.com/102#entry102comment</comments>
      <pubDate>Sun, 18 May 2025 16:13:25 +0900</pubDate>
    </item>
    <item>
      <title>AI가 더 똑똑해지는 비결? Model Context Protocol(MCP) 완전 정복 가이드</title>
      <link>https://jydlove.tistory.com/101</link>
      <description>&lt;!-- 제목 --&gt;
&lt;h1&gt;  초보자를 위한 Model Context Protocol (MCP) 입문 가이드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;AI가 더 똑똑해지는 기술, 지금 이해해보세요&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;✅ AI에 관심 있는 분이라면 꼭 알아야 할 MCP! 개인화된 챗봇, 똑똑한 검색, 고도화된 상담까지 이끄는 핵심 기술입니다.&lt;/blockquote&gt;
&lt;!-- 광고 삽입 위치 (상단) --&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;!-- 티스토리 AdSense 코드 삽입 가능 --&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Model Context Protocol이란?&lt;/li&gt;
&lt;li&gt;왜 MCP가 중요할까?&lt;/li&gt;
&lt;li&gt;MCP의 작동 원리&lt;/li&gt;
&lt;li&gt;실생활 적용 예시&lt;/li&gt;
&lt;li&gt;앞으로의 전망&lt;/li&gt;
&lt;li&gt;마무리&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. Model Context Protocol이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Model Context Protocol (MCP)&lt;/b&gt;는 AI가 과거 대화 내용이나 사용자 정보를 이해하고 현재 질문에 더 똑똑하게 응답할 수 있도록 해주는 맥락 관리 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;쉽게 말해, &amp;ldquo;AI가 나를 기억하는 것처럼 반응하는 기술&amp;rdquo;이에요.&lt;/i&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2. 왜 MCP가 중요할까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;  &lt;b&gt;대화가 길어질수록 AI는 맥락을 잃어요.&lt;/b&gt; MCP는 필요한 정보만 요약해 AI에게 전달해줍니다.&lt;/li&gt;
&lt;li&gt; &amp;zwj;  &lt;b&gt;사용자 맞춤형 경험이 가능&lt;/b&gt; &amp;ndash; 개인화된 대답이 가능합니다.&lt;/li&gt;
&lt;li&gt;  &lt;b&gt;멀티모달 정보 이해&lt;/b&gt; &amp;ndash; 이미지, PDF 등과 함께 작동합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;  실제로 많은 기업들이 MCP를 활용해 AI 상담, 고객 서비스의 품질을 높이고 있습니다.&lt;/blockquote&gt;
&lt;!-- 광고 삽입 위치 (본문 중간) --&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚙️ 3. MCP는 어떻게 작동할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 아래와 같은 구조로 작동합니다:&lt;/p&gt;
&lt;table border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구성요소&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;system&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AI에게 어떤 역할을 맡길지 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;이전 대화나 사용자 정보 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;context&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;현재 상황의 배경 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;query&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자의 질문&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;response&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AI의 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;system&quot;: &quot;너는 친절한 여행 가이드야.&quot;,
  &quot;memory&quot;: {
    &quot;지난 여행지&quot;: &quot;오사카&quot;,
    &quot;선호&quot;: &quot;음식 중심 여행&quot;
  },
  &quot;context&quot;: &quot;봄 시즌 추천 여행지 소개 중&quot;,
  &quot;query&quot;: &quot;벚꽃 볼 수 있는 여행지는?&quot;,
  &quot;response&quot;: &quot;후쿠오카의 모모치 해변 벚꽃이 아주 유명해요!&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. 어디에 쓰이나요?&lt;/h2&gt;
&lt;table border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;분야&lt;/th&gt;
&lt;th&gt;적용 사례&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;고객센터&lt;/td&gt;
&lt;td&gt;고객 이력 기반 자동 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;헬스케어&lt;/td&gt;
&lt;td&gt;환자 기록 기반 상담&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;교육 튜터&lt;/td&gt;
&lt;td&gt;학생 수준에 맞춘 피드백&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문서 요약&lt;/td&gt;
&lt;td&gt;특정 맥락에 맞는 정보만 요약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비즈니스 분석&lt;/td&gt;
&lt;td&gt;회의 기록 요약 및 액션 제안&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. MCP, 앞으로 얼마나 중요해질까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✨ 최신 AI 모델(GPT-4, Claude 등)도 MCP와 유사한 구조를 탑재&lt;/li&gt;
&lt;li&gt; ️ LangChain, OpenAI Function, RAG 등과 유사한 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;  앞으로 대부분의 AI 앱은 MCP 기반 구조를 기본 탑재할 가능성이 높습니다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  6. 마무리: 지금부터 관심 가져야 할 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 &amp;ldquo;AI가 진짜로 인간처럼 이해하고 반응하는 방식&amp;rdquo;을 바꾸는 핵심 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에 관심이 있다면 지금 MCP를 이해하는 것만으로도 다양한 AI 서비스를 만들거나 활용할 때 훨씬 유리합니다!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;  초보자도 MCP를 쉽게 적용할 수 있는 오픈소스나 툴이 계속 나오고 있어요.   &lt;b&gt;곧 MCP 예제 포스트도 업로드 예정!&lt;/b&gt;&lt;/blockquote&gt;
&lt;!-- 광고 삽입 위치 (마무리 하단 광고) --&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  도움이 되셨다면?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;  댓글로 질문 남겨주세요!&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/MCP</category>
      <category>Ai</category>
      <category>GPT</category>
      <category>LangChain</category>
      <category>mcp</category>
      <category>model context protocol</category>
      <category>rag</category>
      <category>대화형 AI</category>
      <category>인공지능</category>
      <category>챗GPT</category>
      <category>프롬프트 엔지니어링</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/101</guid>
      <comments>https://jydlove.tistory.com/101#entry101comment</comments>
      <pubDate>Thu, 24 Apr 2025 23:05:43 +0900</pubDate>
    </item>
    <item>
      <title>윈도우 React https 적용하기</title>
      <link>https://jydlove.tistory.com/100</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우에서는 package.json 파일에서 scripts 부분의 start 부분을 아래와 같이 변경 해주어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;scripts&quot;: { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;start&quot;:&amp;nbsp;&quot;set&amp;nbsp;HTTPS=true&amp;amp;&amp;amp;set&amp;nbsp;SSL_CRT_FILE=cert.pem&amp;amp;&amp;amp;set&amp;nbsp;SSL_KEY_FILE=key.pem&amp;amp;&amp;amp;react-scripts&amp;nbsp;start&quot;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;build&quot;:&amp;nbsp;&quot;react-scripts&amp;nbsp;build&quot;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;test&quot;:&amp;nbsp;&quot;react-scripts&amp;nbsp;test&quot;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;eject&quot;: &quot;react-scripts eject&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;},&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React</category>
      <category>htps</category>
      <category>REACT</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/100</guid>
      <comments>https://jydlove.tistory.com/100#entry100comment</comments>
      <pubDate>Tue, 16 May 2023 00:15:12 +0900</pubDate>
    </item>
    <item>
      <title>주문 서비스 예제</title>
      <link>https://jydlove.tistory.com/99</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 사용자가 주문정보를 전송하고 상품의 재고를 조회 하여서 재고가 부족하거나 재고 조회 서비스 호출 시 오류가 발생하면 주문취소 처리를 하고 재고가 있고 결제 처리 후 배송요청 오류가 발생하면 결제취소, 주문취소를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 설명한 Saga 패턴의 핵심요소인 이전 로컬 트랜잭션에 의해 변경된 내용을 실행 취소하는&amp;nbsp;일련의 보상&amp;nbsp;트랜잭션을 실행한다. 라는 내용을 기억 할 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주문서비스 시퀀스 다이어그램&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;주문생성.jpg&quot; data-origin-width=&quot;2642&quot; data-origin-height=&quot;1756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9HvHl/btr0kmDnkuE/4udhU2eBo5h16Rts8YME0k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9HvHl/btr0kmDnkuE/4udhU2eBo5h16Rts8YME0k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9HvHl/btr0kmDnkuE/4udhU2eBo5h16Rts8YME0k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9HvHl%2Fbtr0kmDnkuE%2F4udhU2eBo5h16Rts8YME0k%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;2642&quot; height=&quot;1756&quot; data-filename=&quot;주문생성.jpg&quot; data-origin-width=&quot;2642&quot; data-origin-height=&quot;1756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주문서비스 Orchestration&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Order_SAGA_Orchestration.PNG&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;415&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dN3q9z/btr0kmXEGN5/fn31KwoHX9GnzOXAKCvNEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dN3q9z/btr0kmXEGN5/fn31KwoHX9GnzOXAKCvNEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dN3q9z/btr0kmXEGN5/fn31KwoHX9GnzOXAKCvNEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdN3q9z%2Fbtr0kmXEGN5%2Ffn31KwoHX9GnzOXAKCvNEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1156&quot; height=&quot;415&quot; data-filename=&quot;Order_SAGA_Orchestration.PNG&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;415&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주문서비스 Orchestration Command and Event Flow&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Order_saga_orchestration_command_and_event_flow.PNG&quot; data-origin-width=&quot;1259&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0ukeU/btr0ojMxWpX/N8HHIfq9pVTPnxWnpCZYH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0ukeU/btr0ojMxWpX/N8HHIfq9pVTPnxWnpCZYH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0ukeU/btr0ojMxWpX/N8HHIfq9pVTPnxWnpCZYH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0ukeU%2Fbtr0ojMxWpX%2FN8HHIfq9pVTPnxWnpCZYH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1259&quot; height=&quot;604&quot; data-filename=&quot;Order_saga_orchestration_command_and_event_flow.PNG&quot; data-origin-width=&quot;1259&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로젝트설정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Axon Server 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.axoniq.io/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.axoniq.io/download&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1677066276723&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Download - AxonIQ Developer Portal - AxonIQ&quot; data-og-description=&quot;The&amp;nbsp;AxonIQ Initializr&amp;nbsp;is a web application that creates an Axon project structure for you. It doesn&amp;rsquo;t generate any application code, but it will give you a solid project structure, offering either a Maven or a Gradle build specification to suit your ne&quot; data-og-host=&quot;developer.axoniq.io&quot; data-og-source-url=&quot;https://developer.axoniq.io/download&quot; data-og-url=&quot;https://developer.axoniq.io/download&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.axoniq.io/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.axoniq.io/download&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Download - AxonIQ Developer Portal - AxonIQ&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The&amp;nbsp;AxonIQ Initializr&amp;nbsp;is a web application that creates an Axon project structure for you. It doesn&amp;rsquo;t generate any application code, but it will give you a solid project structure, offering either a Maven or a Gradle build specification to suit your ne&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.axoniq.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Spring Boot 프로젝트 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://start.spring.io/&quot;&gt;https://start.spring.io/&lt;/a&gt; 사이트에서 order-service 프로젝트를 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;프로젝트생성_1.PNG&quot; data-origin-width=&quot;1471&quot; data-origin-height=&quot;685&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XbAQM/btr0oOyMpGM/LdHVXseG6tNN6zEKs39wy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XbAQM/btr0oOyMpGM/LdHVXseG6tNN6zEKs39wy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XbAQM/btr0oOyMpGM/LdHVXseG6tNN6zEKs39wy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXbAQM%2Fbtr0oOyMpGM%2FLdHVXseG6tNN6zEKs39wy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1471&quot; height=&quot;685&quot; data-filename=&quot;프로젝트생성_1.PNG&quot; data-origin-width=&quot;1471&quot; data-origin-height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 프로젝트도 위와 같이 동일하게 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;common-service의 경우 공통으로 사용하는 command, events만 관리하므로 Dependencies는 Lombok만 추가 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 프로젝트 설정 후 IDE화면은 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;intellij_프로젝트구조.PNG&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cS4pFY/btr0o7dP9b6/BjfdkkYWw6sC9vrAg53IfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cS4pFY/btr0o7dP9b6/BjfdkkYWw6sC9vrAg53IfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cS4pFY/btr0o7dP9b6/BjfdkkYWw6sC9vrAg53IfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcS4pFY%2Fbtr0o7dP9b6%2FBjfdkkYWw6sC9vrAg53IfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;278&quot; height=&quot;228&quot; data-filename=&quot;intellij_프로젝트구조.PNG&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;OrderProcessingSaga.java&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 가장 핵심적인 소스입니다. 주문서비스 전체의 command and flow를 관리하는 소스 입니다. 소스를 구현하다 보면 workflow의 느낌이 많이 들 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1677066409646&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.roopy.orderservice.api.saga;

import com.roopy.commonservice.api.command.*;
import com.roopy.commonservice.api.events.*;
import com.roopy.orderservice.api.events.OrderCreatedEvent;
import com.roopy.orderservice.api.service.InventoryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.axonframework.modelling.saga.EndSaga;
import org.axonframework.modelling.saga.SagaEventHandler;
import org.axonframework.modelling.saga.StartSaga;
import org.axonframework.spring.stereotype.Saga;
import org.springframework.beans.factory.annotation.Autowired;

@Saga
@Slf4j
public class OrderProcessingSaga {

    @Autowired
    private transient CommandGateway commandGateway;

    @Autowired
    private InventoryService inventoryService;

    @StartSaga
    @SagaEventHandler(associationProperty = &quot;orderId&quot;)
    private void handle(OrderCreatedEvent event) {
        log.info(&quot;[Saga] OrderCreatedEvent in Saga for Order Id : {}&quot;, event.getOrderId());
        boolean isValidInventory;
        try {
            isValidInventory = inventoryService.isValidInventory(event);
            
            if (isValidInventory) {
                // 결재처리
                CreatePaymentCommand createPaymentCommand = CreatePaymentCommand.builder()
                        .paymentId(event.getPaymentId())
                        .orderId(event.getOrderId())
                        .totalPaymentAmt(event.getTotalPaymentAmt())
                        .paymentDetails(event.getPaymentDetails())
                        .build();
                commandGateway.sendAndWait(createPaymentCommand);
            } else {
                cancelOrderCommand(event.getOrderId());
            }

        } catch (Exception e) {
            log.error(e.getMessage());
            cancelOrderCommand(event.getOrderId());
        }
    }

    private void cancelOrderCommand(String orderId) {
        CancelOrderCommand cancelOrderCommand = new CancelOrderCommand(orderId);
        commandGateway.sendAndWait(cancelOrderCommand);
    }

    @SagaEventHandler(associationProperty = &quot;orderId&quot;)
    private void handle(PaymentProcessedEvent event) {
        log.info(&quot;[Saga] PaymentProcessedEvent in Saga for Order Id : {}&quot;, event.getOrderId());
        try {
            DeliveryOrderCommand deliveryOrderCommand = DeliveryOrderCommand.builder()
                    .deliveryId(RandomStringUtils.random(15, false, true))
                    .orderId(event.getOrderId())
                    .build();
            log.info(deliveryOrderCommand.toString());
            commandGateway.sendAndWait(deliveryOrderCommand);
        } catch (Exception e) {
            log.error(e.getMessage());
            cancelPaymentCommand(event);
        }
    }

    private void cancelPaymentCommand(PaymentProcessedEvent event) {
        CancelPaymentCommand cancelPaymentCommand = new CancelPaymentCommand(event.getPaymentId(), event.getOrderId());
        commandGateway.sendAndWait(cancelPaymentCommand);
    }

    @SagaEventHandler(associationProperty = &quot;orderId&quot;)
    public void handle(OrderDeliveriedEvent event) {
        log.info(&quot;[Saga] OrderDeliveriedEvent in Saga for Order Id : {}&quot;, event.getOrderId());

        CompleteOrderCommand completeOrderCommand = CompleteOrderCommand.builder()
                .orderId(event.getOrderId()).orderStatus(&quot;APPROVED&quot;)
                .build();

        commandGateway.sendAndWait(completeOrderCommand);
    }

    @SagaEventHandler(associationProperty = &quot;orderId&quot;)
    @EndSaga
    public void handle(OrderCompletedEvent event) {
        log.info(&quot;[Saga] OrderCompletedEvent in Saga for Order Id : {}&quot;, event.getOrderId());
    }

    @SagaEventHandler(associationProperty = &quot;orderId&quot;)
    @EndSaga
    public void handle(OrderCancelledEvent event) {
        log.info(&quot;[Saga] OrderCancelledEvent in Saga for Order Id : {}&quot;, event.getOrderId());
    }

    @SagaEventHandler(associationProperty = &quot;orderId&quot;)
    public void handle(PaymentCancelledEvent event) {
        log.info(&quot;[Saga] PaymentCancelledEvent in Saga for order Id : {}&quot;, event.getOrderId());
        cancelOrderCommand(event.getOrderId());
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 구현시 CommandGateWay Component Injection 을 위해서 @Autowired annotation 적용시 꼭 transient 를 선언해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 서비스 전체의 트랜잭션의 시작과 끝을 알려주는 @StartSaga와 @EndSaga annotation 을 설정 해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 구현 시 굉장히 헷갈리는 부분이 많았지만 정해진 규칙에 따라 Command와 Event 설정을 잘해준다면 처음 접하는 개발자들도 쉽게 적응 할 것으로 생각이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 Axon Server 대신 Kafka를 사용하는 경우도 많다고 하였습니다. 이 부분의 차이점은 알아보았고 처음으로 MSA Transaction을 공부하시는 개발자라면 Axon Framework + Axon Server를 추천 드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka의 경우는 따로 공부를 해야하고 Kafka 에 대해서 공부하는 시간도 많이 들것입니다. 그리고 Kafka 실시간 메세지 처리에 적합하다는 생각이 듭니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트 준비&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Axon Server 를 먼저 실행 해주어야 합니다.&lt;/li&gt;
&lt;li&gt;Axon Server 설치 경로 에서 &lt;b&gt;java -jar axonserver.jar&lt;/b&gt; 실행하여 줍니다.&lt;/li&gt;
&lt;li&gt;각 서비스별 서버를 실행 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;POSTMAN 설정&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;테스트1.PNG&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaE3ap/btr0r6yvAfE/hsXDI2qLBIs6tmjfdbFbZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaE3ap/btr0r6yvAfE/hsXDI2qLBIs6tmjfdbFbZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaE3ap/btr0r6yvAfE/hsXDI2qLBIs6tmjfdbFbZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaE3ap%2Fbtr0r6yvAfE%2FhsXDI2qLBIs6tmjfdbFbZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1112&quot; height=&quot;730&quot; data-filename=&quot;테스트1.PNG&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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 data-token-index=&quot;0&quot;&gt;Body Request 데이터 설정&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1677066538218&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
	&quot;orderDetails&quot;:[
		{
			&quot;productId&quot;:&quot;3820840811&quot;,
			&quot;orderSeq&quot;:1,
			&quot;qty&quot;:1,
			&quot;orderAmt&quot;:3800
		},
		{
			&quot;productId&quot;:&quot;3820840812&quot;,
			&quot;orderSeq&quot;:2,
			&quot;qty&quot;:2,
			&quot;orderAmt&quot;:1200
		}		
	],
	&quot;paymentId&quot;:&quot;263547771707652&quot;,
	&quot;paymentDetails&quot;:[
		{
			&quot;paymentId&quot;:&quot;263547771707652&quot;,
			&quot;paymentGbcd&quot;:&quot;10&quot;,
			&quot;paymentAmt&quot;:5000
		},
		{
			&quot;paymentId&quot;:&quot;263547771707652&quot;,
			&quot;paymentGbcd&quot;:&quot;20&quot;,
			&quot;paymentAmt&quot;:2000
		}
		
	]
}&lt;/code&gt;&lt;/pre&gt;
&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;주문상태는 APPROVED(40), 결재상태는 COMPLETED(30), 배송상태는 REQUESTED(10) 으로 저장&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;테스트결과_정상.PNG&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhjsKv/btr0nClEYYq/X2a5VjfACz2As6wF4gkwFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhjsKv/btr0nClEYYq/X2a5VjfACz2As6wF4gkwFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhjsKv/btr0nClEYYq/X2a5VjfACz2As6wF4gkwFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdhjsKv%2Fbtr0nClEYYq%2FX2a5VjfACz2As6wF4gkwFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;424&quot; data-filename=&quot;테스트결과_정상.PNG&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;주문상태는 CANCELD(20) 결재, 배송 데이터는 등록이 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://InventoryProjection.java&quot;&gt;InventoryProjection.java&lt;/a&gt; 에서 아래 코드를 변경 후 inventory-service 재시작 후 테스트를 진행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1677066632055&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;InventoryVO inventoryVO = new InventoryVO(query.getProductId(), 0);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;테스트결과_오류1.PNG&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sh01i/btr0p84EqkA/QeSxhJKAFkOZ3UMrrCXlYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sh01i/btr0p84EqkA/QeSxhJKAFkOZ3UMrrCXlYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sh01i/btr0p84EqkA/QeSxhJKAFkOZ3UMrrCXlYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsh01i%2Fbtr0p84EqkA%2FQeSxhJKAFkOZ3UMrrCXlYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;367&quot; data-filename=&quot;테스트결과_오류1.PNG&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;주문상태는 CANCELD(20), 결재상태는 CANCELD(20), 배송 데이터는 등록 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://OrderProcessingSaga.java&quot;&gt;OrderProcessingSaga.java&lt;/a&gt; 에서 강제로 배송요청 오류를 발생시킨 후 테스트를 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 수정 후 order-server 재시작 후 테스트를 진행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1677066745904&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SagaEventHandler(associationProperty = &quot;orderId&quot;)
private void handle(PaymentProcessedEvent event) {
	log.info(&quot;[Saga] PaymentProcessedEvent in Saga for Order Id : {}&quot;, event.getOrderId());
	try {
		if (true)
			throw new RuntimeException(&quot;배송요청중 오류가 발생하였습니다.&quot;);

		DeliveryOrderCommand deliveryOrderCommand = DeliveryOrderCommand.builder()
				.deliveryId(RandomStringUtils.random(15, false, true))
				.orderId(event.getOrderId())
				.build();
		log.info(deliveryOrderCommand.toString());
		commandGateway.sendAndWait(deliveryOrderCommand);
	} catch (Exception e) {
		log.error(e.getMessage());
		cancelPaymentCommand(event);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;테스트결과_오류2.PNG&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bO5WBc/btr0n8xwxOF/WczRmRqDFgHNNflbIsYD4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bO5WBc/btr0n8xwxOF/WczRmRqDFgHNNflbIsYD4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bO5WBc/btr0n8xwxOF/WczRmRqDFgHNNflbIsYD4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbO5WBc%2Fbtr0n8xwxOF%2FWczRmRqDFgHNNflbIsYD4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;424&quot; data-filename=&quot;테스트결과_오류2.PNG&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;소스다운로드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/roopy1210/spring-msa-eda-with-axon&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/roopy1210/spring-msa-eda-with-axon&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1677066835619&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - roopy1210/spring-msa-eda-with-axon&quot; data-og-description=&quot;Contribute to roopy1210/spring-msa-eda-with-axon development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/roopy1210/spring-msa-eda-with-axon&quot; data-og-url=&quot;https://github.com/roopy1210/spring-msa-eda-with-axon&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/roopy1210/spring-msa-eda-with-axon&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/roopy1210/spring-msa-eda-with-axon&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - roopy1210/spring-msa-eda-with-axon&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to roopy1210/spring-msa-eda-with-axon development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Micro Services/event driven architecture</category>
      <category>Axon</category>
      <category>Event Driven Architecture</category>
      <category>event sourcing</category>
      <category>MSA</category>
      <category>SAGA</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/99</guid>
      <comments>https://jydlove.tistory.com/99#entry99comment</comments>
      <pubDate>Wed, 22 Feb 2023 20:55:25 +0900</pubDate>
    </item>
    <item>
      <title>Axon Framework + Axon Server + Saga pattern</title>
      <link>https://jydlove.tistory.com/98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 주문서비스를 Axon Framwork + Axon Server + Saga pattern을 이용하여서 구현하도록 해보고자 한다. 구현에 앞서 Axon Framwork + Axon Server + Saga pattern 에 대해 알아보고 구현을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Axon Framework + Axon Server&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;axon.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpHKyR/btr0mdl3L7z/TG2k5HkY6o1SZnh1JuArN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpHKyR/btr0mdl3L7z/TG2k5HkY6o1SZnh1JuArN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpHKyR/btr0mdl3L7z/TG2k5HkY6o1SZnh1JuArN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpHKyR%2Fbtr0mdl3L7z%2FTG2k5HkY6o1SZnh1JuArN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;503&quot; data-filename=&quot;axon.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Axon Server는 필수 사항은 아니다. Axon Server 대신에 Kafka도 많이 사용하는듯 하다. 예제도 Axon Framework + Kafka로 구현한 예제도 있는 것을 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Axons Server &lt;b&gt;vs&lt;/b&gt; Kafka의 차이는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.axoniq.io/blog/axon-and-kafka&quot;&gt;https://www.axoniq.io/blog/axon-and-kafka&lt;/a&gt; 사이트에서 정의한 내용은 아래와 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;i&gt;&lt;b&gt;Kafka - Event Streaming, not Event Sourcing&lt;/b&gt;&lt;/i&gt;&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;EventSouring&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 소싱은 애플리케이션의 상태가 발생한 일련의 이벤트에서 파생되는 디자인 패턴입니다. 이 패턴에서는 엔터티의 현재 상태를 저장하는 대신 엔터티에 대한 모든변경 사항이 일련의 이벤트로 기록됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이벤트는 영구 데이터 저장소에 저장되며 언제든지 엔터티 상태를 재구성하는 데 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 소싱은 발생한 모든 변경 사항에 대한 전체 기록을 제공하므로 변경 사항의 감사 및 추적 가능성이 중요한 복잡한 비즈니스 애플리케이션에서 자주 사용됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;EventStreaming&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 스트리밍은 이벤트 스트림을 실시간으로 지속적으로 처리하는 메커니즘입니다. 여기에는 하나 이상의 소스에서 이벤트 스트림을 캡처하고 이벤트를 처리 및 보강한다음 결과를 데이터 저장소에 기록하거나 추가 작업을 트리거하는 작업이 포함됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 스트리밍은 실시간 분석, 사기 탐지 및 IoT 데이터 처리와 같이 실시간 처리가 필요한 시나리오에서 자주 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Saga 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saga 패턴은 long-lived 트랜잭션을 관리하는데 사용되는 디자인 패터입니다. long-lived 트랜잭션은 여러 단계가 포함되며 각 단계는 시스템의 다른 서비스 또는 구성 요소에 의해 수행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saga 패턴의 사용하는 주된 목적은 부분적인 실패에도 불구하고 트랜잭션이 안정적이고 일관되게 실행되도록 하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saga 패턴은 일련의&amp;nbsp;로컬 트랜잭션을 사용하여 트랜잭션 관리를 제공합니다. 로컬 트랜잭션은 Saga 참가자가 수행하는 원자성 작업입니다. 각 로컬 트랜잭션은 데이터베이스를 업데이트하고 메시지 또는 이벤트를 게시하여 Saga에서 다음 로컬 트랜잭션을 트리거합니다. 로컬 트랜잭션이 실패하면 Saga는 이전 로컬 트랜잭션에 의해 변경된 내용을 실행 취소하는&amp;nbsp;일련의 보상&amp;nbsp;트랜잭션을 실행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;saga-overview.png&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/do4uy6/btr0ojFMs8T/KdI79znhNrTBiCpHk9SNuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/do4uy6/btr0ojFMs8T/KdI79znhNrTBiCpHk9SNuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/do4uy6/btr0ojFMs8T/KdI79znhNrTBiCpHk9SNuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdo4uy6%2Fbtr0ojFMs8T%2FKdI79znhNrTBiCpHk9SNuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;973&quot; height=&quot;290&quot; data-filename=&quot;saga-overview.png&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Saga Orchestration&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오케스트레이션은 중앙 집중식 컨트롤러가 Saga 참가자에게 실행할 로컬 트랜잭션을 알려주는 Saga 조정 방법입니다. Saga 오케스트레이터는 모든 트랜잭션을 처리하고 이벤트에 따라 수행할 작업을 참가자에게 알려줍니다. 오케스트레이터는 Saga 요청을 실행하고, 각 작업의 상태를 저장 및 해석하며, 보상 트랜잭션을 사용하여 오류 복구를 처리합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;orchestrator.png&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b495qw/btr0pwq6Vdu/KzmacIj9hFqig1fQhSTGD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b495qw/btr0pwq6Vdu/KzmacIj9hFqig1fQhSTGD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b495qw/btr0pwq6Vdu/KzmacIj9hFqig1fQhSTGD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb495qw%2Fbtr0pwq6Vdu%2FKzmacIj9hFqig1fQhSTGD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;679&quot; height=&quot;288&quot; data-filename=&quot;orchestrator.png&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;참고사이트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/ko-kr/azure/architecture/reference-architectures/saga/saga&quot;&gt;https://learn.microsoft.com/ko-kr/azure/architecture/reference-architectures/saga/saga&lt;/a&gt;&lt;/p&gt;</description>
      <category>Spring Micro Services/event driven architecture</category>
      <category>Axon</category>
      <category>axon framework</category>
      <category>axon server</category>
      <category>Event Driven Architecture</category>
      <category>event sourcing</category>
      <category>MSA</category>
      <category>saga pattern</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/98</guid>
      <comments>https://jydlove.tistory.com/98#entry98comment</comments>
      <pubDate>Wed, 22 Feb 2023 20:40:10 +0900</pubDate>
    </item>
    <item>
      <title>인터페이스 JMeter 성능 테스트</title>
      <link>https://jydlove.tistory.com/97</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;MSA 서비스 개발시 보통 REST 방식을 가장 많이 쓸것이다. REST방식외에 WebFlux와 gRPC 방식으로 개발했을때 성능을 테스트 해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebFlux의 경우 R2DBC와 JDBC 방식으로 개발하여서 테스트를 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 전의 예상의 WebFlux는 비동기 방식을 지원하더라도 일부 NoSQL이 아닌 RDB의 경우 Blocking 방식으로 처리 되어서 JDBC 연동은 느릴것으로 생각했었고 R2DBC는 비동기 방식을 지원하기 때문에 R2DBC가 빠를 것으로 예상했으나 그렇지는 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 단순희 조회 하나만 테스트 해서 그럴 수 있을 것이다. 향후 좀더 다양한 케이이스를 추가해봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트시나리오&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1초에 동시에 100명의 사용자가 현재 위치에서 가까운 스타벅스 매장을 검색한다고 가정 하고 JMeter를 통해서 테스트를 해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB는 R2DBC 지원을 하는 PostgreSQL을 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 방식은 총 4가지 방식으로 진행하였다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;RESTful&lt;/li&gt;
&lt;li&gt;WebFlux + JDBC&lt;/li&gt;
&lt;li&gt;WebFlux + R2DBC&lt;/li&gt;
&lt;li&gt;gRPC&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;JMeter 설정&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Thred Group 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jmeter_thread_group_설정.PNG&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJBov/btrYLyjpkvI/xgT20XkreZxjCnrEsoETG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJBov/btrYLyjpkvI/xgT20XkreZxjCnrEsoETG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJBov/btrYLyjpkvI/xgT20XkreZxjCnrEsoETG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJBov%2FbtrYLyjpkvI%2FxgT20XkreZxjCnrEsoETG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1127&quot; height=&quot;457&quot; data-filename=&quot;jmeter_thread_group_설정.PNG&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;HttpRequest 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jmeter_http_request_설정.PNG&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n0ak3/btrYJ1sRM1N/3TunBpYvyXLaziWAkkSJJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n0ak3/btrYJ1sRM1N/3TunBpYvyXLaziWAkkSJJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n0ak3/btrYJ1sRM1N/3TunBpYvyXLaziWAkkSJJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn0ak3%2FbtrYJ1sRM1N%2F3TunBpYvyXLaziWAkkSJJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1124&quot; height=&quot;414&quot; data-filename=&quot;jmeter_http_request_설정.PNG&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트결과&lt;/b&gt;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Summary Report&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Report의 Label 설명은 아래와 같다.&lt;/p&gt;
&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;#Samples&lt;/b&gt;: 사용자별 서버 요청 횟수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Average&lt;/b&gt;: 평균응답시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Min&lt;/b&gt;: 최소응답시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Max&lt;/b&gt;: 최대응답시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Std. Dev&lt;/b&gt;: 표준편차&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;요청에 대한 응답시간의 일정하고 안정적인가를 확인, 값이 작을수록 안정적이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Error %: &lt;/b&gt;Error율&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Throughout&lt;/b&gt;: &lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;처리량(초당 처리건수)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;&lt;b&gt;KB/sec&lt;/b&gt; : 처리량(초당 처리 KB)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;RESTFul&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;summary_reposrt.PNG&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuAy8E/btrYGtKXvzI/CpW4n7qQh4Getp2g88KC9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuAy8E/btrYGtKXvzI/CpW4n7qQh4Getp2g88KC9k/img.png&quot; data-alt=&quot;RESTFul Summay Report&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuAy8E/btrYGtKXvzI/CpW4n7qQh4Getp2g88KC9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuAy8E%2FbtrYGtKXvzI%2FCpW4n7qQh4Getp2g88KC9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1141&quot; height=&quot;233&quot; data-filename=&quot;summary_reposrt.PNG&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RESTFul Summay Report&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;WebFlux + JDBC&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;summary_reposrt.PNG&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnwUP6/btrYJaDzrKA/I1qiwAVoqkfcvKNKG4z4Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnwUP6/btrYJaDzrKA/I1qiwAVoqkfcvKNKG4z4Zk/img.png&quot; data-alt=&quot;WebFlux + JDBC Summary Report&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnwUP6/btrYJaDzrKA/I1qiwAVoqkfcvKNKG4z4Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnwUP6%2FbtrYJaDzrKA%2FI1qiwAVoqkfcvKNKG4z4Zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1135&quot; height=&quot;229&quot; data-filename=&quot;summary_reposrt.PNG&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WebFlux + JDBC Summary Report&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;WebFlux + R2DBC&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;summary_reposrt.PNG&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi6kxo/btrYMXcaYhm/Nva7gk07ZrKKYWZIIkXT41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi6kxo/btrYMXcaYhm/Nva7gk07ZrKKYWZIIkXT41/img.png&quot; data-alt=&quot;WebFlux + R2DBC Summary Report&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi6kxo/btrYMXcaYhm/Nva7gk07ZrKKYWZIIkXT41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi6kxo%2FbtrYMXcaYhm%2FNva7gk07ZrKKYWZIIkXT41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1133&quot; height=&quot;230&quot; data-filename=&quot;summary_reposrt.PNG&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WebFlux + R2DBC Summary Report&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;gRPC&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;summary_reposrt.PNG&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HJ7A1/btrYJbboUFl/flvXUKZnCQm90L3K3e79uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HJ7A1/btrYJbboUFl/flvXUKZnCQm90L3K3e79uK/img.png&quot; data-alt=&quot;gRPC Summary Report&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HJ7A1/btrYJbboUFl/flvXUKZnCQm90L3K3e79uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHJ7A1%2FbtrYJbboUFl%2FflvXUKZnCQm90L3K3e79uK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1133&quot; height=&quot;230&quot; data-filename=&quot;summary_reposrt.PNG&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gRPC Summary Report&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 보면 WebFlux + JDBC의 성능이 가장 좋은 것으로 나타 났다. 위의 결과엣 우선 표준편차의 경우 작으면 작을수록 좋으면 그만큼 요청에 대한 응답시간이 일정하고 안정적인이므로 어플리케션 튜닝에 참고 하면 될것이다. 바면에 Throughout의 경우는 크면 클수록 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Response Times Over Time&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;RESTFul&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;response_times_over_time.PNG&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhRlEr/btrYI6Vu9P8/Wm5MrnykvsWlWg7zRhYKG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhRlEr/btrYI6Vu9P8/Wm5MrnykvsWlWg7zRhYKG1/img.png&quot; data-alt=&quot;RESTFul Response Times Over Time&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhRlEr/btrYI6Vu9P8/Wm5MrnykvsWlWg7zRhYKG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhRlEr%2FbtrYI6Vu9P8%2FWm5MrnykvsWlWg7zRhYKG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1114&quot; height=&quot;546&quot; data-filename=&quot;response_times_over_time.PNG&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RESTFul Response Times Over Time&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span&gt;WebFlux + JDBC&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;response_times_over_time.PNG&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yMAzp/btrYJbia4Jt/87jFfp1PK5IowttvJssY91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yMAzp/btrYJbia4Jt/87jFfp1PK5IowttvJssY91/img.png&quot; data-alt=&quot;WebFlux + JDBC Response Times Over Time&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yMAzp/btrYJbia4Jt/87jFfp1PK5IowttvJssY91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyMAzp%2FbtrYJbia4Jt%2F87jFfp1PK5IowttvJssY91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1114&quot; height=&quot;541&quot; data-filename=&quot;response_times_over_time.PNG&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;541&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WebFlux + JDBC Response Times Over Time&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;WebFlux + R2DBC&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;response_times_over_time.PNG&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2ty8z/btrYLzvSfcj/AhGp140ukVwilcJVZRbBak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2ty8z/btrYLzvSfcj/AhGp140ukVwilcJVZRbBak/img.png&quot; data-alt=&quot;WebFlux + R2DBC Response Times Over Time&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2ty8z/btrYLzvSfcj/AhGp140ukVwilcJVZRbBak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2ty8z%2FbtrYLzvSfcj%2FAhGp140ukVwilcJVZRbBak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1116&quot; height=&quot;540&quot; data-filename=&quot;response_times_over_time.PNG&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WebFlux + R2DBC Response Times Over Time&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;gRPC&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;response_times_over_time.PNG&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z0Tzp/btrYMPFcJzp/AvqdkzETippqE5RsxGION1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z0Tzp/btrYMPFcJzp/AvqdkzETippqE5RsxGION1/img.png&quot; data-alt=&quot;gRPC Response Times Over Time&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z0Tzp/btrYMPFcJzp/AvqdkzETippqE5RsxGION1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ0Tzp%2FbtrYMPFcJzp%2FAvqdkzETippqE5RsxGION1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1112&quot; height=&quot;538&quot; data-filename=&quot;response_times_over_time.PNG&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gRPC Response Times Over Time&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 보면 WebFlux + JDBC 의 경우 Max 01.초 이고 평균 0.05초의 응답시간을 나타내었고 다음으로 WebFlux + R2DBC 의 경우 처음에 Max 2.1 초 이고 평균 0.03초 아래로 거의 바로 응답이 오는 거을 확인 할 수있다. 나머지 RESTful 과 gRPC의 경우는 많은 차이가 나는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;TPS&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;RESTFul&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;transaction_per_second.PNG&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d3AYkW/btrYMOTPJ4y/iJVRr3VESnMq9l01VxM701/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d3AYkW/btrYMOTPJ4y/iJVRr3VESnMq9l01VxM701/img.png&quot; data-alt=&quot;RESTFul TPS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d3AYkW/btrYMOTPJ4y/iJVRr3VESnMq9l01VxM701/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd3AYkW%2FbtrYMOTPJ4y%2FiJVRr3VESnMq9l01VxM701%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1117&quot; height=&quot;545&quot; data-filename=&quot;transaction_per_second.PNG&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;545&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RESTFul TPS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span&gt;WebFlux + JDBC&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;transaction_per_second.PNG&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwctD3/btrYG8GPjFf/dabM3qWpQQpOOf0gfJkJhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwctD3/btrYG8GPjFf/dabM3qWpQQpOOf0gfJkJhK/img.png&quot; data-alt=&quot;WebFlux + JDBC TPS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwctD3/btrYG8GPjFf/dabM3qWpQQpOOf0gfJkJhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwctD3%2FbtrYG8GPjFf%2FdabM3qWpQQpOOf0gfJkJhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1117&quot; height=&quot;544&quot; data-filename=&quot;transaction_per_second.PNG&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WebFlux + JDBC TPS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;WebFlux + R2DBC&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;transaction_per_second.PNG&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K4xgR/btrYI5I1KlH/qwyCLzHNDKdALtlxfd6ji0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K4xgR/btrYI5I1KlH/qwyCLzHNDKdALtlxfd6ji0/img.png&quot; data-alt=&quot;WebFlux + R2DBC TPS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K4xgR/btrYI5I1KlH/qwyCLzHNDKdALtlxfd6ji0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK4xgR%2FbtrYI5I1KlH%2FqwyCLzHNDKdALtlxfd6ji0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;540&quot; data-filename=&quot;transaction_per_second.PNG&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WebFlux + R2DBC TPS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;gRPC&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;transaction_per_second.PNG&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bavBOL/btrYG8tkCDZ/4iG7xduxSf7F4pjX4FYGpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bavBOL/btrYG8tkCDZ/4iG7xduxSf7F4pjX4FYGpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bavBOL/btrYG8tkCDZ/4iG7xduxSf7F4pjX4FYGpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbavBOL%2FbtrYG8tkCDZ%2F4iG7xduxSf7F4pjX4FYGpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1111&quot; height=&quot;536&quot; data-filename=&quot;transaction_per_second.PNG&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 보면 WebFlux + JDBC 가 가장 좋은 결과를 나타내는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 보면 WebFlux를 사용하는 경우 가장 좋은 성능을 나타내고 있는 거을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSA 뿐만 아니라 서버 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;b&gt;소스다운로드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/roopy1210/spring-msa-interfaces/blob/main/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/roopy1210/spring-msa-interfaces/blob/main/README.md&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675960582993&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - roopy1210/spring-msa-interfaces&quot; data-og-description=&quot;Contribute to roopy1210/spring-msa-interfaces development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/roopy1210/spring-msa-interfaces/blob/main/README.md&quot; data-og-url=&quot;https://github.com/roopy1210/spring-msa-interfaces&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cisbsm/hyRzr5oe1V/h0anYWmQjnjaCaPBVc9Imk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/roopy1210/spring-msa-interfaces/blob/main/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/roopy1210/spring-msa-interfaces/blob/main/README.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cisbsm/hyRzr5oe1V/h0anYWmQjnjaCaPBVc9Imk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - roopy1210/spring-msa-interfaces&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to roopy1210/spring-msa-interfaces development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Micro Services/interfaces</category>
      <category>grpc</category>
      <category>jmeter</category>
      <category>r2dbc</category>
      <category>restful</category>
      <category>webflux</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/97</guid>
      <comments>https://jydlove.tistory.com/97#entry97comment</comments>
      <pubDate>Fri, 10 Feb 2023 01:07:00 +0900</pubDate>
    </item>
    <item>
      <title>Step1: react-netflix-clone-ui</title>
      <link>https://jydlove.tistory.com/96</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;React+Tailwind css를 사용하여서 UI를 구성하며 사용자의 인증 처리는 JWT 토큰을 통해서 인증 처리한다. 본 프로젝트에서는 Refresh Token은 사용하지 않고 AccessToken 만을 사용하여서 인증 처리를 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;주요기능&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;로그인&amp;nbsp;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;사용자가 로그인 요청시 AccessToken 발급 처리 후 LocalStorage에 Token값과, 만료일시를 저장한다. 만료 일시가 중요한 이유는 callBack 함수에서 만료일시가 되면 로그아웃을 수행하기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;로그인.PNG&quot; data-origin-width=&quot;1081&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qvfN5/btrUyilqisN/fl8vLToiSOmcZeKGsLRKHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qvfN5/btrUyilqisN/fl8vLToiSOmcZeKGsLRKHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qvfN5/btrUyilqisN/fl8vLToiSOmcZeKGsLRKHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqvfN5%2FbtrUyilqisN%2Ffl8vLToiSOmcZeKGsLRKHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1081&quot; height=&quot;637&quot; data-filename=&quot;로그인.PNG&quot; data-origin-width=&quot;1081&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 Github를 참고 하시기 바랍니다.&lt;/p&gt;</description>
      <category>React/React Netflix Clone Project</category>
      <category>REACT</category>
      <category>token</category>
      <category>useContext</category>
      <category>로그인</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/96</guid>
      <comments>https://jydlove.tistory.com/96#entry96comment</comments>
      <pubDate>Mon, 26 Dec 2022 01:26:15 +0900</pubDate>
    </item>
    <item>
      <title>Netflix Clone Project 를 시작하며</title>
      <link>https://jydlove.tistory.com/95</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;데모화면.gif&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;963&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmRWyB/btrUuYBvKuD/UcW7U025Qk3vhg2r7wH1Y1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmRWyB/btrUuYBvKuD/UcW7U025Qk3vhg2r7wH1Y1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmRWyB/btrUuYBvKuD/UcW7U025Qk3vhg2r7wH1Y1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bmRWyB/btrUuYBvKuD/UcW7U025Qk3vhg2r7wH1Y1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1912&quot; height=&quot;963&quot; data-filename=&quot;데모화면.gif&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;963&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;프로젝트 개요&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;프로젝트를 시작하게 된 이유는 React와 Tailwind css로 된 clone 사이트를 찾다가 아래 강좌를 보고 UI는 구성하게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ATz8wg6sg30&quot;&gt;https://www.youtube.com/watch?v=ATz8wg6sg30&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=ATz8wg6sg30&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bojOwO/hyQ1hP9W1q/EGvRUYNivO1v8KCYWp1Ef1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=254_130_398_286&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/ATz8wg6sg30&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;하지만 Backend는 Spring Security 와 JWT 를 사용하여서 자체 구축 하였습니다.&lt;/span&gt;&lt;span style=&quot;&quot;&gt;다른 예제에는 Refresh Token 갱신까지 다루었지만 본 프로젝트에서는 서버에서는 지정한 토큰만료시간에 맞춰 로그아웃 처리하도록 하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;&quot;&gt;Requirements&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;[TMDB]:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.themoviedb.org/?language=ko&quot;&gt;https://www.themoviedb.org/?language=ko&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사이트 인증키 발급을 받아야 합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;본 예제에서 사용되는 영화 정보 및 이미지 정보는 TMDB API를 통하여서 구현이 되어있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;Installation&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;i&gt;Step1: react-netflix-clone-ui&lt;/i&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React+Tailwindcss 프로젝트 생성&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-step2-app-server&quot; href=&quot;https://github.com/roopy1210/react-netflix-clone-project#step2-app-server&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;i&gt;&lt;b&gt;Step2: app-server&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-netflix-clone-project 에 사용하는 API 서버&lt;br /&gt;[소스다운로드]:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/roopy1210/react-netflix-clone-project/tree/main/app-server&quot;&gt;https://github.com/roopy1210/react-netflix-clone-project/tree/main/app-server&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-step3-account-server&quot; href=&quot;https://github.com/roopy1210/react-netflix-clone-project#step3-account-server&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;i&gt;&lt;b&gt;Step3: account-server&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 관리를 위한 API Server&lt;br /&gt;[소스다운로드]:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/roopy1210/react-netflix-clone-project/tree/main/account-server&quot;&gt;https://github.com/roopy1210/react-netflix-clone-project/tree/main/account-server&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-step4-token-server&quot; href=&quot;https://github.com/roopy1210/react-netflix-clone-project#step4-token-server&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;i&gt;&lt;b&gt;Step4: token-server&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 토큰 관리&lt;br /&gt;[소스다운로드]:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/roopy1210/react-netflix-clone-project/tree/main/token-server&quot;&gt;https://github.com/roopy1210/react-netflix-clone-project/tree/main/token-server&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-api-flow&quot; href=&quot;https://github.com/roopy1210/react-netflix-clone-project#api-flow&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;API Flow&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;NETFLIX Clone 프로젝트 흐름도.PNG&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dy4x1J/btrUuRh4QY1/BALHKc1Thn0KvMvvaM6ag1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dy4x1J/btrUuRh4QY1/BALHKc1Thn0KvMvvaM6ag1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dy4x1J/btrUuRh4QY1/BALHKc1Thn0KvMvvaM6ag1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdy4x1J%2FbtrUuRh4QY1%2FBALHKc1Thn0KvMvvaM6ag1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1129&quot; height=&quot;455&quot; data-filename=&quot;NETFLIX Clone 프로젝트 흐름도.PNG&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>React/React Netflix Clone Project</category>
      <category>accesstoken</category>
      <category>Netflix Clone</category>
      <category>REACT</category>
      <category>Spring Security</category>
      <category>Tailwind CSS</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/95</guid>
      <comments>https://jydlove.tistory.com/95#entry95comment</comments>
      <pubDate>Mon, 26 Dec 2022 01:02:00 +0900</pubDate>
    </item>
    <item>
      <title>React infinite scroll</title>
      <link>https://jydlove.tistory.com/94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;react-infinite-scroll-component 라이브러리를 이용하여서 페이징 처리에 대해 알아보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;프로젝트 설정 및 라이브러리 설치&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Tailwind 프로젝트 설정&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사이트: &lt;a href=&quot;https://tailwindcss.com/docs/guides/create-react-app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tailwindcss.com/docs/guides/create-react-app&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669814674474&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;Install Tailwind CSS with Create React App - Tailwind CSS&quot; data-og-description=&quot;Setting up Tailwind CSS in a Create React App project.&quot; data-og-host=&quot;tailwindcss.com&quot; data-og-source-url=&quot;https://tailwindcss.com/docs/guides/create-react-app&quot; data-og-url=&quot;https://tailwindcss.com/docs/guides/create-react-app&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bjxjIe/hyQJtRVO4o/jVKxjlvIYVnb9m28KV8yi1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cOsrEM/hyQJvhTcFb/6LGEjIzl3v1K2jwvAO8AB0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bLfsL8/hyQKV0kKgk/Di7p6tnPvVOlVWyfPjwGZK/img.png?width=2880&amp;amp;height=1232&amp;amp;face=0_0_2880_1232&quot;&gt;&lt;a href=&quot;https://tailwindcss.com/docs/guides/create-react-app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tailwindcss.com/docs/guides/create-react-app&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bjxjIe/hyQJtRVO4o/jVKxjlvIYVnb9m28KV8yi1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cOsrEM/hyQJvhTcFb/6LGEjIzl3v1K2jwvAO8AB0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bLfsL8/hyQKV0kKgk/Di7p6tnPvVOlVWyfPjwGZK/img.png?width=2880&amp;amp;height=1232&amp;amp;face=0_0_2880_1232');&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;Install Tailwind CSS with Create React App - Tailwind CSS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Setting up Tailwind CSS in a Create React App project.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tailwindcss.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;react-infinite-scroll-component 설치&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사이트: &lt;a href=&quot;https://www.npmjs.com/package/react-infinite-scroll-component&quot;&gt;https://www.npmjs.com/package/react-infinite-scroll-component&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669814676671&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;react-infinite-scroll-component&quot; data-og-description=&quot;An Infinite Scroll component in react.. Latest version: 6.1.0, last published: 2 years ago. Start using react-infinite-scroll-component in your project by running &amp;#96;npm i react-infinite-scroll-component&amp;#96;. There are 342 other projects in the npm registry usi&quot; data-og-host=&quot;www.npmjs.com&quot; data-og-source-url=&quot;https://www.npmjs.com/package/react-infinite-scroll-component&quot; data-og-url=&quot;https://www.npmjs.com/package/react-infinite-scroll-component&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ezn0E/hyQK1ztuFK/3YHUtjxY98UAFD81WB2RA0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/67viw/hyQKOtmTdK/SPcXvATSK4lM1RXqdW5kBK/img.jpg?width=460&amp;amp;height=460&amp;amp;face=157_108_290_253,https://scrap.kakaocdn.net/dn/zkVhP/hyQKRcxKXz/02u37lGCV2tlni0NRUAn51/img.jpg?width=460&amp;amp;height=460&amp;amp;face=137_78_281_235&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/react-infinite-scroll-component&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.npmjs.com/package/react-infinite-scroll-component&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ezn0E/hyQK1ztuFK/3YHUtjxY98UAFD81WB2RA0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/67viw/hyQKOtmTdK/SPcXvATSK4lM1RXqdW5kBK/img.jpg?width=460&amp;amp;height=460&amp;amp;face=157_108_290_253,https://scrap.kakaocdn.net/dn/zkVhP/hyQKRcxKXz/02u37lGCV2tlni0NRUAn51/img.jpg?width=460&amp;amp;height=460&amp;amp;face=137_78_281_235');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;react-infinite-scroll-component&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An Infinite Scroll component in react.. Latest version: 6.1.0, last published: 2 years ago. Start using react-infinite-scroll-component in your project by running `npm i react-infinite-scroll-component`. There are 342 other projects in the npm registry usi&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.npmjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;tailwind-scrollbar-hide 설치&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사이트: &lt;a href=&quot;https://www.npmjs.com/package/tailwind-scrollbar-hide&quot;&gt;https://www.npmjs.com/package/tailwind-scrollbar-hide&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669814678400&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;tailwind-scrollbar-hide&quot; data-og-description=&quot;tailwindcss plugin for hide scrollbar. Latest version: 1.1.7, last published: a year ago. Start using tailwind-scrollbar-hide in your project by running &amp;#96;npm i tailwind-scrollbar-hide&amp;#96;. There are 32 other projects in the npm registry using tailwind-scrollb&quot; data-og-host=&quot;www.npmjs.com&quot; data-og-source-url=&quot;https://www.npmjs.com/package/tailwind-scrollbar-hide&quot; data-og-url=&quot;https://www.npmjs.com/package/tailwind-scrollbar-hide&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dIUaSa/hyQKS3CRB8/1GECt5TGomdaKuVHUk2ZGk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/tailwind-scrollbar-hide&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.npmjs.com/package/tailwind-scrollbar-hide&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dIUaSa/hyQKS3CRB8/1GECt5TGomdaKuVHUk2ZGk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;tailwind-scrollbar-hide&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;tailwindcss plugin for hide scrollbar. Latest version: 1.1.7, last published: a year ago. Start using tailwind-scrollbar-hide in your project by running `npm i tailwind-scrollbar-hide`. There are 32 other projects in the npm registry using tailwind-scrollb&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.npmjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;The movie database API 인증키 발급&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사이트: &lt;a href=&quot;https://www.themoviedb.org&quot;&gt;https://www.themoviedb.org&lt;/a&gt; &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669814707293&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;The Movie Database (TMDB)&quot; data-og-description=&quot;Welcome. Millions of movies, TV shows and people to discover. Explore now.&quot; data-og-host=&quot;www.themoviedb.org&quot; data-og-source-url=&quot;https://www.themoviedb.org&quot; data-og-url=&quot;https://www.themoviedb.org&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.themoviedb.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.themoviedb.org&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;The Movie Database (TMDB)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Welcome. Millions of movies, TV shows and people to discover. Explore now.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.themoviedb.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;App.js&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669814770350&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState, useEffect } from &quot;react&quot;;
import InfiniteScroll from &quot;react-infinite-scroll-component&quot;;

// API Key
const key = &quot;APIKey&quot;;

export default function App() {
  const [movies, setMvoies] = useState([]);

  const [hasMore, sethasMore] = useState(true);

  const [page, setpage] = useState(2);

  useEffect(() =&amp;gt; {
    const initMovies = async () =&amp;gt; {
      const response = await fetch(
        `https://api.themoviedb.org/3/movie/popular?api_key=${key}&amp;amp;language=en-US&amp;amp;page=1`
      );
      const data = await response.json();
      setMvoies(data.results);
    };

    initMovies();
  }, []);

  // fetch movies
  const fetchMovies = async () =&amp;gt; {
    const response = await fetch(
      `https://api.themoviedb.org/3/movie/popular?api_key=${key}&amp;amp;language=en-US&amp;amp;page=${page}`
    );
    const data = await response.json();

    return data.results;
  };

  // fetch data
  const fetchData = async () =&amp;gt; {
    const userMovies = await fetchMovies();
    console.log(userMovies);

    setMvoies([...movies, ...userMovies]);
    if (userMovies.length === 0 || userMovies.length &amp;lt; 20) {
      sethasMore(false);
    }

    setpage(page + 1);
  };

  return (
    &amp;lt;div id=&quot;scrollableDiv&quot; className=&quot;h-[800px] overflow-y-auto scrollbar-hide&quot;&amp;gt;
      &amp;lt;InfiniteScroll
        dataLength={movies.length}                            // ① 반복되는 컴포넌트의 개수
        next={fetchData}                                      // ② 스크롤이 화면 맨 아리에 닿았을때 호출 되는 함수
        hasMore={hasMore}                                     // ③ 추가 데이터가 있는지 여부
        loader={&amp;lt;h4&amp;gt;Loading...&amp;lt;/h4&amp;gt;}
        scrollableTarget=&quot;scrollableDiv&quot;
        endMessage={
          &amp;lt;p style={{ textAlign: &quot;center&quot; }}&amp;gt;
            &amp;lt;b&amp;gt;Yay! You have seen it all&amp;lt;/b&amp;gt;
          &amp;lt;/p&amp;gt;
        }
      &amp;gt;
        &amp;lt;div className=&quot;w-full p-6&quot;&amp;gt;
          {movies.map((item, id) =&amp;gt; (
            &amp;lt;div
              key={id}
              className=&quot;w-[190px] sm:w-[230px] md:w-[270px] lg:w-[310px] inline-block cursor-pointer relative p-2&quot;
            &amp;gt;
              &amp;lt;img
                className=&quot;w-full h-auto block&quot;
                src={`https://image.tmdb.org/t/p/w500/${item?.backdrop_path}`}
                alt={item?.title}
              /&amp;gt;
              &amp;lt;div className=&quot;absolute top-0 left-0 w-full h-full hover:bg-black/80 opacity-0 hover:opacity-100 text-white&quot;&amp;gt;
                &amp;lt;p className=&quot;whitespace-normal text-xs md:text-sm font-bold flex justify-center items-center h-full text-center&quot;&amp;gt;
                  {item?.title}
                &amp;lt;/p&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          ))}
        &amp;lt;/div&amp;gt;
      &amp;lt;/InfiniteScroll&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 방법은https://www.npmjs.com/package/react-infinite-scroll-component 사이트를 참고 하면 됩니다.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;소스에서&amp;nbsp;가장&amp;nbsp;중요한&amp;nbsp;부분에&amp;nbsp;대해서&amp;nbsp;설명&amp;nbsp;하겠습니다. &lt;br /&gt;&lt;br /&gt;①&amp;nbsp;&lt;u&gt;&lt;b&gt;dataLength&lt;/b&gt;&lt;/u&gt;&amp;nbsp;속성은&amp;nbsp;반복되는&amp;nbsp;컴포넌의&amp;nbsp;개수&amp;nbsp;입니다.&amp;nbsp;The&amp;nbsp;movie&amp;nbsp;database&amp;nbsp;API&amp;nbsp;에서&amp;nbsp;인기&amp;nbsp;있는&amp;nbsp;영화&amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; 목록을 조회하는데 페이지당 20개의 데이터를 받기 때문에 dataLength 속성은 movies.length 로&amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; 지정 합니다. &lt;br /&gt;&lt;br /&gt;②&amp;nbsp;&lt;u&gt;&lt;b&gt;next&lt;/b&gt;&lt;/u&gt;&amp;nbsp;속성은&amp;nbsp;스크롤이&amp;nbsp;화면&amp;nbsp;맨아래&amp;nbsp;닿았을때&amp;nbsp;호출이&amp;nbsp;되는&amp;nbsp;함수인데&amp;nbsp;모니터의&amp;nbsp;해상도가&amp;nbsp;높은&amp;nbsp;경우&amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; 20개의 이미지를 불러와도 화면에 꽉차지 않기때문에 스크롤이 안되는 현상이 발생 하였습니다. &lt;br /&gt;&amp;nbsp; &amp;nbsp;이를 해결하기 위해 InfiniteScroll 컴포넌트를 감싸는 div 태그에 id=&amp;rdquo;scrollableDiv&amp;rdquo; 를 지정 하고&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;infiniteScroll의 scrollableTarget 속성에 scrollableDiv 지정 합니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;그리고 scollbar를 숨기기 위해서 scrollbar-hide 속성을 사용하여서 scrollbar 숨김처리를 해줍니다. &lt;br /&gt;&lt;br /&gt;③&amp;nbsp;&lt;u&gt;&lt;b&gt;hasMore&lt;/b&gt;&lt;/u&gt;&amp;nbsp;속성은&amp;nbsp;데이터가&amp;nbsp;더&amp;nbsp;있는&amp;nbsp;경우&amp;nbsp;스크롤을&amp;nbsp;계속&amp;nbsp;해주고&amp;nbsp;그렇지&amp;nbsp;않으면&amp;nbsp;endMessage&amp;nbsp;속성 &lt;br /&gt;&amp;nbsp; &amp;nbsp; 에 정의된 메세지를 출력하고 끝납니다. &lt;br /&gt;&amp;nbsp; &amp;nbsp; hasMore 처리로직은 페이지당 20개의 데이터를 불러오게 되는데 마지막에는 0이거나 20개이하 &lt;br /&gt;&amp;nbsp; &amp;nbsp; 이기 때문에 불러온 데이터의 length 로 판단 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데모 화면&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;데모화면.gif&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;907&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OVvh2/btrSw5VMFnS/WPt5wn7kVKq4Hg6b1UdbOK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OVvh2/btrSw5VMFnS/WPt5wn7kVKq4Hg6b1UdbOK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OVvh2/btrSw5VMFnS/WPt5wn7kVKq4Hg6b1UdbOK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/OVvh2/btrSw5VMFnS/WPt5wn7kVKq4Hg6b1UdbOK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1304&quot; height=&quot;907&quot; data-filename=&quot;데모화면.gif&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;907&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;소스다운로드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/roopy1210/react-infinite-scroll&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/roopy1210/react-infinite-scroll&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669815045355&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - roopy1210/react-infinite-scroll&quot; data-og-description=&quot;Contribute to roopy1210/react-infinite-scroll development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/roopy1210/react-infinite-scroll&quot; data-og-url=&quot;https://github.com/roopy1210/react-infinite-scroll&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tf3ns/hyQKT2weSf/9Tp4bbr5jXzjk4GvvqGfr0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/roopy1210/react-infinite-scroll&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/roopy1210/react-infinite-scroll&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tf3ns/hyQKT2weSf/9Tp4bbr5jXzjk4GvvqGfr0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - roopy1210/react-infinite-scroll&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to roopy1210/react-infinite-scroll development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React</category>
      <category>React Infinite Scroll</category>
      <category>React Paging</category>
      <category>scrollbar-hide</category>
      <category>TAILWIND</category>
      <category>The movie db</category>
      <author>devguru</author>
      <guid isPermaLink="true">https://jydlove.tistory.com/94</guid>
      <comments>https://jydlove.tistory.com/94#entry94comment</comments>
      <pubDate>Wed, 30 Nov 2022 23:36:38 +0900</pubDate>
    </item>
  </channel>
</rss>