<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>인생은 파도타기</title>
    <link>https://goodthinking.tistory.com/</link>
    <description>개발자의 웹서핑, 기술 블로그</description>
    <language>ko</language>
    <pubDate>Sun, 12 Apr 2026 13:43:32 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>긍정왕웹서퍼</managingEditor>
    <image>
      <title>인생은 파도타기</title>
      <url>https://tistory1.daumcdn.net/tistory/4529329/attach/a469274eea9e4631845a7fee72b57b64</url>
      <link>https://goodthinking.tistory.com</link>
    </image>
    <item>
      <title>이너서클 재직자과정 후기</title>
      <link>https://goodthinking.tistory.com/111</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;이너서클 전 고민&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이너서클에 합류하기 전 당시 나는 혼자서 학습은 꾸준히 했지만 성장에 대한 고민이 많았었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제대로 학습하고 있는가?&lt;/li&gt;
&lt;li&gt;잘못된 방향으로 하고 있는건 아닌가?&lt;/li&gt;
&lt;li&gt;이직 준비는 제대로 하고 있는가?&lt;/li&gt;
&lt;li&gt;무엇부터 준비해야 하는가?&lt;/li&gt;
&lt;li&gt;내게 필요한게 무엇인가?&lt;/li&gt;
&lt;li&gt;지금 하는 일과 회사가 내가 성장할 수 있는 환경인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;thinking.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kxfZc/btsLSrRhzhN/M4CigcGBaVrUi0Qe13dwS1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kxfZc/btsLSrRhzhN/M4CigcGBaVrUi0Qe13dwS1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kxfZc/btsLSrRhzhN/M4CigcGBaVrUi0Qe13dwS1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkxfZc%2FbtsLSrRhzhN%2FM4CigcGBaVrUi0Qe13dwS1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;553&quot; data-filename=&quot;thinking.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&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;하지만 이너서클은 원한다고 모두 참여할 수 있는 게 아니었고, 신청할 때 요구된 사전질문 사항들이 평소 고민하고 준비하지 않았다면 작성하기 힘들었을 것 같다. 그럼에도 정말 하고싶다는 간절한 생각으로 부단히 써서 제출했던 기억이 있다. 그리고 운이 좋게도 여러 절차끝에 이너서클에 참여할 수 있었다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;innerCircle-최종합격.png&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PULJp/btsLQ5hFMtl/mZ7hoL6P6GZkbK9UI4Qsxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PULJp/btsLQ5hFMtl/mZ7hoL6P6GZkbK9UI4Qsxk/img.png&quot; data-alt=&quot;이너서클 최종 합류 안내&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PULJp/btsLQ5hFMtl/mZ7hoL6P6GZkbK9UI4Qsxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPULJp%2FbtsLQ5hFMtl%2FmZ7hoL6P6GZkbK9UI4Qsxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;601&quot; height=&quot;681&quot; data-filename=&quot;innerCircle-최종합격.png&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이너서클 최종 합류 안내&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;과정을 시작하며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이너서클을 본격적으로 시작한건 7월 20일 OT 였는데, 디스코드와 Zep이라는 메타버스 플랫폼에 접속했고 앞으로 일정에 대해 공유받고 이너서클을 시작할 수 있었다. 처음 몇 주 동안은 개인 프로젝트로 오픈소스를 만들어보고 리뷰받으며 진태양 리더님에게 여러가지 코딩과 관련된 팁도 전달받고 기술 이야기도 나누며 즐거운 시간을 보냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 4주차 무렵, 드디어 팀 프로젝트를 하게 되었고, 리더님들의 많은 고민이 담긴 프로젝트 주제를 알게 되었을 때 정말 놀랐던 기억이 있다! 경력자 수준에 맞게 요구사항이 디테일했고 요구하는 기능이 명확했지만 어떻게 개발할 것인가?에서 부터 고민해야 할 부분들이 많았지만, 성장하고자 하는 욕심이 있는 사람들끼리 모여 팀프로젝트를 진행하니 수월하게 진행할 수 있었고, 초기 기획단계부터 재밌게 진행할 수 있었다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;innerCircle-Zep.webp&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw1Etf/btsLSRhVdCQ/badYatVQM68wINLDbRSElK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw1Etf/btsLSRhVdCQ/badYatVQM68wINLDbRSElK/img.webp&quot; data-alt=&quot;이너서클 과정 중&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw1Etf/btsLSRhVdCQ/badYatVQM68wINLDbRSElK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw1Etf%2FbtsLSRhVdCQ%2FbadYatVQM68wINLDbRSElK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;712&quot; height=&quot;374&quot; data-filename=&quot;innerCircle-Zep.webp&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이너서클 과정 중&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;중간중간 리더님들과 외부 멘토를 초청하는 등 지속적으로 프로젝트의 진행과 고민사항에 대해 공유하고 이야기 나눌 수 있어서 선배 개발자들의 시선을 보고 배우며, 멘토링을 통해 개인적인 고민까지 다양한 이야기를 나누고 성장할 수 있는 좋은 기회였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 좋기만 한 건 아니었다. 주4일이상 평일 저녁과 토요일엔 9-6까지 14주차가 넘는 많은 시간을 투자해야하며, 이 시간외에도 고민하고 개발하는 시간까지 생각하면 이너서클 기간동안 정말 몰두할 수 있어야한다. (그래서 여름 휴가를 못갔다 ㅠㅠ) 주말까지 투자해야하는 스케줄인데 직장인들이 대부분인 이너서클은 끝까지 버티기가 정말 쉽지 않았다. (국비교육이라 출석율도 어느정도 지켜야 했다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;수료하면서 느낀점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 이너서클을 참여하고 끝까지 완주하면서 느끼고 배운점이 많았고 반강제?라도 계속해서 공부하고 코딩하는 시간을 투자할 수 있게되어 많은 성장을 할 수 있었고, 정신 없이 학습과 개발에 몰입하는 소중한 시간이었다. 그리고 스스로도 무언가를 끝까지 해내는 경험을 통해 자신감도 얻을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이너서클을 통해 배운점도 많았고 내가 살아오면서 이만큼 몰입하는 경험을 할 기회가 얼마나 될까? 싶을정도로 일하면서도 이너서클에서 할 작업들을 고민하고, 공부하면서 하루종일 코딩했지만 즐겁게 해나갈 수 있었다. 그리고 그 결과! 업계가 많이 힘들지만 나름 &lt;b&gt;대기업 계열사의 이커머스 기업&lt;/b&gt;으로 &lt;b&gt;이직&lt;/b&gt;할 수 있었고 연봉과 처우도 많이 좋아졌다고 생각한다. 이전보다 많은 트래픽을 경험하고 한 도메인의 담당자로서 이전에는 하지못했을 경험들을 하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;innerCircle-수료증.png&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKCRZL/btsLRITOSRj/aZdaqMPolNJaKCvOcoE7r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKCRZL/btsLRITOSRj/aZdaqMPolNJaKCvOcoE7r0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKCRZL/btsLRITOSRj/aZdaqMPolNJaKCvOcoE7r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKCRZL%2FbtsLRITOSRj%2FaZdaqMPolNJaKCvOcoE7r0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;820&quot; data-filename=&quot;innerCircle-수료증.png&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이너서클을 진행하며 멘토님들과 멘토링, 이너서클 동료들과의 기술 토론과 교류, 성장할 수 있는 환경(반강제) 등 이너서클을 해야할 이유들이 정말 많았고 후회하지 않는 시간이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ps&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간중간 네트워킹과 수료식등 오프라인으로 만나 교류할 수 있는 시간도 있어서 즐겁게 참여할 수 있었고, 열정있는 사람들을 많이 알게되어 좋았다. 미처 글에서 말하지 못한 다양한 크고 작은 세션(1:1 &amp;amp; 그룹 멘토링, 멘토님의 사이드 프로젝트 노하우 세션, 포트폴리오 특강 등)들도 뜻깊은 시간들이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;innerCircle-노하우.webp&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgeQ0X/btsLQ3qA1yS/7JRhbzjKU5NPahZau4A9rk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgeQ0X/btsLQ3qA1yS/7JRhbzjKU5NPahZau4A9rk/img.webp&quot; data-alt=&quot;태양 리더님의 사이드 프로젝트 특강&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgeQ0X/btsLQ3qA1yS/7JRhbzjKU5NPahZau4A9rk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgeQ0X%2FbtsLQ3qA1yS%2F7JRhbzjKU5NPahZau4A9rk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;405&quot; data-filename=&quot;innerCircle-노하우.webp&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;태양 리더님의 사이드 프로젝트 특강&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Living</category>
      <category>fastcampus 성공 스토리</category>
      <category>inner circle</category>
      <category>개발자 경력</category>
      <category>이너서클</category>
      <category>패스트캠퍼스 재직자 과정</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/111</guid>
      <comments>https://goodthinking.tistory.com/111#entry111comment</comments>
      <pubDate>Fri, 17 Jan 2025 23:39:07 +0900</pubDate>
    </item>
    <item>
      <title>토스 SLASH 24 - 시간표 공유하기 이벤트</title>
      <link>https://goodthinking.tistory.com/110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;토스가 올해도 SHASH 라는 이름의 개발자 컨퍼런스를 개최한다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 재밌는 주제로 발표를 하는데, 저는 서버 개발세션으로만 꽉 채웠습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주제들도 재밌어보여서 다른 세션을 볼 시간이 안날거같네요~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;slash24.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;1701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWSSq4/btsJfqB3eGk/jGmgC4kkbSaqLx3wU9k2xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWSSq4/btsJfqB3eGk/jGmgC4kkbSaqLx3wU9k2xK/img.png&quot; data-alt=&quot;토스 슬래쉬 24 내가 정한 시간표&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWSSq4/btsJfqB3eGk/jGmgC4kkbSaqLx3wU9k2xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWSSq4%2FbtsJfqB3eGk%2FjGmgC4kkbSaqLx3wU9k2xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;1701&quot; data-filename=&quot;slash24.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;1701&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;토스 슬래쉬 24 내가 정한 시간표&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Living</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/110</guid>
      <comments>https://goodthinking.tistory.com/110#entry110comment</comments>
      <pubDate>Tue, 27 Aug 2024 11:29:13 +0900</pubDate>
    </item>
    <item>
      <title>외부 결제 API에서 문제가 생겼을 때 에러 핸들링은 어떻게 할까?</title>
      <link>https://goodthinking.tistory.com/109</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;문제 정의&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 결제 대행 서비스(PG)를 하는 업체에 API를 사용해 결제를 진행하는 로직이 있다고 가정하겠습니다. 해당 PG사에 요청을 보냈는데 커넥션, timeout 혹은 이 외의 예외가 발생해 이에 대한 에러 핸들링이 필요한 상황이라고 가정할 때 어떻게 해야 우아하게 외부 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;가설 1 : 외부 API 에 커넥션 문제가 발생했을 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 서버의 상황을 알 수 없지만, 연결 자체에 문제가 생겼을 경우, 결제 처리 자체가 시도하지 못했으므로, 중복결제가 발생할 확률은 없다고 가정할 수 있습니다. 이런 경우 재시도 로직을 생각해봐야합니다. 먼저 재시도 로직 처리를 위해 각각 거래에 대한 유니크 ID값과 함께 거래 상태를 데이터베이스등에 저장합니다. 그리고 배치와 스케줄러 혹은 메시징 큐와 같이 재시도 처리를 위한 트리거를 통해 재시도 전략을 세우고 시도해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 정책상 재시도처리를 하지 않고 아예 결제상태를 실패 상태로 변경후 데이터베이스에 저장하고 트랜잭션을 종료할 수도 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재시도 로직
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스케줄링&lt;/li&gt;
&lt;li&gt;배치&lt;/li&gt;
&lt;li&gt;메시지큐&lt;/li&gt;
&lt;li&gt;실패처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가설 2 : API의 응답을 기다리던 중 timeout이 발생했을 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, PG사에서 연결 후 응답을 주기전에 처리가 지연되면서 생긴 케이스인지, 연결 시도에서 생긴 케이스인지 구분해볼 수 있습니다. 하지만 이번 케이스에서는 그 상태를 구분하기 어렵다고 가정하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 결제에 대한 처리 상태가 불투명한 경우, 결제 상태를 확인하기 전까지 재시도 처리를 하기에는 중복 결제에 대한 위험이 있습니다. 이는 서비스에 심각한 신뢰에 대한 의심을 가지게 할 수 있으므로 신중히 처리해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 가정할 수 있는 경우는 결제에 대한 주문번호같은 유니크값을 상호 PG사와 공유하고 있는 경우 재시도를 요청해볼 수 있습니다. 보통 결제 시 주문번호를 클라이언트가 생성해 함께 요청하면서 해당 요청건에 대해 검증하는 프로세스가 있다는 가정입니다. 하지만 이와 같은 케이스가 아닌 경우, 다양한 방안을 고려해봐야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 응답에 대해 신뢰할 수 없거나 PG사에서 어떻게 처리되었는지 확인할 수 없다면 재시도 처리가 아닌 대사용 API를 통해 PG사와 검증을 하여 결제에 대한 추적을 해볼 수 있으며, 만약 대사용 API도 어렵거나 신뢰할 수 없다면 먼저 실패 처리로 데이터베이스에 저장 후 클라이언트에 응답한 이후에 나중에 해당 PG사와 대사를 통해 결제건이 성공했다면 취소처리를 할 수 있습니다. 이런 경우 데드레터큐(Dead letter queue) 를 사용해 실패 메시지큐를 보내 위 로직을 비동기로 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결제에서 가장 중요한점은 중복 결제는 없어야한다.&lt;/li&gt;
&lt;li&gt;결제에 주문번호같은 유니크값이 상호 공유된 상태라면 재시도를 요청해 볼 수 있다.&lt;/li&gt;
&lt;li&gt;응답을 알 수 없다면 &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;대사용 API로 검증 혹은 실패&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;2&quot;&gt; 처리로 응답한 이후 나중에 대사를 통해 취소처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;2&quot;&gt;대사와 재시도 등 후처리 로직의 경우 Dead letter queue 같은 실패 처리에 대한 메시지큐를 사용으로 비동기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가설 3 : API의 응답에서 에러가 발생했을 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 경우에는 간단하게 결제상태를 실패처리로 데이터베이스에 저장한 이후 응답하는 것으로 정리할 수 있습니다. 다만, 이 경우에도 PG사에서는 결제가 성공했지만 응답과정에서 오류가 발생했을 경우도 있을 순 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우는 드문 케이스로 보통 내부 직원이 직접 대사를 진행하고 핸들링해야하는 경우가 많을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 경우 외에도 더 고려해볼 수 있는 케이스들이 많겠지만 제가 생각해 볼 수 있는건 이정도인 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 피드백이 있다면 환영합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다.&lt;/p&gt;</description>
      <category>Programming</category>
      <category>결제</category>
      <category>백엔드</category>
      <category>시스템설계</category>
      <category>외부 API</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/109</guid>
      <comments>https://goodthinking.tistory.com/109#entry109comment</comments>
      <pubDate>Mon, 12 Aug 2024 23:14:00 +0900</pubDate>
    </item>
    <item>
      <title>DNS 와 DNS Cache 에 대해 알아보자</title>
      <link>https://goodthinking.tistory.com/108</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 회사에서 내부망과 외부망으로 분리하는 작업을 진행하면서 DNS Cache 때문에 내부망에서 HTTP 요청이 정상적으로 동작하지 않던 이슈가 있었다. 이를 해결하기 위해 서버 전용LAN선을 재연결하거나, 컴퓨터를 재시동하거나, 네트워크를 지웠다 다시 설치하는 등 여러 시도를 했지만 결국 해결한 방법은 DNS Cache를 초기화함으로써 해결할 수 있었고, 오늘 경험했던 DNS Cache가 뭔지 제대로 알기 위해 정리하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;DNS ?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 DNS(Domain Name System) 으로 명칭에서도 직관적으로 알 수 있듯 사람이 읽을 수 있는 도메인 이름을 IP주소로 변환하는 시스템을 의미하며 보통 &lt;a href=&quot;http://www.naver.com&quot;&gt;www.example.com&amp;nbsp;&lt;/a&gt; 이라는 도메인 이름을 ex) 192.168.1.1 처럼 바꿔주는 기능을 하는 서비스이다. 그렇다면 제목에 있는 DNS Cache도 무엇인지 유추할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DNS Cache ?&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS 정보를 일시적으로 저장하는 메모리나, 저장소를 의미하며 이는 동일한 도메인 이름을 다시 검색하는 경우 중복된 요청 대신 캐시에 보관된 IP주소를 반환하는 역할을 함으로써 네트워크 부하를 줄이고 사용자 경험을 개선하기 위해 빠른 응답을 제공할 수 있다. 물론 캐시이기 때문에 캐시를 만료시키기 위한 TTL(Time-To-Live)을 설정하거나 다른 옵션을 통해 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 DNS 캐시는 DNS에 같은 요청을 여러번 하는 것을 줄이기 위해 캐시에 이전에 요청했던 DNS 응답값들을 저장해두었다가 동일한 요청이 온 경우 캐시에서 바로 꺼냄으로써 DNS 서버를 거치는 과정을 줄여 성능을 개선하기 위한 것이었는데, 나같은 경우 내/외부 망을 공용으로 사용하던 환경에서 망 분리를 하면서 이전에 응답받은 DNS 캐시에 값을 받아서 HTTP 요청을 하고 있어서 내부망으로 요청을 하지 않고 변경전 외부 IP로 요청을 보내고 있었던 것이었다.&amp;nbsp; 바로 DNS 캐시를 확인하지 않았던 이유는 망 분리작업이 진행된지 몇일이 지난 상태였기 때문에 당연히 내 네트워크 문제인줄로만 알았는데 DNS 캐시 문제인줄 생각도 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>cache</category>
      <category>DNS</category>
      <category>DNS Cache</category>
      <category>dns 캐시</category>
      <category>network</category>
      <category>네트워크</category>
      <category>캐시</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/108</guid>
      <comments>https://goodthinking.tistory.com/108#entry108comment</comments>
      <pubDate>Thu, 12 Oct 2023 00:09:16 +0900</pubDate>
    </item>
    <item>
      <title>Redis Study - 레디스의 정보 처리</title>
      <link>https://goodthinking.tistory.com/107</link>
      <description>&lt;h1&gt;Redis의 특징&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Key &amp;amp; Value 데이터베이스로 분류되는 NoSQL이며, 2009년 개발되어 2015년에 상용SW로 개발&lt;/li&gt;
&lt;li&gt;키밸류 DB이면서 대표적인 In-Memory 데이터 처리 및 저장기술을 제공하기 때문에 상대적으로 빠른 Read/Write 가능&lt;/li&gt;
&lt;li&gt;String, Set, Sorted Set, Hash, List, HyperLogLogs 등 유형의 데이터 저장 가능&lt;/li&gt;
&lt;li&gt;Dump 파일과 AOF(Append Of File) 방식으로 메모리 상의 데이터를 디스크에 저장할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Master &amp;amp; Slave Replication 기능을 통해 데이터의 분산, 복제 기능을 제공하며 Query Off Loading 기능을 통해 Master는 Read/Write 를 수행하고 Slave는 Read 만 수행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;파티셔닝(Partitioning)을 통해 동적인 스케일 아웃(Scale Out)인 수평 확장이 가능합니다.&lt;/li&gt;
&lt;li&gt;Expriation 기능은 일정 시간이 지났을 때 메모리 상의 데이터를 자동 삭제할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 업무 영역&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인메모리 DB의 특성상 빠른 읽기와 쓰기 작업은 가능하지만 지속적인 관리가 필요한 비즈니스에서는 Sub DB로 사용되는 것이 보편적&lt;/li&gt;
&lt;li&gt;주된 영역은 데이터 캐싱(Caching)을 통한 빠른 쓰기/읽기 작업이 요구되는 영역과 IOT등 데이터 수집 및 처리, 실시간 분석 및 통계 영역&lt;/li&gt;
&lt;li&gt;메시지 큐(Message Queue), 머신 러닝(Machine Learning), Application Job Management, 검색엔진등의 영역&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제품 유형&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;커뮤니티 에디션&lt;/li&gt;
&lt;li&gt;오픈소스 라이센스를 기반으로 별도의 비용 청구 없이 사용 가능하지만 사용자의 전적인 책임하에 사용&lt;/li&gt;
&lt;li&gt;엔터프라이즈 에디션&lt;/li&gt;
&lt;li&gt;사용자가 사용하면서 발생하는 기술적 문제에 개발사가 책임지고 지원 및 유지 보수작업을 수행하며 보통 연간 단위의 유지보수 계약을 체결하며 그에 상응하는 비용을 지불하고 다양한 옵션을 활용&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redis System 아키텍처&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;분산 시스템(Partitioning)&lt;/li&gt;
&lt;li&gt;복제 시스템(Replication)&lt;/li&gt;
&lt;li&gt;Index Support&lt;/li&gt;
&lt;li&gt;관리 툴(Redis-server, redis-cli, redis-benchmark 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3&amp;rsquo;rd Party Module&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis Search Engine : 검색엔진&lt;/li&gt;
&lt;li&gt;RedisSQL : SQLite DB와 연동 솔루션&lt;/li&gt;
&lt;li&gt;RedisGraph : Graph DB와 연동 솔루션&lt;/li&gt;
&lt;li&gt;Redis sPiped : 암호화 솔루션&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt; # 설치 
docker pull redis

 # 도커 내부 network 브릿지 생성 별칭 redis-net 으로 설정함
docker network create redis-net

# 컨테이너 이름설정 후 실행 포트 포워딩, network 등 매개변수로 설정가능
docker run --name redis-test 

# redis_cli 로 네트워크를 통해 접속 
docker run -it --network redis-net --rm redis redis-cli -h redis-test

# 도커 컨테이너 내부에 직접 접속하여 redis-cli를 실행하여 접속
docker exec -it redis-test redis-cli --raw

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redis 기본문법&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;redis-cli [OPTION] [cmd [arg [arg ...]]]

-h &amp;lt;hostname&amp;gt;  Server hostname (default: 127.0.0.1)
-p &amp;lt;port&amp;gt;      Server port (default: 6379)
-s &amp;lt;socket&amp;gt;    Server socket (overrides hostname and port)
-a &amp;lt;password&amp;gt;  Password to use when connection to the server
-u &amp;lt;uri&amp;gt;       Server URI
-r &amp;lt;repeat&amp;gt;    Execute specified command N times
-i &amp;lt;interval&amp;gt;  When -r is used, waits &amp;lt;interval&amp;gt; seconds per comand
-n &amp;lt;db&amp;gt;        Database number
-x             Read last argument from STDIN
-d &amp;lt;delimiter&amp;gt; Multi-bulk delimiter in for raw formatting (default: \\n)
-c             Enable cluster mode (follow -ASK and -MOVED redirections)
--raw          Use raw formatting for replies (default when STDOUT is not a tty)
--no-raw       Force formatted output even when STDOUT is not a tty
--csv          Output in CSV format
--stat         Print rolling stats about server: mem, clients, ...
--latency      Enter a special mode continuosly sampling latency
...-history    Like --latency but tracking latency changes over time. Default time interval is 15 sec.
--latency-dist Shows latency as a spectrum , requires xterm 256 colors. Default time interval is 1 sec. Change it using -i
--lru-test key Simulate a cache workload with an 80-20 distribution
--slave        Simulate a slave showing commands received from the master
--rdb filename transfer an RDB dump from remote server to local file
--pipe         Transfer raw Redis protocol from stdin to server
...-timeout n  pipe mode, abort with error if after sending all data. no reply is received within n seconds. Default timeout: 30.
--bigkeys      Sample Redis keys looking for big keys
--hotkeys      Sample Redis keys looking for hoy keys
--sacn         List all keys using the SCAN command
--pattern pat  Useful with --scan to specify a SCAN pattern
--intrinic-latency sec Run a test to measure intrinsic system latency The test will run for the specified amount of seconds
--eval file    Send an EVAL command using the Lua script at file
--ldb          Used with --eval enable the Redis Lua debugger
--ldb-sync-mode Like --ldb but uses the synchronous Lua debugger
--version      Output version and exit
 

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Redis 데이터 처리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;용어 설명&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Table : 하나의 DB에서 데이터를 저장하는 논리적 구조 (RDB의 테이블과 논리적 개념이 동일)&lt;/li&gt;
&lt;li&gt;Data Sets : 테이블을 구성하는 논리적 단위입니다. 하나의 데이터 셋은 하나의 Key와 한 개 이상의 Filed/Element 로 구성됩니다.&lt;/li&gt;
&lt;li&gt;Key : 하나 이상의 조합된 값&lt;/li&gt;
&lt;li&gt;Values : Key에 대한 구체적인 데이터 값&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 입력, 수정, 삭제, 조회&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;get&lt;/td&gt;
&lt;td&gt;저장된 데이터를 검색할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rename&lt;/td&gt;
&lt;td&gt;저장된 데이터 값을 변경할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;randomkey&lt;/td&gt;
&lt;td&gt;저장된 key 중에 하나의 key를 랜덤하게 검색할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;keys&lt;/td&gt;
&lt;td&gt;저장된 모든 key를 검색할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;exits&lt;/td&gt;
&lt;td&gt;검색 대상 key가 존재하는지 여부를 확인할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mset / mget&lt;/td&gt;
&lt;td&gt;여러 개의 key와 value를 한번 저장하고 검색할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;exists &amp;lt;key&amp;gt;&lt;/td&gt;
&lt;td&gt;key 의 존재여부 검색 (return 있으면 1, 없으면 0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;strlen &amp;lt;key&amp;gt;&lt;/td&gt;
&lt;td&gt;value 의 길이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flushall&lt;/td&gt;
&lt;td&gt;저장되어 있는 모든 key 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;setex &amp;lt;key&amp;gt; (sec) &amp;lt;value&amp;gt;&lt;/td&gt;
&lt;td&gt;value에 대해 일정 시간만 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ttl&lt;/td&gt;
&lt;td&gt;현재 남은 시간 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;incr N&lt;/td&gt;
&lt;td&gt;Incremental 증가값 + 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decr N&lt;/td&gt;
&lt;td&gt;Decremental 감소값 - 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;incrby N 2&lt;/td&gt;
&lt;td&gt;Incremental 증가값 + 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decrby N 10&lt;/td&gt;
&lt;td&gt;Decremental 감소값 - 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;append &amp;lt;key&amp;gt; &amp;lt;value&amp;gt;&lt;/td&gt;
&lt;td&gt;현재 key 에 추가 value를 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set 데이터를 저장할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 타입&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;List&lt;/td&gt;
&lt;td&gt;하나의 key에 여러 개의 배열 값을 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hash&lt;/td&gt;
&lt;td&gt;하나의 key에 여러 개의 Fields와 Value로 구성된 테이블을 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set, Sorted set&lt;/td&gt;
&lt;td&gt;정렬/미정렬 String 타입, Set과 Hash를 결합한 타입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitmaps&lt;/td&gt;
&lt;td&gt;0 과 1로 표현하는 데이터 타입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HyperLogLogs&lt;/td&gt;
&lt;td&gt;Element 중에서 unique 한 개수와 Element 만 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geospatial&lt;/td&gt;
&lt;td&gt;좌표 데이터를 저장 및 관리하는 데이터 타입&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strings 문자, Binary 유형 데이터를 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis에서 데이터를 표현할 때 기본 타입은 하나의 key와 하나 이상의 Field / Element 값으로 저장하는 방법입니다. Key에는 아스키 값을 저장할 수 있고 value에는 기본적으로 Strings 데이터를 저장할 수 있으며 추가로 컨테이너(Container) 타입의 데이터들을 저장할 수 있습니다. 컨테이너 타입에는 Hash, List, Set, Sorted Set 4가지 유형이 있습니다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Strings: Redis에서 가장 기본적인 데이터 타입으로 value에 문자, 숫자 등을 저장한다. 저장 시 별도로 숫자 문자 구분이 없으며 binary safe 하므로 redis string이 파일, 직렬화 객체 등등 다양한 데이터 타입을 모두 포함할 수 있다. 최대 512MB의 길이를 가질 수 있다.&lt;/li&gt;
&lt;li&gt;Hash : Hash 타입은 기존 관계형 DB에서 PK와 하나 이상의 컬럼으로 구성된 테이블 구조와 매우 흡사한 유형입니다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 key는 오브젝트명과 하나 이상의 필드 값을 콜론(:) 기호로 결합하여 표현할 수 있습니다.&lt;/li&gt;
&lt;li&gt;문자 값을 저장할 때는 인용부호(&amp;rdquo;&amp;rdquo;)를 사용하며 숫자 값을 저장할 때는 인용부호가 필요하지 않습니다.&lt;/li&gt;
&lt;li&gt;필드 개수는 제한이 없습니다.&lt;/li&gt;
&lt;li&gt;Hash 타입의 데이터를 처리할 때는 hmset, hget, hgetall, hkey, hlen 명령어가 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;List : 기존의 관계형 테이블에는 존재하지 않는 데이터 유형이며, 일반적인 프로그래밍 언어에서 배열과 유사한 데이터 구조입니다. 단순히 삽입 순서로 정렬된 String List 이며, String 타입이 배열 구조로 구성됐다고 할 수 있다. List 타입의 데이터를 처리할 때는 lpush, lrange, rpush, rpop, llen, lindex 명령어가 있습니다.&lt;/li&gt;
&lt;li&gt;Set : List 타입은 하나의 필드에 여러 개의 배열값을 저장할 수 있는 데이터 구조라면 Set 타입은 배열 구조가 아닌 여러 개의 엘리먼트로 데이터 값을 표현하는 구조입니다. 정렬되지 않은 String collection 이며, List형에서 인덱스가 없어진 형입니다. Value에 중복이 없으며 Lists 보다 빠르게 처리가 가능합니다. Set타입의 데이터를 처리할 때는 sadd, smembers, scard, sdiff, sunion 명령어를 사용합니다.&lt;/li&gt;
&lt;li&gt;Sorted Set : Set타입과 동일한 데이터 구조지만 저장된 데이터 값이 정렬된 상태입니다. Set 타입에서 스코어라는 개념이 붙은 타입으로 스코어 순서로 정렬하거나 스코어에서 임계값 처리가 가능합니다. 데이터를 처리할 때는 zadd, zrange, zcard, zcount, zrank, zrevrank 명령어를 사용합니다.&lt;/li&gt;
&lt;li&gt;Bit : 컴퓨터가 이해하는 0과 1로 표현되는 Bit값으로 가장 빠르게 해석되며 표현하는 구조입니다. 데이터를 처리할 때 setbit, getbit, bitcount 명령어를 사용합니다.&lt;/li&gt;
&lt;li&gt;Geo :&amp;nbsp; 위치정보(경도,위도) 데이터를 효율적으로 저장, 관리 할 수 있으며 이를 활용한 위치 정보 데이터의 분석 및 검색에 사용할 수 있습니다. 데이터를 처리할 때 geoadd, geopos, geodist, georadius, geohash 명령어를 사용합니다.&lt;/li&gt;
&lt;li&gt;HyperLogLogs :&amp;nbsp; RDBMS 에서 Check 제약조건과 유사한 개념의 데이터 구조로 특정 필드 또는 엘리멘트에 저장되어야 할 데이터 값을 미리 생성하여 저장한 후 필요에 따라 연결하여 사용할 수 있는 데이터 타입입니다. 데이터를 처리할 때 pfadd, pfcount, pfmerge 명령어를 사용합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Redis 확장 모듈&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초에 Redis는 Community Edition으로 개발되면서 지속적인 기능 추가 및 서버 확장에 한계를 느껴 사용자들이 이를 개선하고자 Redis 소스를 이용해 다양한 기능들을 배포하게 되었고 이를 확장 모듈이라고 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REJSON : JSON 타입의 데이터를 처리할 수 있는 모듈&lt;/li&gt;
&lt;li&gt;RedisSQL : SQLite(RDBMS)로 데이터를 저장할 수 있는 모듈&lt;/li&gt;
&lt;li&gt;RediSearch : Redis에 저장된 데이터에 대한 검색엔진을 사용할 수 있는 모듈&lt;/li&gt;
&lt;li&gt;Redis-ML : 머신러닝을 사용할 수 있는 모듈&lt;/li&gt;
&lt;li&gt;Redis-sPiped : 전송되는 데이터를 암호화 할 수 있는 모듈&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lua Function &amp;amp; Script&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lua 는 가볍고 내장 가능한 Script Programming Language 중 하나이며 절차형, 객체지향 , 데이터베이스 기반 프로그래밍을 수행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;간단한 프로시저와 배열로 결합할 수 있으며 동적으로 코딩가능 하고 가상머신 기반에 바이트 코드를 해석하여 실행할 수 있고 증분 가비지 컬렉션으로 메모리를 자동으로 관리합니다.&lt;/li&gt;
&lt;li&gt;Lua는 브라질 리오 데 자네이로 교황청 카톨릭 대학교 PUC-Rio 팀에서 개발 및 유지 관리되고 있습니다.&lt;/li&gt;
&lt;li&gt;Redis Server는 내장된 Lua Interpreter를 통해 미리 작성된 Lua Script 또는 Function을 실행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;다양한 Lua Function을 통해 데이터를 검색, 수정, 삭제, 입력을 할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Study/Redis</category>
      <category>Redis</category>
      <category>레디스</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/107</guid>
      <comments>https://goodthinking.tistory.com/107#entry107comment</comments>
      <pubDate>Sat, 19 Aug 2023 18:29:55 +0900</pubDate>
    </item>
    <item>
      <title>가상 면접 사례로 배우는 대규모 시스템 설계 기초 - 1. 사용자 수에 따른 규모 확장성</title>
      <link>https://goodthinking.tistory.com/106</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;단일 서버&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자는 도메인으로 웹사이트에 접속한다. 이 때 DNS에 질의 한다.&lt;/li&gt;
&lt;li&gt;DNS 조회 결과로 IP 주소가 반환된다.&lt;/li&gt;
&lt;li&gt;해당 IP주소로 HTTP 요청이 전달된다.&lt;/li&gt;
&lt;li&gt;요청을 받은 웹 서버에서 HTML, JSON형태의 응답등을 반환한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터베이스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 늘면 서버를 여러개 두어야 한다. 하나는 요청 트래픽 처리용, 하나는 데이터베이스용이다. 각각을 분리하면 독립적으로 확장해 나갈 수 있다. 데이터베이스는 용도에 따라 선택해야 한다. 다음과 같은 경우 NoSQL 을 고려하자&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아주 낮은 응답 지연시간(latency) 요구&lt;/li&gt;
&lt;li&gt;데이터가 비정형(unstructured) 인 경우&lt;/li&gt;
&lt;li&gt;데이터(JSON, YAML, XML등)를 직렬화(serialize) 하거나 역직렬화(deserialize) 할 수 있기만 하면 됨&lt;/li&gt;
&lt;li&gt;아주 많은 양의 데이터를 저장할 필요가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수직적 규모 확장 (scale up) vs 수평적 규모 확장 (scale out)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수직적 규모 확장&lt;/li&gt;
&lt;li&gt;스케일 업이라고도 하는 수직적 규모 확장(vertical scaling) 프로세스는 서버에 고사양 자원(고성능 CPU, RAM 추가 등)을 추가하는 행위를 의미하며, 서버로 유입되는 트래픽의 양이 적을 때는 스케일업이 좋은 선택이며 단순함이 큰 장점이다. 하지만 단점으로 확장의 한계가 있고, 장애에 대한 자동복구(failover) 방안이나 다중화(redundancy) 방안을 제시하지 않으므로 결국 장애가 발생하면 중단된다.&lt;/li&gt;
&lt;li&gt;수평적 규모 확장&lt;/li&gt;
&lt;li&gt;스케일 아웃이라고도 하는 수평적 규모 확장 프로세스는 더 많은 서버를 추가하여 성능을 개선하는 행위를 의미한다. 대규모 애플리케이션을 지원하는 데 적절하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;부하 분산 집합(load balancing set)에 속한 웹 서버들에게 트래픽이 고르게 분산하는 역할이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서로 부하 분산&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 사용자는 DNS에서 로드밸런서의 공개 IP주소(public IP) 로 접속한다. 그리고 내부 서버 간 통신에는 같은 네트워크에 속한 서버 사이의 통신에만 쓰이는 주소인 사설 IP주소(private IP)를 이용한다. 로드밸런서는 장애를 자동복구하는 (failover) 전략을 사용할 수 있으며 웹 계층의 가용성(availability)이 향상된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 1이 다운되는 경우 트래픽은 서버2로 전송되며 전체 서버가 다운되는 경우를 방지하고 부하를 나누기 위해 새로운 서버를 추가할 수 있다.&lt;/li&gt;
&lt;li&gt;트래픽이 급격히 증가하면 서버를 더 추가해 로드밸런서가 부하를 분산한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 서버의 부하는 분산되었는데, 문제는 데이터베이스가 한대만 있을 경우이다. 이럴 때 데이터베이스의 다중화를 사용할 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터베이스 다중화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 사이에 데이터베이스를 주(Master) - 부(slave) 로 나누어 데이터 원본은 마스터 서버에 보관하며 쓰기 연산은 마스터에서만 가능하고, 사본은 슬레이브 서버에 저장하며 읽기 연산만을 지원하여 마스터 데이터베이스가 슬레이브 데이터베이스에게 사본을 전달한다. 보통 애플리케이션은 쓰기 연산보다 읽기 연산의 비중이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 베이스 다중화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중화로 얻는 이득은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 다은 성능 : 주 - 부 다중화 모델에서 데이터의 변경은 주 데이터베이스에서만 처리되고, 읽기 연산은 부 데이터베이스로 전달하여 분산하면서 병렬로 처리할 수 있는 query의 수가 늘어단다.&lt;/li&gt;
&lt;li&gt;안정성 (reliability) : 데이터베이스 서버의 일부 장애가 발생해도 데이터는 보존할 수 있다.&lt;/li&gt;
&lt;li&gt;가용성 (availability) : 데이터를 여러 지역에 복제해 둠으로써 데이터베이스 서버에 장애가 발생해도 다른 서버에 데이터를 가져와 계속 서비스할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 데이터베이스 서버 하나가 다운된다면 무슨 일이 벌어질까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부 서버가 한 대 뿐인데 다운된 경우, 읽기 연산은 마스터 서버가 처리할 것이며, 즉시 새로운 슬레이브 서버가 장애 서버를 대체할 것이다. 슬레이브 서버가 여러 대인 경우 읽기 연산은 나머지 슬레이브 서버들로 분산되며 대체한다.&lt;/li&gt;
&lt;li&gt;주 데이터베이스가 다운됐을 때, 한 대의 슬레이브 데이터베이스만 있는 경우 해당 슬레이브 서버가 마스터 서버가 되며 모든 데이터베이스 연산은 일시적으로 마스터 서버에서 수행되며, 새로운 슬레이브 서버가 추가될 것 입니다. 하지만 프로덕션에서는 더 복잡한게, 마스터 - 슬레이브 서버간 데이터가 최신 상태가 아닐 수 있어서 복구 스크립트를 통해 추가해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서, 서버 다중화, 데이터베이스 다중화가 적용된 모습&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 설계안은 다음과 같이 동작합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자는 DNS에서 로드밸런서의 공개 IP주소를 받습니다.&lt;/li&gt;
&lt;li&gt;해당 IP주소로 로드밸런서에 접속합니다.&lt;/li&gt;
&lt;li&gt;로드밸런서는 부하 분산으로 적절히 서버에 전달합니다.&lt;/li&gt;
&lt;li&gt;웹 서버는 사용자의 데이터를 부 데이터베이스에서 읽습니다.&lt;/li&gt;
&lt;li&gt;웹 서버는 데이터 변경 연산을 주 데이터베이스에 전달합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 적절히 트래픽관리가 가능한 설계가 되었으나, 응답시간(latency)을 개선해야 합니다. 응답 시간은 캐시(cache)를 붙이고 정적 컨텐츠 전송 네트워크(CDN)로 옮기면 개선할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캐시 cache&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값비싼 연산 결과 나 자주 참조되는 데이터를 메모리에 두고 동일한 요청을 보다 빨리 처리할 수 있도록 하는 저장소입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 계층(cache tier) 는 데이터가 잠시 보관되는 곳으로 데이터베이스보다 훨씬 빠르며, 별도의 캐시 계층을 두면 성능이 개선될 뿐 아니라 데이터베이스의 부하를 줄일 수 있고 캐시 계층의 독립적으로 확장시키는 것도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 주도형 캐시 전략(read-through caching strategy)이라고 부르며, 이 외에도 캐시 종류, 크기, 패턴에 맞는 다양한 캐시 전략을 선택할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;캐시 사용 시 유의할 점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시는 데이터 갱신은 적지만 참조가 빈번하게 일어난다면 고려해볼 수 있다.&lt;/li&gt;
&lt;li&gt;캐시는 데이터를 휘발성 메모리에 두므로 영속적으로 보관할 데이터를 보관하는 것은 바람직하지 않습니다.&lt;/li&gt;
&lt;li&gt;캐시에 보관된 데이터의 만료(expire) 정책을 마련해 두어야 한다. 적절한 만료 기한을 두고 데이터를 유지시켜야 합니다.&lt;/li&gt;
&lt;li&gt;일관성(consistency)의 유지는 어떻게 할지 고민해야 합니다. 데이터베이스의 원본과 캐시내 데이터 사본이 같은지 여부를 확인해야 하며, 데이터베이스의 연산과 캐시를 갱신하는 연산이 단일 트랜잭션으로 처리되지 않는 경우 일광성이 깨질 수 있습니다.&lt;/li&gt;
&lt;li&gt;장애 대처 방안에 대해 고민해야 합니다 캐시 서버를 한 대만 두는 경우 해당 서버는 단일 장애 지점(Single point of Failure, SPOF)이 될 가능성이 있습니다. ** 단일 장애지점은 특정 지점에서 장애가 전체 시스템의 동작을 중단하는 경우를 의미합니다 **&lt;/li&gt;
&lt;li&gt;캐시 메모리의 할당을 적절히 조절해야 합니다. 너무 적다면 데이터가 캐시에서 밀려나서 성능이 떨어질 수 있으며, 이를 방지하기 위해 과할당(over provision) 하는 경우도 있습니다.&lt;/li&gt;
&lt;li&gt;데이터 방출(eviction) 정책은 캐시가 꽉 찼을 때 추가로 데이터가 캐시로 들어오는 경우 기존 데이터를 내보내야 할 때 정책을 의미하며, 보통 LRU(Least Recently Used, 마지막 사용 시점이 오래된 데이터를 내보내는 정책)를 많이 사용하며 다른 정책으로는 LFU(Least Frequently Used, 사용 빈도가 낮은 데이터를 내보내는 정책)이나, FIFO(First In First Out, 가장 먼저 캐시에 들어온 데이터 순으로 내보내는 정책)도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;콘텐츠 전송 네트워크(CDN)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 컨텐츠를 전송하는 데 쓰이며, 지리적으로 분산된 서버의 네트워크입니다. 이미지, 비디오, CSS, JS 파일등을 캐싱할 수 있습니다. 간략하게 request path, query string, cookie, header등의 정보에 기반하여 HTML 페이지를 캐시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 페이지를 요청하면 가장 가까운 CDN 서버가 정적 컨텐츠를 전달하게 되며 가까울수록 빠르고 멀수록 느려집니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자A가 이미지 URL을 이용해 image.png 에 접근할 경우 URL의 도메인은 CDN 서비스에서 제공한 것입니다.&lt;/li&gt;
&lt;li&gt;CDN 서버에 해당 이미지가 없는 경우 서버는 원본 서버에 요청해 파일을 가져오게 됩니다.&lt;/li&gt;
&lt;li&gt;원본 서버가 파일을 CDN에 반환하고, HTTP 헤더에 해당 파일이 얼마나 오래 캐시될 수 있는지 설명하는 TTL(Time-To-Live)값이 들어 있습니다.&lt;/li&gt;
&lt;li&gt;CDN 서버는 파일을 캐시하고 사용자에게 반환하며 이미지는 TTL에 명시된 시간까지 캐시됩니다.&lt;/li&gt;
&lt;li&gt;사용자 B가 같은 이미지에 대해 요청하면 만료되지 않은 경우 캐시를 통해 처리합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CDN 사용 시 고려사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비용 : CDN은 보통 제3 사업자에 의해 운영되며 CDN에서 통신되는 데이터의 양에 따라 요금을 책정합니다. 자주 사용되지 않는 콘텐츠를 캐싱하는 것은 이득이 크지 않으므로 적절한 사용 콘텐츠만 캐싱합니다.&lt;/li&gt;
&lt;li&gt;만료 시한: 시의성이 중요한(time-sensitive) 컨텐츠의 경우 만료 시점을 잘 정해야 합니다.&lt;/li&gt;
&lt;li&gt;CDN 장애 대처 방안: CDN 서버에 문제가 생겼을 경우 대처 방안을 마련해야 합니다. 예로 CDN 서버가 응답하지 않는 경우 원본 서버로부터 컨텐츠를 가져오도록 하는 등의 방안이 필요합니다.&lt;/li&gt;
&lt;li&gt;컨텐츠 무효화(invalidation) 방법: 아직 만료되지 않았어도 CDN에서 제거할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CDN 서비스의 API 사용&lt;/li&gt;
&lt;li&gt;다른 버전을 서비스하도록 오브젝트 비저닝(object versioning) 이용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;캐시와 CDN이 추가된 설계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 컨텐츠는 더 이상 웹서버를 통해 제공하지 않고 CDN을 통해 성능을 높이며, 캐시가 데이터 베이스의 부하를 줄여줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;무상태 (Stateless) 웹 계층&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 계층을 수평적으로 확장하기 위해선 상태 정보를 데이터베이스에서 필요할 때 가져오는 것이 바람직한 전략입니다. 이런 구성 상태를 무상태 웹 계층이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상태 정보 의존적인 아키텍처&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 정보를 보관하는 서버는 클라이언트 정보, 즉 상태를 유지하여 요청들 사이에 공유되도록 합니다. 무상태 서버에는 이런 장치가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 그림을 보면 사용자A의 요청은 서버 1에 저장되며 사용자A를 인증하기 위해서 요청을 반드시 서버 1로만 전송해야 합니다. 다른 서버로 요청될 경우 사용자A의 정보가 없으므로 인증에 실패합니다. 문제는 같은 클라이언트로부터의 요청은 항상 같은 서버로 전달되야 한다는 것 입니다. 대부분 로드밸런서가 이 기능을 위해 &lt;b&gt;고정 세션(sticky session)&lt;/b&gt; 이라는 기능을 통해 지원하지만, 로드밸런서에 부담을 주며 서버를 추가하거나 제거하기 까다로워 지고 서버의 장애처리 시 복잡도가 증가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;무상태 아키텍처&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버는 상태 정보를 데이터베이스에서 가져오며 물리적으로 웹서버로 부터 분리되어 있어 단순하고 안정적으고 규모 확장이 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 세션 데이터를 웹 계층에서 분리하고 지속성 데이터 보관소에 저장하도록 설계했으며, 이 저장소는 캐시 시스템이거나 RDB, NoSQL등 다양한 형태일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 센터&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은 두 개의 데이터 센터를 이용하는 사례로, 장애가 없는 상황에서 사용자는 가장 가까운 데이터 센터로 안내되는데, 통상 이 절차를 지리적 라우팅(geoDEN-routing, geo-routing)이라고 부르며, 도메인을 어떤 IP주소로 변활할지 결정할 수 있는 서비스입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 둘중 하나에 센터에 장애가 발생하면 모든 트래픽은 나머지 데이터 센터로 전송됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 다중 데이터센터 아키텍처를 위해서는 몇가지 난제를 해결해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트래픽 우회 : 올바른 데이터 센터로 트래픽을 보내는 효과적인 방법을 찾아야 합니다. geoDNS는 사용자에게서 가장 가까운 데이터 센터로 트래픽을 보낼 수 있도록 해 줍니다.&lt;/li&gt;
&lt;li&gt;데이터 동기화(synchronization) : 데이터 센터마다 별도의 데이터베이스를 사용하고 있는 상황이라면, 장애가 자동으로 복구되어(failover) 트래픽이 다른 데이터베이스로 우회된다 해도, 데이터가 없을 수 있습니다. 이를 막기 위해 보편적으로 여러 데이터센터에 데이터를 다중화해두는 것 입니다.&lt;/li&gt;
&lt;li&gt;테스트와 배포(deployment) : 여러 데이터 센터를 사용하도록 시스템이 구성됐다면 여러 위치에서 요청을 보내 테스트해보는 것이 중요합니다. 물론 자동화된 배포 도구는 동일한 서비스가 설치되도록 하는 중요한 역할입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;메시지 큐(message queue)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템을 더 확장하기 위해서는 시스템의 컴포넌트를 분리하여 각 독립적으로 확장될 수 있도록 해야하며, 메시지 큐는 분산 시스템들이 해당 문제를 풀기 위해 채용하는 핵심적 전략입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지의 무손실(durability, 메시지 큐에 보관된 데이터는 소비자가 꺼낼 때까지 보관된다는 특성)을 보장하는, 비동기 통신(asynchronous communication)을 지원하는 컴포넌트입니다. 메시지의 버퍼 역할을 하며, 비동기적으로 전송합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발행자&lt;/b&gt;(producer/publisher)는 메시지를 만들어 메시지 큐에 발행(publish)하며, 큐에는 &lt;b&gt;소비자&lt;/b&gt;(consumer/subscriber)라 불리는 서비스 혹은 서버가 연결되어 메시지를 받아 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐를 이용하면 서비스 간 결합이 느슨해져서 규모 확장성이 보장되야 하는 안정적 애플리케이션을 구성하기 좋고, 생산자는 소비자 프로세스가 다운되어도 메시지를 발행할 수 있으며, 소비자는 생산자 서비스가 가용 상태가 아니어도 메시지 수신이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로그, 메트릭, 자동화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그 : 에러 로그를 모니터링하는 것은 중요하며, 시스템의 오류와 문제들을 보다 쉽게 찾아낼 수 있고, 에러 로그는 서버 단위로 모니터링 할 수도 있지만, 로그를 단일 서비스로 모아주는 도구를 활용하면 편리하게 관리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;메트릭 : 메트릭을 잘 수집하면 사업 현황에 관한 유용한 정보를 얻을 수도 있고, 시스템의 현재 상태를 손쉽게 파악할 수도 있다. 메트릭 가운데 특히 유용한 것을 몇 가지 살펴보면 다음과 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트 단위 메트릭 : CPU, 메모리, 디스크IO에 관한 메트릭&lt;/li&gt;
&lt;li&gt;종합(aggregated) 메트릭 : 데이터베이스 계층의 성능, 캐시 계층의 성능같은 것&lt;/li&gt;
&lt;li&gt;핵심 비즈니스 메트릭 : 일별 능동 사용자(daily active user), 수익(revenue), 재방문(retention)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자동화 : 시스템이 크고 복잡해지면 생산성을 높이기 위해 자동화 도구를 활용해야 합니다. 지속적 통합(continuous integration) 을 도와주는 도구를 개발자가 만드는 코드에 테스트를 자동으로 할 수 있어 문제 감지가 쉽다. 외에도 빌드, 테스트, 배포 등의 절차를 자동화할 수 있어 개발 생산성을 크게 향상시킬 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐, 로그, 메트릭, 자동화등을 반영한 설계&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;메시지 큐는 각 컴포넌트가 느슨히 결합(loosely coupled)될 수 있도록 하고, 결함에 대한 내성을 높인다.&lt;/li&gt;
&lt;li&gt;로그, 모니터링, 메트릭, 자동화 등을 지원하기 위한 장치를 추가&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케일업이라고도 불리는 수직적 규모 확장은 기존 서버에 많은, 혹은 고성능의 자원을 증설하는 방법입니다. 하지만 단점으로 하드웨어에 한계로 무한 증설이 불가능하며, 단일 지점 장애, SPOF(Single Point of Failure) 로 인한 위험성이 크고, 비용이 많이 든다.&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;데이터베이스의 수평적 확장은 샤딩(sharding)이라고 하는데, 더 많은 서버를 추가함으로써 성능을 개선합니다. 샤딩은 대규모 데이터베이스를 샤드(shard)라고 부르는 작은 단위로 분할하는 기술을 일컫으며, 모든 샤드는 같은 스키마를 쓰지만 샤드에 보관되는 데이터 사이에는 중복이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샤드로 분할된 데이터베이스에 분할 전략을 구현할 때 고려할 가장 중요한 부분은 샤딩 키(sharding key)를 어떻게 정하느냐 입니다. 샤딩 키는 파티션 키(partition key)라고도 부르는데, 데이터가 어떻게 분산될지 정하는 하나 이상의 컬럼으로 구성됩니다. 샤딩은 데이터베이스 규모 확장을 실현하는 훌룡한 기술이지만 문제들도 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터의 재 샤딩(resharding) : 재 샤딩은 1. 데이터가 너무 많아져서 하나의 샤드로는 더 이상 감당하기 어렵거나, 2. 샤드간 데이터 분포가 균등하지 못해 다른 샤드에 비해 공간 소모가 빠른 경우 샤드 소진(shard exhaustion) 이라고 부르는 현상이 발생하면 샤드 키를 구하는 함수를 변경하고 재 배치해야합니다.&lt;/li&gt;
&lt;li&gt;유명인사 (celebrity) 문제 : 핫스팟 키(hotspot key) 문제라고도 하며, 특정 샤드에 질의가 집중되어 서버에 과부하가 걸리는 경우를 의미합니다. 이 문제를 풀기 위해선 샤드 하나씩을 할당해야 할 수도 있고, 더 잘게 쪼개야 할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;조인과 비정규화 (join and de-nomalization) : 하나의 데이터베이스를 여러 샤드 서버로 쪼개면 여러 샤드에 걸친 데이터를 조인하기 힘들어지게 됩니다. 이를 위해 데이터베이스를 비정규화하여 하나의 테이블에서 질의가 수행되도록 하는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 샤딩을 적용한 설계&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;백만 사용자, 그 이상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템의 규모를 확장하는 것은 지속적이고 반복적(iterative)인 과정입니다. 하지만 수백만 사용자 이상을 지원하려면 새로운 전략을 지원해야 합니다. 예로 시스템을 더욱 최적화하고 더 작은 단위의 서비스로 분할하는 경우도 있습니다. 다양한 기법을들 다시 정리하자면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 계층은 무상태 계층으로&lt;/li&gt;
&lt;li&gt;모든 계층에 다중화 도입&lt;/li&gt;
&lt;li&gt;가능한 많은 데이터를 캐시&lt;/li&gt;
&lt;li&gt;여러 데이터 센터를 지원&lt;/li&gt;
&lt;li&gt;정적 컨텐츠는 CDN을 통해 서비스&lt;/li&gt;
&lt;li&gt;데이터 계층은 샤딩을 통해 규모를 확장&lt;/li&gt;
&lt;li&gt;각 계층은 독립적 서비스로 분할&lt;/li&gt;
&lt;li&gt;시스템을 지속적으로 모니터링하고 자동화 도구들을 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-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>Study/가상 면접 사례로 배우는 대규모 시스템 설계 기초</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/106</guid>
      <comments>https://goodthinking.tistory.com/106#entry106comment</comments>
      <pubDate>Sat, 19 Aug 2023 00:51:22 +0900</pubDate>
    </item>
    <item>
      <title>주니어 개발자를 위한 TPO for TDD 세미나 후기</title>
      <link>https://goodthinking.tistory.com/105</link>
      <description>&lt;h1&gt;개요&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TPO for TDD 세미나를 듣고와서 내용을 정리하고, 느낀점을 포스팅해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 TPO란 Time(시간), Place(장소), Occasion(상황)에 따른 TDD 적용 이라는 의미 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적인 맥락에서 강사님이 말씀하시는 TDD 가 유연하게 필요에 따라 적용되어야 한다는 점과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 강조하시는 테스트의 중요성에 대해서 인상깊게 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;p data-ke-size=&quot;size16&quot;&gt;테스트 주도 개발(이하 TDD)이라는 방법론이 나오기 이전에 개발 방법론은 필요에 따라 진화해 왔다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략적인 진화도를 보자면, &lt;b&gt;구조적&lt;/b&gt; &amp;rarr; &lt;b&gt;정보공학&lt;/b&gt; &amp;rarr; &lt;b&gt;객체지향&lt;/b&gt; &amp;rarr; &lt;b&gt;CBD&lt;/b&gt; &amp;rarr; &lt;b&gt;Agile&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법론과 더불어 프로그램을 다룰 수 있는 운영체제와 환경도 진화해 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 OS &amp;rarr; 하이퍼바이저(가상 OS) &amp;rarr; 컨테이너 &amp;rarr; 오케스트레이션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 아키텍처또한 기본적인 모놀로식 &amp;rarr; 마이크로 서비스 아키텍처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;일반 개발 방식&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 개발을 하는 방식은 폭포수 방식처럼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계획 &amp;rarr; 요구분석 &amp;rarr; &lt;b&gt;[설계 &amp;rarr; 구현 &amp;rarr; 테스트]&lt;/b&gt; &amp;rarr; 운영,유지보수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이루어 지는데, TDD는 &lt;b&gt;[설계 &amp;rarr; 구현 &amp;rarr; 테스트]&lt;/b&gt; 범위의 애플리케이션 실제 개발단계에서의 방법론이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;TDD (Test Driven Development) 란?&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TDD.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;838&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EHoS2/btsrq8EZZW2/PTI2ha9OQF9dRjj3Z4PLR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EHoS2/btsrq8EZZW2/PTI2ha9OQF9dRjj3Z4PLR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EHoS2/btsrq8EZZW2/PTI2ha9OQF9dRjj3Z4PLR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEHoS2%2Fbtsrq8EZZW2%2FPTI2ha9OQF9dRjj3Z4PLR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;321&quot; height=&quot;294&quot; data-filename=&quot;TDD.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;838&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RED(write a failing test)&lt;/b&gt; : 해당 기능이 정상적으로 동작하는지 검증하기 위한 테스트 작성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GREEN(make the test pass)&lt;/b&gt; : 테스트를 통과하는 최소한의 코드 작성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BLUE(refactor)&lt;/b&gt; : 테스트에 성공한 코드를 리팩토링&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 1. 볼링게임&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;볼링 게임은 1개의 프레임&lt;/li&gt;
&lt;li&gt;각프레임은 2롤을 가짐&lt;/li&gt;
&lt;li&gt;spare : 10 + 다음 첫번째 롤에서 쓰러뜨린 핀&lt;/li&gt;
&lt;li&gt;stike : 10 + 다음 두 롤에서 쓰러드린 수&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스를 만들어 아무 동작도 하지 않는 코드 및 실패하는 코드 작성 &amp;rarr;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;0점으로 테스트를 실패하는 (거터게임)테스트 작성&lt;/li&gt;
&lt;li&gt;테스트를 실패함으로써 기본 토대를 만듬&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;테스트를 통과하는 최소한의 코드만 만듬 &amp;rarr;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;전부 1점일 때 통과하는 경우를 만듬&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;구현 설계 개선 &amp;rarr;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;테스트에 있는 중복 제거&lt;/li&gt;
&lt;li&gt;공통 함수를 생성해서 호출을 공통 함수에서 하도록 개선&lt;/li&gt;
&lt;li&gt;&amp;hellip;개선 작업&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이클 반복&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;TDD 특징 : 장,단점&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TDD 특징과 장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;코드 품질 향상&lt;/b&gt;황장이 용이 (변경 시 Side-effect 발견이 쉬움) 고품질의 코드 생산&lt;/li&gt;
&lt;li&gt;반복적인 테스트가 쉬워지므로 유지 관리 가능한 코드를 작성할 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 개발&lt;/b&gt;고도화와 동시에 테스트가 이뤄져서 초기에 수정 작업이 가능&lt;/li&gt;
&lt;li&gt;장기적으로 코드가 복잡해 질수록&lt;/li&gt;
&lt;li&gt;&lt;b&gt;협업에 용이&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;개발 단계에서도 요구 사항에 대한 테스트 코드가 존재&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TDD 단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;초기 투자 시간&lt;/li&gt;
&lt;li&gt;테스트를 작성하기 위한 초기 투자 시간이 필요함&lt;/li&gt;
&lt;li&gt;테스트 코드를 작성하기 어려움&lt;/li&gt;
&lt;li&gt;특정 요구 사항의 경우 테스트 코드를 작성하기 어려움&lt;/li&gt;
&lt;li&gt;느린 개발실패 테스트 작성 &amp;rarr; 테스트 &amp;rarr; 성공 테스트 작성 &amp;rarr; 테스트 &amp;hellip; 생산성 낮아짐&lt;/li&gt;
&lt;li&gt;1번 작성할 코드를 여러번에 나눠 작성하게 됨&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트 코드 작성 시 사용할 수 있는 객체 의존성 - 테스트 더블(Test Doubles)&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stub&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 중인 클래스에 데이터를 제공&lt;/li&gt;
&lt;li&gt;상태 확인에 사용&lt;/li&gt;
&lt;li&gt;테스트에 실패할 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mock&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 중인 클래스에 의해 호출&lt;/li&gt;
&lt;li&gt;동작 확인에 사용&lt;/li&gt;
&lt;li&gt;테스트에 실패할 수 있음&lt;/li&gt;
&lt;li&gt;데이터 + 구현을 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;hellip; 외에도 Fakes, spy 등이 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;TDD 에 관한 편견&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD는 무조건 해야하는게 아님, 초기 비용이 많으 들지만 전체적으로 비용이 점진적으로 낮아짐&lt;/li&gt;
&lt;li&gt;TDD가 버그를 없애지는 않음, 버그를 효과적으로 개선할 수 있도록 할 수 있으나 버그 자체가 없어지진 않음&lt;/li&gt;
&lt;li&gt;TDD를 도입하면 업무속도가 느려지진 않음, 초기 자원이 미진할 땐 속도가 나지 않는다고 느끼지만, 익숙해지면 점진적으로 빨라짐&lt;/li&gt;
&lt;li&gt;TDD자체가 목적이 되면 안됨, 공동의 목표를 효율적으로 달성하기 위한 도구일뿐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법론이나 기술의 선택의 폭을 넓히기 위함, 기술 자체가 목표가 아님&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;BDD&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Behavior Driven Development&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD를 근간으로 파생된 개발 프로세스&lt;/li&gt;
&lt;li&gt;TDD가 테스트 자체에 집중된 반면&lt;/li&gt;
&lt;li&gt;BDD는 &lt;b&gt;비즈니스 요구사항에 집중&lt;/b&gt;하여 테스트 케이스 개발&lt;/li&gt;
&lt;li&gt;테스트 케이스 자체가 요구 사양이 되도록 개발하는 방식&lt;/li&gt;
&lt;li&gt;TDD와 결합해 &lt;b&gt;시나리오 테스트&lt;/b&gt;까지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;DDD&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Domain Driven development&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 주도 개발&lt;/li&gt;
&lt;li&gt;순수한 도메인의 모델과 로직에 집중&lt;/li&gt;
&lt;li&gt;분석 작업과 설계, 구현까지 통일된 방식&lt;/li&gt;
&lt;li&gt;도메인 모델부터 코드까지&lt;/li&gt;
&lt;li&gt;함께 움직이는 구조의 모델을 지향&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;성공 사례 - 레거시&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단위 테스트 없이 예전에 작성된 함수를 사용하는 새 기능 개발&lt;/li&gt;
&lt;li&gt;이전 코드에 대한 단위 테스트 작성 전까지 배포할 수 없음&lt;/li&gt;
&lt;li&gt;레거시 코드를 블랙박스로 취급&lt;/li&gt;
&lt;li&gt;모든 코드는 컴포지션이다?&lt;/li&gt;
&lt;li&gt;기존 코드에 대한 유닛 테스트를 작성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기존 코드에 대한 그린 상태를 유지하면서 리팩토링&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의존성 테스트에는 최대한 Mock 을 사용&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 급증하며 보그가 발견되어 핫픽스를 제공&lt;/li&gt;
&lt;li&gt;변경 사항이 적용되고 고객 지원 통화량이 두배가 됨&lt;/li&gt;
&lt;li&gt;핫픽스가 기존의 코드를 깨뜨림&lt;/li&gt;
&lt;li&gt;머피의 디버깅 법칙?&lt;/li&gt;
&lt;li&gt;단위 테스트 어설션(asertion)을 재검토&lt;/li&gt;
&lt;li&gt;코드가 문서화된 대로 작당한다는 증거 == 테스트 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;TDD를 적용하기 좋은 경우&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;처음 해보는 프로그램 주제&lt;/li&gt;
&lt;li&gt;고객의 요구 사항이 바뀔 수 있는 프로젝트 (외부적인 불확실성이 높음)&lt;/li&gt;
&lt;li&gt;개발하는 중에 코드를 많이 바꿔야한다고 생각하는 경우&lt;/li&gt;
&lt;li&gt;내가 개발하고 이 코드를 누가 유지보수 할 지 모르는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트는 필수로 진행해야 하지만, TDD가 무조건 먼저 적용되어야 하는건 아님&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커버리지 또한 너무 집착하지 않고 비즈니스의 따라, 필요에 따라 사용되어야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적절한 커버리지는 60~80 % 라고 하지만, 실제 현업에서는 지켜지기 힘듬&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;느낀점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강사님께서 재밌게 말을 풀어서 해주시려고 준비하고 노력하신게 보인 세미나였다. TDD가 어떤건지, 어떤 방식으로 진행되는지에 대해 다시 생각해 볼 수 있었고, 예제와 성공사례로 TDD가 비즈니스에 따라 어떻게 적용해야 할지와 무조건 장점만 있는 개발방법론이 아니란점에 대해 다시 생각해볼 수 있었다. 그리고 현업에서 실제로 사용하기 어렵고, 실패하는 이유에 대해 설명하셨을 때는 어느정도 이해는 되었다. TDD를 하면서 비즈니스 코드보다 테스트 코드를 중요시하면 안된다는 점과 커버리지에 집착하지 않고 유연하게 필요에 따라 적용해야 한다고 하셨으며, BDD, DDD등의 다른 개발 방법론도 함께 알아보면 좋겠다고 한다.&lt;/p&gt;</description>
      <category>Living</category>
      <category>TDD</category>
      <category>개발방법론</category>
      <category>세미나</category>
      <category>한빛</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/105</guid>
      <comments>https://goodthinking.tistory.com/105#entry105comment</comments>
      <pubDate>Fri, 18 Aug 2023 00:35:33 +0900</pubDate>
    </item>
    <item>
      <title>Redis Study - 레디스란</title>
      <link>https://goodthinking.tistory.com/104</link>
      <description>&lt;h1 data-react-autofocus=&quot;true&quot;&gt;Redis 스터디&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1690614113670&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;NoSQL &amp;amp; Redis | 주종면 - 교보문고&quot; data-og-description=&quot;NoSQL &amp;amp; Redis | ㆍ 도서 특징 ⑴ Redis의 주요 구조와 기술들을 도형 그림을 통해 체계적인 설명과 원리 위주로 학습합니다. ⑵ 각 장에서 소개하는 기술들에 대해 실습이 가능하도록 구성되어 있습&quot; data-og-host=&quot;product.kyobobook.co.kr&quot; data-og-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001594159&quot; data-og-url=&quot;https://product.kyobobook.co.kr/detail/S000001594159&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJnGLs/hyTtwDC3vy/KjpklLKYTfDeQpKHnUCN11/img.jpg?width=458&amp;amp;height=635&amp;amp;face=0_0_458_635,https://scrap.kakaocdn.net/dn/ckRkdr/hyTttfPRDZ/2k8mst2N4ewwyr842RnNg0/img.jpg?width=458&amp;amp;height=635&amp;amp;face=0_0_458_635,https://scrap.kakaocdn.net/dn/chZQhZ/hyTtjjYsgG/340rgnjef15LTBEYKRrB21/img.png?width=335&amp;amp;height=335&amp;amp;face=0_0_335_335&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001594159&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001594159&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJnGLs/hyTtwDC3vy/KjpklLKYTfDeQpKHnUCN11/img.jpg?width=458&amp;amp;height=635&amp;amp;face=0_0_458_635,https://scrap.kakaocdn.net/dn/ckRkdr/hyTttfPRDZ/2k8mst2N4ewwyr842RnNg0/img.jpg?width=458&amp;amp;height=635&amp;amp;face=0_0_458_635,https://scrap.kakaocdn.net/dn/chZQhZ/hyTtjjYsgG/340rgnjef15LTBEYKRrB21/img.png?width=335&amp;amp;height=335&amp;amp;face=0_0_335_335');&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;NoSQL &amp;amp; Redis | 주종면 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;NoSQL &amp;amp; Redis | ㆍ 도서 특징 ⑴ Redis의 주요 구조와 기술들을 도형 그림을 통해 체계적인 설명과 원리 위주로 학습합니다. ⑵ 각 장에서 소개하는 기술들에 대해 실습이 가능하도록 구성되어 있습&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;product.kyobobook.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-react-autofocus=&quot;true&quot; data-ke-size=&quot;size16&quot;&gt;해당 책을 바탕으로 매주 1챕터씩 진행하는 스터디를 하기로 했습니다.&lt;/p&gt;
&lt;p data-react-autofocus=&quot;true&quot; data-ke-size=&quot;size16&quot;&gt;Redis를 위 책과 함께 공식 문서를 바탕으로 공부하면서 정리한 내용을 포스팅 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-react-autofocus=&quot;true&quot;&gt;NoSQL&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Key - Value : 간단한 키와 연결된 값을 저장하는 방식으로 빠른 읽기와 쓰기, 높은 확장성&lt;b&gt;Amazon DynamoDB&lt;/b&gt; : AWS에서 제공하는 키 - 밸류 저장소, 자동 확장 기능을 제공하여 대규모 처리가능, 빠른 속도와 높은 확장성을 갖추고 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Redis&lt;/b&gt; : 대표적인 key - value 형태의 In-memory 데이터 구조 저장소, 데이터를 메모리에 저장하므로 높은 속도를 제공하지만 영속성에 대한 설정이 가능, 세션 캐싱, 메시지 브로커등 다양한 활용이 가능&lt;/li&gt;
&lt;li&gt;Column - Family : 컬럼 패밀리라는 개념을 사용, 여러 개의 열 패밀로 구성되며 각 열 패밀리는 다른 컬럼들을 포함 &lt;b&gt;Cassandra&lt;/b&gt; : 분산형 컬럼 패밀리 데이터베이스로 대규모 데이터 처리에 특화, 확장성이 높고 고가용성을 제공, 다양한 데이터 센터에 걸쳐 데이터를 분산 저장가능&lt;/li&gt;
&lt;li&gt;Document : JSON, BSON 과 같은 문서 형식의 데이터 구조, 비정형 데이터로 자유로운 저장과 중첩 구조&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MongoDB&lt;/b&gt; : 문서 데이터베이스로 JSON 형식의 문서를 저장하고 다양한 쿼리를 지원, 확장성이 뛰어나고 높은 고가용성을 제공, 주로 웹 애플리케이션, 로그처리, 실시간 분석등에 사용&lt;/li&gt;
&lt;li&gt;Graph : 데이터 간의 관계를 그래프 형식으로 저장하고 처리, 복잡한 관계를 효율적으로 표현하고 쿼리가능 &lt;b&gt;Neo4j&lt;/b&gt; : 그래프 데이터베이스로 복잡한 관계를 저장하고 관련 쿼리를 효율적으로 수행, 사회 네트워크 분석, 추천 시스템, 지리 정보 시스템등에 적합&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;Key - Value 데이터 구조&lt;/h1&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;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Redis 는 In-memory 기반의 데이터 저장 구조 : 기존의 RDBMS에서 사용하는 인-메모리 솔루션은 파일 기반의 데이터 저장 구조로 먼저 메모리에 저장되었다가 나중에 디스크에 입력되는 구조지만, 순수한 인-메모리 구조인 Redis같은 DB는 모든 데이터를 메모리에 저장하고 필요에 따라 설정을 통해 선택적으로 디스크에 저장합니다. 만약 추가 설정을 하지 않는다면 메모리 상에만 존재하며 장애가 발생한다면 데이터가 유실될 수 있습니다.&lt;/li&gt;
&lt;li&gt;Key - Value 데이터 구조는 하나의 Key와 데이터 값으로 구성 : Redis 에서 데이터 저장 구조를 테이블이라고 표현합니다. 기존 RDBMS 의 저장 구조와 논리적인 표현은 동일하지만 데이터를 표현하는 방법은 다릅니다. 또한 인덱스를 Redis에서도 사용할 수 있지만 종류 및 구조가 많이 다르기 떄문에 표현에는 한계가 있습니다. 그러므로 RDBMS 데이터 구조를 그대로 매핑해서 사용할 수는 없으며 일부를 잠시 보관하거나 가공 처리해서 임시 보관은 가능합니다.&lt;/li&gt;
&lt;li&gt;가공처리가 요구되는 비즈니스 환경 : Redis, Memcached 같은 키-밸류 DB를 사용하는 사용자들은 보통 다양한 DB를 함께 하이브리드로 구축하여 운영하는 경우가 많은데, 대부분의 이유는 메인 DB로 선택한 제품들의 구조가 파일 기반이다 보니 디스크 IO 문제로 인해 발생하는 성능 지연 문제를 해결하기 위한 솔루션으로 키-밸류 DB를 보조로 사용합니다. 즉, 데이터의 가공, 통계 분석, 데이터 캐싱, 메시지 큐등 다양한 용도로 사용할 수 있습니다.&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;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;실시간 분석 : Redis의 장점을 가장 잘 살려서 적용하는 비즈니스 영역은 실시간 가공 처리 및 분석이 요구되는 분야로, 필요한 데이터를 메인 DB에서 데이터를 저장하고 읽은 다음 가공 및 분석 작업은 Redis를 통해 수행해 메모리에 저장하고 그 결과를 사용자에게 제공합니다.&lt;/li&gt;
&lt;li&gt;IOT 영역 : 경량 SW로서 IOT 제품들의 데이터를 저장 및 관리하기에 적합합니다. 라즈베리파이, 아두이노, 안드로이드, ARM 등의 플랫폼이 있습니다.&lt;/li&gt;
&lt;li&gt;계측 정보수집 영역 : 다양한 계측 장비 (온도, 습도, 조도 ,속도, 위치, 맥박/심장 박동수 등)로 부터 수집된 정보를 저장 및 가공합니다.&lt;/li&gt;
&lt;li&gt;개인화 정보관리 영역 : 웹사이트에서 사용자 개인의 특성과 기호에 맞는 페이지 화면을 편집해서 보듯, Redis에서도 저장된 데이터를 사용자의 필요에 따라 쉽게 가공 처리하여 편집이 요구되는 비즈니스 환경에 활용이 가능합니다.&lt;/li&gt;
&lt;li&gt;전자 상거래 비즈니스 영역 : 기존 RDBMS를 기반으로 한 영역에도 동일하게 적용이 가능하지만 과거 시점 데이터를 지속적으로 저장 관리하는 것은 한계가 있고, 반드시 분산-복제(Shard-Replication) 시스템을 구축 운영해야 합니다.&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 영역에서 사용할 수 있지만, 어떤 제품을 선택하는게 올바른지에 대해 알아야합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Simple API 지원 : 대부분 오픈소스 라서 개발 언어들과 간편하게 연동되지 않을 수 있으므로 확인해야 합니다.&lt;/li&gt;
&lt;li&gt;Distributed 제공 : NoSQL은 대규모 데이터 처리를 위해 개발된 만큼 데이터의 분선 저장과 처리가 중요하며 이를 위한 기술로 샤딩(Sharding) 또는 파티셔닝(Partitioning) 같은 기술들의 제공여부와 구현 방법이 개발 환경과 적합한지에 대해 검토해야 합니다.&lt;/li&gt;
&lt;li&gt;Replication 지원 : 서비스가 중단되거나 장애가 발생했을 경우 데이터의 유실을 방지하기 위해 반드시 복제(Replication) 시스템을 구축해야 하며 해당 기술이 제공되는지, 개발 환경과 적합한지 검토해야 합니다.&lt;/li&gt;
&lt;li&gt;Scale-Out 가능 여부 : 시스템의 자원이 부족한 경우 상용 라이센스가 아닌 오픈소스 라이센스로 추가비용 없이 자원을 추가하거나 늘리는 등 다양한 방법으로 유연한 처리가 가능하며, 스케일-아웃이 필요한 경우 NoSQL에서 제공하는 다양한 운영, 관리 매커니즘을 활용할 수 있다는 장점이 있습니다.&lt;/li&gt;
&lt;li&gt;유지보수 비용 : RDBMS에 비해 저렴한 비용으로 유지보수가 가능합니다.&lt;/li&gt;
&lt;li&gt;비정형 데이터 구조 지원과 트랜잭션 제어 여부 : 영상, 사진, 음성, 좌표등 다양한 비정형 데이터들에 대해 처리가 가능하도록 설계되어 편리합니다. 대부분의 NoSQL은 Auto-Commit을 제공하지만 몇몇 제품은 사용자가 직접 트랜잭션 제어가 가능합니다. Redis에 multi 명령어, MongoDB의 Multi-Document Transaction, Cassandra DB의 LWT등입니다.&lt;/li&gt;
&lt;li&gt;오픈소스 여부 : 대부분의 경우 NoSQL은 듀얼 라이센스(Community Edition, Enterprise Edition)정책을 제공하며, 엔터프라이즈 에디션의 경우 초기 구매 비용이 발생하는데, 추가 기능과 유지보수 비용이 포함되어 있습니다. 그리고 해당 제품에 대한 기술지원 업체의 여부도 중요합니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Study/Redis</category>
      <category>NoSQL</category>
      <category>Redis</category>
      <category>레디스</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/104</guid>
      <comments>https://goodthinking.tistory.com/104#entry104comment</comments>
      <pubDate>Sat, 29 Jul 2023 16:04:52 +0900</pubDate>
    </item>
    <item>
      <title>Real MySQL - 엔진 2</title>
      <link>https://goodthinking.tistory.com/103</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;메모리 할당 및 사용 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 메모리 사용 및 할당 구조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 에서 메모리는 크게 두가지로 구분된다. 글로벌 메모리 영역과 로컬 메모리 영역으로 구분되며 글로벌 메모리 영역의 모든 메모리는 MySQL서버가 시작되면서 운영체제로부터 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mysqlMemory.png&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTZA5K/btsoCFxXCJg/vTTKHwgwRLAK99XQkSzM8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTZA5K/btsoCFxXCJg/vTTKHwgwRLAK99XQkSzM8K/img.png&quot; data-alt=&quot;MySQL 메모리 사용 및 할당 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTZA5K/btsoCFxXCJg/vTTKHwgwRLAK99XQkSzM8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTZA5K%2FbtsoCFxXCJg%2FvTTKHwgwRLAK99XQkSzM8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;609&quot; height=&quot;461&quot; data-filename=&quot;mysqlMemory.png&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MySQL 메모리 사용 및 할당 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;글로벌 메모리 영역&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 하나의 메모리 공간만 할당되며 필요에 따라 2개 이상의 메모리 공간이 할당받을 수 있지만 클라이언트의 수와 무관하며 생성된 글로벌 영역은 모든 스레드에 의해 공유된다. 글로벌 메모리 영역에 종류는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테이블 캐시&lt;/li&gt;
&lt;li&gt;InnoDB 버퍼 풀&lt;/li&gt;
&lt;li&gt;InnoDB 어댑티브 해시 인덱스&lt;/li&gt;
&lt;li&gt;InnoDB 리두 로그 버퍼&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로컬 메모리 영역&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 메모리 영역이라고도 표현하며 클라이언트 스레드가 쿼리를 처리하는 데 사용하는 메모리 영역이다. 클라이언트 커넥션으로부터 요청을 처리하기 위해 스레드를 할당하게 되며 이 때 할당되는 메모리 공간이라고 해서 클라이언트 메모리 영역이라고도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 메모리는 각 클라이언트 스레드별로 독립적으로 할당되며 절대 공유되지 않는다는 특징이 있다. 또한 각 쿼리의 용도별로 필요할 때만 공간이 할당되고 필요하지 않은 경우에는 MySQL이 메모리 공간을 할당조차 하지 않을 수 있다는 점이다. 대표적으로 정렬 버퍼와 조인 버퍼가 그러하다. 로컬 메모리 공간은 커넥션이 열려있는 동안 계속 할당된 상태로 남아 있는 공간도 있고 그렇지 않고 실행하는 순간에만 할당했다가 다시 해제하는 공간도 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정렬 버퍼&lt;/li&gt;
&lt;li&gt;조인 버퍼&lt;/li&gt;
&lt;li&gt;바이너리 로그 캐시&lt;/li&gt;
&lt;li&gt;네트워크 버퍼&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;플러그인 스토리지 엔진 모델&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL의 독특한 구조 중 대표적인 것인 플러그인 모델이다. 전문 검색 엔진을 위한 검색어 파서도 플러그인 형태로 개발해서 사용가능하며, 사용자 인증을 위한 Auth관련 플러그인들도 있다. 그리고 MySQL 쿼리가 실행되는 과정을 살펴보면 대부분의 작업이 MySQL엔진에서 처리되고, 마지막 &amp;lsquo;데이터 읽기/쓰기&amp;rsquo; 작업만 스토리지 엔진에 의해 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서 &lt;b&gt;핸들러(Handler)&lt;/b&gt;란 자동차로 비유하자면 사람이 핸들을 이용해 자동차를 운전하듯 프로그래밍 언어에서 어떤 기능을 호출하기 위해 사용하는 운전대와 같은 역할을 하는 객체를 의미하며, MySQL 엔진이 사람역할이고, 각 스토리지 엔진이 자동차 역할을 하는데 여기서 스토리지 엔진을 조정하기 위해 핸들러가 사용된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;하나의 쿼리 작업은 여러 하위 작업으로 나뉘는데, 각 하위 작업이 MySQL 엔진 영역에서 처리되는지 아니면 스토리지 엔진 영역에서 처리되는지 구분할 줄 알아야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;8버전부터는 기존의 플러그인 아키텍처를 대체하기 위해 컴포넌트 아키텍처가 지원된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;플러그인은 오직 MySQL 서버와 인터페이스 할 수 있고, 플러그인끼리는 통신할 수 없음&lt;/li&gt;
&lt;li&gt;플러그인은 MySQL 서버의 변수나 함수를 직접 호출하기 때문에 안전하지 않음(캡슐화가 안됨)&lt;/li&gt;
&lt;li&gt;플러그인은 상호 의존 관계를 설정할 수 없어서 초기화가 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿼리 실행 구조&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;queryArch.png&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZMPQp/btsoyoRgykj/WmOq7LTkwFeb5SLl966km1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZMPQp/btsoyoRgykj/WmOq7LTkwFeb5SLl966km1/img.png&quot; data-alt=&quot;쿼리 실행 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZMPQp/btsoyoRgykj/WmOq7LTkwFeb5SLl966km1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZMPQp%2FbtsoyoRgykj%2FWmOq7LTkwFeb5SLl966km1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;487&quot; height=&quot;319&quot; data-filename=&quot;queryArch.png&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쿼리 실행 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;쿼리 파서 사용자 요청으로 들어온 쿼리 문장을 토큰(MySQL이 인식할 수 있는 최소 단위의 어휘나 기호)으로 분리해 트리 형태의 구조로 만들어 내는 작업을 의미하며 쿼리 문장의 기본 문법 오류는 이과정에서 발견된다.&lt;/li&gt;
&lt;li&gt;전처리기 파서 과정에서 만들어진 트리를 기반으로 쿼리 문장에 구조적 문제점이 있는지 확인하며, 각 토큰을 테이블 이름이나 컬럼 이름, 내장 함수등 개체를 매핑해 해당 객체의 존재 여부와 객체의 접근 권한등을 확인하는 과정을 거친다.&lt;/li&gt;
&lt;li&gt;옵티마이저 옵티마이저가 두뇌라면 실행 엔진과 핸들러는 손과 발에 비유할 수 있다. 실행 엔진이 하는 일을 더 쉽게 이해할 수 있게 간단한 예를 들어보자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 엔진이 핸들러에게 임시 테이블을 만들라고 요청&lt;/li&gt;
&lt;li&gt;다시 실행 엔진은 WHERE 절에 일치하는 레코드를 읽어오라고 핸들러에 전달&lt;/li&gt;
&lt;li&gt;읽어온 레코드들을 1번에서 준비한 임시 테이블로 저장하라고 다시 핸들러에게 요청&lt;/li&gt;
&lt;li&gt;데이터가 준비된 임시 테이블에서 필요한 방식으로 데이터를 읽어 오라고 핸들러에게 다시 요청&lt;/li&gt;
&lt;li&gt;최종적으로 실행 엔진은 결과를 사용자나 다른 모듈로 넘김&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;핸들러(스토리지 엔진) 핸들러는 가장 밑단에서 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 읽어 오는 역할을 담당한다. 핸들러는 결국 스토리지 엔진을 의미하며, MyISAM 테이블을 조작하는 경우에는 핸들러가 MyISAM 스토리지 엔진이 되고 InnoDB 테이블을 조작하는 경우 핸들러가 InnoDB 스토리지 엔진이 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿼리 캐시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 서버에서 쿼리 캐시 (Query Cache)는 SQL의 실행 결과를 메모리에 캐시하고, 동일 SQL 쿼리가 실행되면 테이블을 읽지 않고 즉시 결과를 반환하기 때문에 매우 빠른 성능을 보였다. 하지만 쿼리 캐시는 테이블의 데이터가 변경되면 캐시에 저장된 결과 중에서 변경된 테이블과 관련된 것들은 모두 삭제(Invalidate) 해야 했다. 이는 심각한 동시 처리 성능 저하를 유발한다.&amp;nbsp; 결국 MySQL 8버전에서 쿼리 캐시는 제거되었고, 관련 시스템 변수도 모두 제거됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스레드 풀 (Thread Pool)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 서버에서 스레드 풀은 내부적으로 사용자의 요청을 처리하는 스레드 개수를 줄여서 동시 처리되는 요청이 많다 하더라도 MySQL 서버의 CPU가 제한된 개수의 스레드 처리에만 집중할 수 있게 해서 서버의 자원 소모를 줄이는 것이 목적이다. 제한된 수의 스레드만으로 CPU가 잘 처리하도록 유도한다면 CPU의 프로세스 친화도(Processor affinity)도 높이고 운영체제 입장에서는 불필요한 컨텍스트 스위치를 줄여 오버헤드를 낮출 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Percona Server의 스레드 풀은 기본적으로 CPU 코어의 개수만큼 스레드 그룹을 생성하는데, 스레드 그룹의 개수는 시스템 변수를 변경해서 조정할 수 있다. Percona Server 스레드 풀 플러그인은 선순위 큐와 후순위 큐를 이용해 특정 트랜잭션이나 쿼리를 우선적으로 처리할 수 있는 기능도 제공한다. 이렇게 먼저 시작된 트랜잭션 내 속한 SQL을 빨리 처리해주면 해당 트랜잭션이 가지고 있던 잠금이 빨리 해제되고 잠금 경합을 낮춰서 전체적인 처리 성능을 향상시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;트랜잭션 지원 메타데이터&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타데이터란 테이블 구조 정보와 스토어드 프로그램등 정보를 데이터 딕셔너리 또는 메타데이터라고 한다. 8버전 이후 부터는 메타데이터를 InnoDB의 테이블에 저장하도록 개선됐으며, 트랜잭션 기반의 InnoDB 스토리지 엔진에 저장되도록 개선되면서 이제 스키마 변경 작업 중 비정상적으로 종료되어도 완전한 성공이나 실패로 정리된다. (트랜잭션처리)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Database</category>
      <category>DB</category>
      <category>MySQL</category>
      <category>rdbms</category>
      <category>realmysql</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/103</guid>
      <comments>https://goodthinking.tistory.com/103#entry103comment</comments>
      <pubDate>Sun, 23 Jul 2023 21:36:46 +0900</pubDate>
    </item>
    <item>
      <title>인프런 - 인프콘 다시 노려보기</title>
      <link>https://goodthinking.tistory.com/102</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;이번 인프콘2023은 꼭 가고싶어서 진짜 진짜 가고싶다고 빌었는데,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 추첨에 떨어졌습니다.. 작년에 이어 두 번째 탈락.. 면접,서류 탈락보다 더 상실감이 크네요..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 인프랩에서 이번에 SNS에 올리면 추첨을 통해 다시 한번 기회를 줄 지도?! 몰라서 이렇게 글을 써 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 내용은 이러합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;인프콘 시간표&quot; href=&quot;https://www.inflearn.com/infcon-2023/schedule/share?id=643251&amp;amp;hash=meengi07%406d63dc43&amp;amp;name=Mingi+Kim&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;인프콘행사 시간표&amp;nbsp;&lt;/a&gt; 를 보고 내가 원하는 세션을 담아 나만의 시간표를 만들면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 시간표를 공유하면 25명을 다시 추첨한다고 해요... 잔인한 인프랩...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에라도 됐으면 좋겠습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-19 오후 8.57.39.png&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bADvf4/btsoe1CfKbm/ak3gl1VJMppcOzsnHezN00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bADvf4/btsoe1CfKbm/ak3gl1VJMppcOzsnHezN00/img.png&quot; data-alt=&quot;인프콘2023&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bADvf4/btsoe1CfKbm/ak3gl1VJMppcOzsnHezN00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbADvf4%2Fbtsoe1CfKbm%2Fak3gl1VJMppcOzsnHezN00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;620&quot; data-filename=&quot;스크린샷 2023-07-19 오후 8.57.39.png&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인프콘2023&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming</category>
      <author>긍정왕웹서퍼</author>
      <guid isPermaLink="true">https://goodthinking.tistory.com/102</guid>
      <comments>https://goodthinking.tistory.com/102#entry102comment</comments>
      <pubDate>Wed, 19 Jul 2023 20:58:21 +0900</pubDate>
    </item>
  </channel>
</rss>