<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>공대생 정리노트</title>
    <link>https://woojinger.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 20 Aug 2026 23:05:47 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>woojinger</managingEditor>
    <item>
      <title>[React] What you Need to Know about Complex React Architecture</title>
      <link>https://woojinger.tistory.com/115</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 &lt;a href=&quot;https://boom.co/blogs/complex-react-architecture/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://boom.co/blogs/complex-react-architecture/&lt;/a&gt; 의 일부분을 번역한 글입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://boom.co/blogs/complex-react-architecture/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://boom.co/blogs/complex-react-architecture/&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;일반적인 Modularized React Architecture&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-13 오후 2.32.29.png&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;819&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kY2BW/btrQ3UgVgM2/XYAG4wpnVnfryFH6toHbN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kY2BW/btrQ3UgVgM2/XYAG4wpnVnfryFH6toHbN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kY2BW/btrQ3UgVgM2/XYAG4wpnVnfryFH6toHbN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkY2BW%2FbtrQ3UgVgM2%2FXYAG4wpnVnfryFH6toHbN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;387&quot; height=&quot;751&quot; data-filename=&quot;스크린샷 2022-11-13 오후 2.32.29.png&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;819&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 Modularized 아키텍처는 위와 같은 형태를 띠고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 모듈은 자신의 컴포넌트들을 각자 가지고 있고, 다른 모듈에 영향을 받지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shared folder에서는 모듈들이 공유해서 사용하는 tool들을 정의한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;The clean architecture&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서 제시하는 아키텍처는 FE 레이어를 3개로 나눈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Presentation, Application, Infrastructure 레이어다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QQfcD/btrQZ2AIGYR/f7WbtoR9vy3dkTjM9Xk7y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QQfcD/btrQZ2AIGYR/f7WbtoR9vy3dkTjM9Xk7y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QQfcD/btrQZ2AIGYR/f7WbtoR9vy3dkTjM9Xk7y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQQfcD%2FbtrQZ2AIGYR%2Ff7WbtoR9vy3dkTjM9Xk7y1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;266&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;/figure&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;백엔드의 변경 영향이 Infrastructure Layer에만 한정된다.&lt;/li&gt;
&lt;li&gt;백엔드의 배포가 잦더라도 Application Layer에서 간단히 스위치 해주면 큰 문제가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(1) The Presentation Layer&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저 인터페이스를 보여주고 유저와 상호작용하는 레이어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션 레이어로부터 데이터를 읽어와 뷰를 생성해 유저에게 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SXPuP/btrQ0kBkPFf/GWnFb14u2iCv0Z3mzOPC51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SXPuP/btrQ0kBkPFf/GWnFb14u2iCv0Z3mzOPC51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SXPuP/btrQ0kBkPFf/GWnFb14u2iCv0Z3mzOPC51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSXPuP%2FbtrQ0kBkPFf%2FGWnFb14u2iCv0Z3mzOPC51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;336&quot; height=&quot;521&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;521&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Presentation Layer의 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) pages&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;context에 독립적으로 display될 수 있는 페이지들
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;error page, status page ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) shared&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;input, style library, hooks, utilities ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) module&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;다른 모듈을 import 해서는 안된다&lt;/li&gt;
&lt;li&gt;page: 특정 라우트에 매치되는 페이지 컴포넌트&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(2) The infrastructure layer&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API, WebSocdket, third-party 서비스들과 같은 외부 서비스들과의 상호작용을 담당한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uJcfL/btrQ0wO8T9t/U4k8SHzIhZotPWHOBjCzTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uJcfL/btrQ0wO8T9t/U4k8SHzIhZotPWHOBjCzTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uJcfL/btrQ0wO8T9t/U4k8SHzIhZotPWHOBjCzTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJcfL%2FbtrQ0wO8T9t%2FU4k8SHzIhZotPWHOBjCzTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;336&quot; height=&quot;372&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Infrastructure layer의 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) dtos&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;API처럼 서비스와 강하게 관계되어 있다.&lt;/li&gt;
&lt;li&gt;내부 로직 및 도메인에 adapt 되면 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) mappers&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;Dtos에서 어플리케이션 레이어로 데이터를 변환시켜주는 클래스이다.&lt;/li&gt;
&lt;li&gt;Infrastructure layer의 flexibility를 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) files&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;서비스를 호출하거나 mapper를 이용해 변환을 하는 등 작업을 할 때 필요한 선언들이 담겨 있는 파일들이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3) The application Layer&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱에서 비즈니스 로직 적용을 담당하는 레이어이다. Presentaiton 레이어에 존재하는 모듈과 상호 작용하고, Infrastructure 레이어에 존재하는 서비스들을 호출한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;193&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ausci/btrQ1mZviN1/ASCXpY9P2Wu8YzAeKlwiE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ausci/btrQ1mZviN1/ASCXpY9P2Wu8YzAeKlwiE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ausci/btrQ1mZviN1/ASCXpY9P2Wu8YzAeKlwiE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAusci%2FbtrQ1mZviN1%2FASCXpY9P2Wu8YzAeKlwiE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;193&quot; height=&quot;121&quot; data-origin-width=&quot;193&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) types&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;전체 구조에서 flow되는 모든 데이터를 정의하기 위한 interface, enumerator, type들을 포함한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) services&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;Infrastructure layer로 오고가는 flow를 실행시키거나 계산하기 위한 함수 및 클래스를 정의한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) redux&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;Actions, Reducers, Selectors, Middlewares를 지원하기 위한 Redux structure를 포함한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실제 프로젝트 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 호텔 예약을 관리하는 어플리케이션을 예로 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요구사항&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;조회, 생성, 수정, 삭제가 가능한 order section을 가지고 있다.&lt;/li&gt;
&lt;li&gt;달의 매출을 표시하는 dashboard 페이지를 가지고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백엔드가 지원해주는 API&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;/orders: 모든 CURD 가능&lt;/li&gt;
&lt;li&gt;/dashboard: GET만 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) Presentation Layer&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;865&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jn9Mr/btrQ65oHC7V/eGHvE1OhBM2KBuxVvWecCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jn9Mr/btrQ65oHC7V/eGHvE1OhBM2KBuxVvWecCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jn9Mr/btrQ65oHC7V/eGHvE1OhBM2KBuxVvWecCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJn9Mr%2FbtrQ65oHC7V%2FeGHvE1OhBM2KBuxVvWecCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;418&quot; height=&quot;865&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;865&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) Application Layer&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zNkVa/btrQ2RLwNDm/b9M0rlatWy8xkghtRNkD90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zNkVa/btrQ2RLwNDm/b9M0rlatWy8xkghtRNkD90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zNkVa/btrQ2RLwNDm/b9M0rlatWy8xkghtRNkD90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzNkVa%2FbtrQ2RLwNDm%2Fb9M0rlatWy8xkghtRNkD90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;341&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3) Infrastructure Layer&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caGYcq/btrQ0GjSnNc/YOyZckYVidZtYXPJls6Q00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caGYcq/btrQ0GjSnNc/YOyZckYVidZtYXPJls6Q00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caGYcq/btrQ0GjSnNc/YOyZckYVidZtYXPJls6Q00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaGYcq%2FbtrQ0GjSnNc%2FYOyZckYVidZtYXPJls6Q00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;381&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4) Flow&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조는 다음의 flow들로 이루어진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Read order flow&lt;/li&gt;
&lt;li&gt;Create or Edit flows&lt;/li&gt;
&lt;li&gt;Read dashboard flows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1XIej/btrQ0vbFOcZ/yfXRq6M3avsrNoDkCsv9Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1XIej/btrQ0vbFOcZ/yfXRq6M3avsrNoDkCsv9Nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1XIej/btrQ0vbFOcZ/yfXRq6M3avsrNoDkCsv9Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1XIej%2FbtrQ0vbFOcZ%2FyfXRq6M3avsrNoDkCsv9Nk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;610&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API를 통해서 읽는 데이터는 &quot;get-order.dto.ts&quot;에 정의되어 있고, &quot;ordersDtoToOrders.ts&quot;가 호출하는 mapper로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 mapper는 DTO를 어플리케이션 레이어로 변환하기 때문에 DTO는 Infrastructure layer에서만 사용이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 데이터는 presentation layer로 전달되어 최종적으로 유저에게 보여지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조에서 백엔드가 DTO를 바꾸는데, FE가 백엔드의 배포 시간을 정확히 모른다면 어떻게 대응해야 할까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRv7eQ/btrQ668Za7o/jUQAeKeowbhKMpJdwoVCY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRv7eQ/btrQ668Za7o/jUQAeKeowbhKMpJdwoVCY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRv7eQ/btrQ668Za7o/jUQAeKeowbhKMpJdwoVCY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRv7eQ%2FbtrQ668Za7o%2FjUQAeKeowbhKMpJdwoVCY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;737&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read-orders-lists.ts에서 flag에 따라 어떤 API를 쓸지 결정하게 하여 해결할 수 있다. 즉, 새로운 flow를 사용하는 것을 알려주는 flag가 보이면 사용할 mapper가 달라져 처리를 할 수 있게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 Application Layer와 Presentation Layer는 전혀 변경이 되지 않는다.&lt;/p&gt;</description>
      <category>로드맵/FE</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/115</guid>
      <comments>https://woojinger.tistory.com/115#entry115comment</comments>
      <pubDate>Sun, 13 Nov 2022 18:54:21 +0900</pubDate>
    </item>
    <item>
      <title>커널 - Slab</title>
      <link>https://woojinger.tistory.com/114</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Slab이란?&lt;/b&gt;&lt;/h2&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;Slab은 커널 데이터 객체 생성/파괴의 오버헤드로 인한 성능 저하를 줄이기 위해서 자주 사용되는 오브젝트들을 미리 만들어 관리한다.&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;Slab에 할당된 객체가 release 되더라도 slab은 재사용을 위해서 캐시된 상태로 유지한다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이해를 하기 위해 전체적인 메모리 할당 구조를 한번 보자.&lt;/div&gt;
&lt;div&gt;kmalloc() 함수를 통해 커널 내부에서 메모리를 할당받을 때 어떤 일이 일어날까?
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.20.22.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pZVV3/btrQ2REzc7h/DtWoCI0sbWfj2UYMSz777K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pZVV3/btrQ2REzc7h/DtWoCI0sbWfj2UYMSz777K/img.png&quot; data-alt=&quot;https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pZVV3/btrQ2REzc7h/DtWoCI0sbWfj2UYMSz777K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpZVV3%2FbtrQ2REzc7h%2FDtWoCI0sbWfj2UYMSz777K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1656&quot; height=&quot;1394&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.20.22.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1394&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;커널은 메모리를 페이지 단위로 관리하고, 커널의 page allocator을 &lt;b&gt;buddy allocator&lt;/b&gt;라고 한다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;buddy allocator&lt;/b&gt;는 external fragmentation을 줄이기 위해 메모리를 관리한다.&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.27.25.png&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckBb40/btrQZRlLcsi/yXDL4J3WWTePDD9QidPeIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckBb40/btrQZRlLcsi/yXDL4J3WWTePDD9QidPeIK/img.png&quot; data-alt=&quot;https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckBb40/btrQZRlLcsi/yXDL4J3WWTePDD9QidPeIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckBb40%2FbtrQZRlLcsi%2FyXDL4J3WWTePDD9QidPeIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1760&quot; height=&quot;916&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.27.25.png&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;(Buddy allocator에 대한 자세한 설명은 다른 글들을 참고하면 좋다)&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;커널은 buddy allocator에 두 가지 메커니즘으로 접근한다&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;slab allocator&lt;/b&gt; - physically contiguous한 메모리를 얻는다.&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;vmalloc&lt;/b&gt; - virtually contiguous하지만 physically scattered 할 수 있다.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;b&gt;slab allocator&lt;/b&gt;는 빠른 특징을 가지고 있고, physical page가 한 개 이하로 필요할 때&amp;nbsp; 주로 사용을 한다.&lt;/div&gt;
&lt;div&gt;첫번째 그림을 보면, &lt;b&gt;kmalloc&lt;/b&gt;은 이 slab allocator을 사용한다.&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.31.42.png&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1038&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2FyZv/btrQ4WrPL0N/lWyPhkGeVdPEMwPLli41n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2FyZv/btrQ4WrPL0N/lWyPhkGeVdPEMwPLli41n1/img.png&quot; data-alt=&quot;https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2FyZv/btrQ4WrPL0N/lWyPhkGeVdPEMwPLli41n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2FyZv%2FbtrQ4WrPL0N%2FlWyPhkGeVdPEMwPLli41n1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1684&quot; height=&quot;1038&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.31.42.png&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1038&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slab은 slab object들을 가지고 있는데, slab object는 slab이 미리 할당해놓은 메모리 공간이다.&lt;/p&gt;
&lt;div&gt;다음의 구조체들을 주로 할당해 가지고 있는다&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;struct task_struct&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;struct inode&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;struct mm_struct&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;kmalloc-512, kmalloc-256, kmalloc-192 ...&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;즉, kmalloc(54)를 호출하면 커널에서 buddy allocator가 관리하는 최소 페이지 단위인 4k를 할당하는 것이 아닌 kmalloc-64 슬랩 캐시가 작동해 64만큼을 할당해준다.&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Slab의 상태&lt;/b&gt;&lt;/h2&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;slab은 여러 개 존재하며, 한 slab에 존재하는 object는 모두 같은 종류이다.&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SlabDiagram.jpeg&quot; data-origin-width=&quot;481&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4J9K2/btrQ1mLLK4A/xz1ARI9YeTVs6FVTV7J8Uk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4J9K2/btrQ1mLLK4A/xz1ARI9YeTVs6FVTV7J8Uk/img.jpg&quot; data-alt=&quot;https://wiki.amigaos.net/wiki/Exec_Memory_Allocation&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4J9K2/btrQ1mLLK4A/xz1ARI9YeTVs6FVTV7J8Uk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4J9K2%2FbtrQ1mLLK4A%2Fxz1ARI9YeTVs6FVTV7J8Uk%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;481&quot; height=&quot;482&quot; data-filename=&quot;SlabDiagram.jpeg&quot; data-origin-width=&quot;481&quot; data-origin-height=&quot;482&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://wiki.amigaos.net/wiki/Exec_Memory_Allocation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;Slab은 다음 상태 중 하나로 존재한다&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;empty&lt;/b&gt;: slab의 모든 객체가 free로 마킹됨&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;parital&lt;/b&gt;: slab의 일부 객체만 used로 마킹됨&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;full&lt;/b&gt;: slab의 모든 객체가 used로 마킹 됨&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;초기에는 시스템은 모든 slab을 empty로 마킹을 한다.&lt;/div&gt;
&lt;div&gt;프로세스가 새 커널 객체를 호출했을 때, 시스템은 partial slab에서 해당하는 객체가 있는지 확인한다.&lt;/div&gt;
&lt;div&gt;만약 없다면, 시스템은 새로운 slab을 physical page에 할당해 캐시에 배정한다.&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Buffer와 Cache&lt;/h2&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;메모리 사용량을 확인하기 위해 free -h 를 쳤을 때 buff/cache는 무엇일까? slab과 어떻게 관련이 있을까?&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.43.42.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lx31H/btrQ0X6xm3z/FgbKRNtcct28SSr7r7K8X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lx31H/btrQ0X6xm3z/FgbKRNtcct28SSr7r7K8X0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lx31H/btrQ0X6xm3z/FgbKRNtcct28SSr7r7K8X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flx31H%2FbtrQ0X6xm3z%2FFgbKRNtcct28SSr7r7K8X0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;966&quot; height=&quot;116&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.43.42.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div data-pm-slice=&quot;1 3 []&quot; data-en-clipboard=&quot;true&quot;&gt;man free 로 알아보면 다음과 같다&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;buffers: 커널 버퍼에 의해 사용되는 메모리&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;cache: page cache와 slabs (Cached and SReclaimable in /proc/meminfo)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&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;스크린샷 2022-11-09 오후 5.47.47.png&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cc4H12/btrQZWtMjdQ/sFbO2j4oXZ2AHebGGeN5C0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cc4H12/btrQZWtMjdQ/sFbO2j4oXZ2AHebGGeN5C0/img.png&quot; data-alt=&quot;https://www.slideserve.com/tara/file-system-structure&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cc4H12/btrQZWtMjdQ/sFbO2j4oXZ2AHebGGeN5C0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcc4H12%2FbtrQZWtMjdQ%2FsFbO2j4oXZ2AHebGGeN5C0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;630&quot; height=&quot;402&quot; data-filename=&quot;스크린샷 2022-11-09 오후 5.47.47.png&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.slideserve.com/tara/file-system-structure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;buffer는 커널이 I/O 성능 향상을 위해 사용하는 메모리 영역으로,&amp;nbsp; &lt;b&gt;Super block&lt;/b&gt;과 &lt;b&gt;inode block&lt;/b&gt;에 해당하는 메타데이터를 저장한다.&lt;/div&gt;
&lt;div&gt;page cache는 파일의 데이터를 캐싱하는 것이고, slab은 위에서 설명한 바 있다.&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Slab 메모리 보는 법&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1668308237229&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo cat /proc/slabinfo&lt;/code&gt;&lt;/pre&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;커널에서 자주 사용되는 객체들의 캐시를 보여준다&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;
&lt;pre id=&quot;code_1668308248589&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo vmstat -m&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;slabinfo의 캐시 객체 별로 Num, Total, Size, Pages 를 보여준다&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SReclaimable, SUnreclaim&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1668308447395&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat /proc/meminfo&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;위 명령어로 메모리를 조회하면 SReclaimable과 SUnreclaim을 확인할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;SReclaimable&lt;/b&gt;: reclaim이 될 수 있는 slab의 파트&lt;/div&gt;
&lt;div&gt;&lt;b&gt;SUnreclaim&lt;/b&gt;: Slab이 사용을 하고 있는 영역이므로, 메모리가 꽉 차 더 필요하더라도 &lt;b&gt;release되지 않는다&lt;/b&gt;.&lt;/div&gt;
&lt;div&gt;(출처: &lt;a href=&quot;https://man7.org/linux/man-pages/man5/proc.5.html&quot;&gt;https://man7.org/linux/man-pages/man5/proc.5.html&lt;/a&gt;)&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;SUnreclaim이 늘어나는 이유는 커널 컴포넌트가 kamlloc으로 메모리를 할당한 후 release를 안해서 그런 것이다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;이 경우 프로세스를 kill해 죽이더라도, SUnreclaim은 줄어들지 않는다.&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;(출처:&amp;nbsp;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.alibabacloud.com/help/en/elastic-compute-service/latest/identify-the-causes-of-high-percentage-of-the-slab-unreclaimable-memory-in-the-linux-operating-system&quot;&gt;https://www.alibabacloud.com/help/en/elastic-compute-service/latest/identify-the-causes-of-high-percentage-of-the-slab-unreclaimable-memory-in-the-linux-operating-system&lt;/a&gt;)&lt;/p&gt;
&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Shrink&lt;/b&gt;&lt;/h2&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;커널의 캐시를 줄이는 방법은 아래와 같다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://community.oracle.com/tech/apps-infra/discussion/3553285/linux-meminfo-very-high-slab&quot;&gt;https://community.oracle.com/tech/apps-infra/discussion/3553285/linux-meminfo-very-high-slab&lt;/a&gt;&lt;/div&gt;
&lt;pre id=&quot;code_1668308495003&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;To free pagecache: echo 1 &amp;gt; /proc/sys/vm/drop_caches

To free reclaimable slab objects (includes dentries and inodes): echo 2 &amp;gt; /proc/sys/vm/drop_caches

To free slab objects and pagecache: echo 3 &amp;gt; /proc/sys/vm/drop_caches&lt;/code&gt;&lt;/pre&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;하지만 위 방법은 SUnreclaim을 줄이지는 못한다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;SUnreclaim을 줄이는 방법은 다음과 같다.&lt;/div&gt;
&lt;pre id=&quot;code_1668308530360&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo bash -c 'echo 1 &amp;gt; /sys/kernel/slab/kmalloc-4096/shrink'&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;사실 이 명령어는 slab의 kmalloc-4096 오브젝트를 shrink하는 것이다.
&lt;div&gt;더 효과를 보려면 slabinfo를 조회해 다른 kmalloc 오브젝트를 줄이는 것이 도움이 될 수 있다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;위 명령어는 위험하지 않을까?&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-kernel-slab&quot;&gt;https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-kernel-slab&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;위 글은 2007~2008년 작성된 kernel docs로 shrink에 대한 언급이 있다.&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1668308549201&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;The shrink file is used to reclaim unused slab cache
memory from a cache.  Empty per-cpu or partial slabs
are freed and the partial list is sorted so the slabs
with the fewest available objects are used first.
It only accepts a value of &quot;1&quot; on write for shrinking
the cache. Other input values are considered invalid.
Shrinking slab caches might be expensive and can
adversely impact other running applications.  So it
should be used with care.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;다른 실행 중인 어플리케이션에 영향을 미칠 수 있다고 되어 있다. 되도록이면 안 쓰는 것이 좋아보인다.&lt;/div&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;div&gt;&lt;a href=&quot;https://brunch.co.kr/@dreaminz/2&quot;&gt;https://brunch.co.kr/@dreaminz/2&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://brunch.co.kr/@dreaminz/3&quot;&gt;https://brunch.co.kr/@dreaminz/3&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;http://rousalome.egloos.com/10001242&quot;&gt;http://rousalome.egloos.com/10001242&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://lore.kernel.org/linux-mm/20190723151445.7385-1-longman@redhat.com/&quot;&gt;https://lore.kernel.org/linux-mm/20190723151445.7385-1-longman@redhat.com/&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Slab_allocation&quot;&gt;https://en.wikipedia.org/wiki/Slab_allocation&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://wiki.amigaos.net/wiki/Exec_Memory_Allocation&quot;&gt;https://wiki.amigaos.net/wiki/Exec_Memory_Allocation&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://community.oracle.com/tech/apps-infra/discussion/3553285/linux-meminfo-very-high-slab&quot;&gt;https://community.oracle.com/tech/apps-infra/discussion/3553285/linux-meminfo-very-high-slab&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://www.alibabacloud.com/help/en/elastic-compute-service/latest/identify-the-causes-of-high-percentage-of-the-slab-unreclaimable-memory-in-the-linux-operating-system&quot;&gt;https://www.alibabacloud.com/help/en/elastic-compute-service/latest/identify-the-causes-of-high-percentage-of-the-slab-unreclaimable-memory-in-the-linux-operating-system&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-kernel-slab&quot;&gt;https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-kernel-slab&lt;/a&gt;&lt;/div&gt;</description>
      <category>로드맵/OS</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/114</guid>
      <comments>https://woojinger.tistory.com/114#entry114comment</comments>
      <pubDate>Sun, 13 Nov 2022 12:02:44 +0900</pubDate>
    </item>
    <item>
      <title>Recovery System(1)</title>
      <link>https://woojinger.tistory.com/113</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewEng.laf?mallGb=ENG&amp;amp;ejkGb=ENG&amp;amp;barcode=9781260084504&quot;&gt;http://www.kyobobook.co.kr/product/detailViewEng.laf?mallGb=ENG&amp;amp;ejkGb=ENG&amp;amp;barcode=9781260084504&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1647675223425&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;book&quot; data-og-title=&quot;Database System Concepts - 교보문고&quot; data-og-description=&quot; &quot; data-og-host=&quot;www.kyobobook.co.kr&quot; data-og-source-url=&quot;http://www.kyobobook.co.kr/product/detailViewEng.laf?mallGb=ENG&amp;amp;ejkGb=ENG&amp;amp;barcode=9781260084504&quot; data-og-url=&quot;http://www.kyobobook.co.kr/product/detailViewEng.laf?mallGb=ENG&amp;amp;ejkGb=ENG&amp;amp;barcode=9781260084504&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eyU92/hyNKc5N98E/i7bpyMBrTytGKHiDCjHj80/img.jpg?width=458&amp;amp;height=574&amp;amp;face=0_0_458_574&quot;&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewEng.laf?mallGb=ENG&amp;amp;ejkGb=ENG&amp;amp;barcode=9781260084504&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.kyobobook.co.kr/product/detailViewEng.laf?mallGb=ENG&amp;amp;ejkGb=ENG&amp;amp;barcode=9781260084504&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eyU92/hyNKc5N98E/i7bpyMBrTytGKHiDCjHj80/img.jpg?width=458&amp;amp;height=574&amp;amp;face=0_0_458_574');&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;Database System Concepts - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.kyobobook.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Storage&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에서 스토리지는 3가지로 나눌 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Volatile storage&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;Non-Volatile storage&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;Stable storage&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;b&gt;volatile storage&lt;/b&gt;&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;volatile storage 안의 정보들은 시스템 크래시가 발생하면 일반적으로 살아남지 못한다.&lt;/div&gt;
&lt;div&gt;메인 메모리나 캐시 메모리가 해당된다.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;volatile storage로의 접근은 굉장히 빠른데, 메모리 접근 자체가 그 자체로 빠를 뿐더러 volatile storage의 어떤 데이터든 directly하게 접근할 수 있기 때문이다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;Non-volatile storage&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;non volatile storage의 정보들은 시스템 크래시 발생 이후에도 살아남을 수 있다.&lt;/div&gt;
&lt;div&gt;하드 디스크나 플래시 메모리와 같은 2차 스토리지가 해당된다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;stable storage&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;stable storage에 있는 정보는 절대 소실되지 않는다.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;stable storage를 구현하려면 우리는 여러 non-volatile storage에(보통은 disk) replicate를 해놓는다.&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAID 시스템이 한 예이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Data access&lt;/b&gt;&lt;/h2&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;데이터베이스는 block이라는 고정된 길이의 단위로 나뉘어진다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;physical block&lt;/b&gt; : 디스크와 메인 메모리간 오퍼레이션들은 블록 단위로 이루어진다. 디스크에 저장된 블록이 physical block&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;buffer block&lt;/b&gt; : 메인 메모리에 일시적으로 저장된 블록&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;disk buffer&lt;/b&gt; : 메모리에 저장된 블록들의 영역&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;디스크와 메인 메모리간 블록 움직임은 다음과 같다&lt;/div&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;input(B)&lt;/b&gt;는 physical block B를 메인 메모리로 옮긴다&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;output(B)&lt;/b&gt;는 buffer block B를 디스크로 옮기고 적절한 physical block과 교체한다.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;우리는 두 개의 오퍼레이션으로 데이터를 옮긴다&lt;/div&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;Read(X)&lt;/b&gt; : 데이터 아이템 X의 값을 지역 변수 xi에 할당한다.&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;Write(X)&lt;/b&gt; : 지역 변수 xi의 값을 buffer block 의 X에 할당한다.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;buffer block이 버퍼 매니저가 메모리가 필요하거나 데이터베이스가 B의 변경을 디스크에 반영하기 위해 디스크에 써지는 경우가 있다. 이것을 데이터 시스템이 output(B)를 통해 buffer B를 force-output 했다고 말한다.&lt;/div&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;Recovery and Atomicity&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Log Records&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;데이터베이스 수정을 기록하는데 가장 많이 사용되는 구조는 log이다.&lt;/div&gt;
&lt;div&gt;log는 log records로 이루어져 데이터베이스의 모든 업데이트 활동을 기록한다.&lt;/div&gt;
&lt;div&gt;log record에는 여러 타입이 있고,&lt;b&gt; update log record는 single database write&lt;/b&gt;을 의미한다.&lt;/div&gt;
&lt;div&gt;update log record는 다음의 field를 가지고 있다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;transaction identifier : write operation을 만든 트랜잭션의 unique identifier&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;Data-item identifier : written된 데이터 아이템의 unique identifier. 보통 데이터 아이템의 디스크 위치이고, 블록 identifier와 블록 내부의 오프셋으로 이루어진다.&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;Old value : write되기 전 이전 값&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;New value : write된 후의 값&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;update log record는 &amp;lt;Ti,Xj, V1,V2&amp;gt; 로 표현가능하다(트랜잭션 Ti가 데이터 아이템 Xj에 write을 하고 값 V1이 V2로 바뀐다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;&amp;lt;Ti start&amp;gt;&lt;/b&gt; : 트랜잭션 Ti가 시작&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;&amp;lt;Ti commit&amp;gt;&lt;/b&gt; : 트랜잭션 Ti가 커밋됐다&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;&amp;lt;Ti abort&amp;gt;&lt;/b&gt; : 트랜잭션 Ti가 aborted 됐다&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;트랜잭션이 write을 수행하면 데이터베이스가 수정되기 전에 log record가 생성되고 log에 추가된다.&lt;/div&gt;
&lt;div&gt;log record가 존재하면 우리는 우리가 원할때 데이터베이스에 수정을 output할 수 있고, 이미 데이터베이스에 output된 사항을 되돌릴 수 있다. old value 필드를 이용하면 가능하다.&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Database Modification&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;트랜잭션은 log record를 데이터베이스를 수정하기 전에 만든다.&lt;/div&gt;
&lt;div&gt;log record들이 트랜잭션이 abort되는 상황에서 undo하게 해주거나 디스크에 있는 DB에 반영이 되기 전 시스템이 크래시가 났을 때 redo할 수 있게 해준다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;트랜잭션이 데이터를 수정하는 과정&lt;/b&gt;&lt;/div&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;트랜잭션이 메인 메모리의 private part에서 계산 과정들을 수행한다&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;트랜잭션이 메인 메모리의 디스크 버퍼의 데이터 블록을 수정한다&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;데이터베이스 시스템이 데이터 블록을 디스크에 쓰기 위해 output operation을 수행한다&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;트랜잭션이 디스크 버퍼를 업데이트 했거나 디스크를 업데이트 하는 경우 트랜잭션이 데이터베이스를 수정한다고 한다. 메인 메모리의 private part를 업데이트 하는 것은 데이터베이스를 수정한다고 하지 않는다.&lt;/div&gt;
&lt;div&gt;만약 트랜잭션이 커밋되기 전까지 데이터베이스를 수정하지 않는다면 &lt;b&gt;deferred modification&lt;/b&gt; 테크닉을 쓴다고 한다.&lt;/div&gt;
&lt;div&gt;트랜잭션이 active할 때 데이터베이스 수정이 일어나면&lt;b&gt; immediate modification&lt;/b&gt; 테크닉을 쓴다고 한다.&lt;/div&gt;
&lt;div&gt;deffered modification 테크닉은 업데이트된 아이템들에 대해 로컬 카피를 만들어야 해서 오버헤드가 있다. 트랜잭션이 업데이트된 데이터를 읽으려고 하면 로컬 카피에서 읽어와야 한다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;recovery 알고리즘은 여러 상황을 고려해야하고, 다음과 같은 부분까지도 고려해야 한다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;트랜잭션이 커밋되었지만 일부 데이터베이스 수정이 메인 메모리의 디스크 버퍼에만 있고 디스크에는 반영이 되지 않은 경우&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;트랜잭션이 active한 상태에서 데이터베이스를 수정했지만 뒤 과정에서 failure가 발생해 abort해야 하는 경우&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;b&gt;undo&lt;/b&gt; : 로그 레코드의 old value를 이용해 명시된 데이터를 old 값으로 바꾸는 것&lt;/div&gt;
&lt;div&gt;&lt;b&gt;redo&lt;/b&gt; : new value를 이용해 명시된 데이터를 새 값으로 바꾸는 것&lt;/div&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;Concurrency control and recovery&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;concurrency control scheme을 허용하면&lt;/div&gt;
&lt;div&gt;데이터 아이템 X가 트랜잭션 T1에 의해 수정되고 T1이 커밋되기 전 T2에 의해 수정될 때, T1을 undo하기 위해 X를 old value로 복구하면 T2도 undo가 된다.&lt;/div&gt;
&lt;div&gt;이러한 상황을 피하기 위해 recovery 알고리즘은 데이터 아이템이 트랜잭션에 의해 수정될 때 커밋이나 abort되기 전까지 다른 트랜잭션이 수정하지 못하게 한다&lt;/div&gt;
&lt;div&gt;이는&lt;b&gt; two-phase locking이 필요&lt;/b&gt;하다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Transaction Commit&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;b&gt;트랜잭션의 최신 로그 레코드인 커밋 로그 레코드가 안정한 스토리지로 output 되었을 때 커밋되었다고 한다&lt;/b&gt;.&lt;/div&gt;
&lt;div&gt;이때 &lt;b&gt;모든 로그 레코드들은 안정한 스토리지로 output 되어 있는 상태&lt;/b&gt;여야 한다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;시스템 크래시가 발생했을 때 트랜잭션이 redo될 수 있는 충분한 정보를 제공한다.&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;만약 로그 레코드에 &amp;lt;Ti commit&amp;gt;이 stable storage로 output되기 전에 시스템 크래시가 발생하면 트랜잭션 Ti는 롤백되야 한다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Using the Log to Redo and Undo Transactions&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;은행 시스템을 생각해보자.&lt;/div&gt;
&lt;div&gt;T0는 A계좌에서&amp;nbsp; B 계좌로 50달러를 옮긴다&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;137&quot; data-origin-height=&quot;145&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0IaVe/btrwnaxIEry/RkeO3KDPMK4qQXJ7xBm8F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0IaVe/btrwnaxIEry/RkeO3KDPMK4qQXJ7xBm8F1/img.png&quot; data-alt=&quot;Database System Concepts&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0IaVe/btrwnaxIEry/RkeO3KDPMK4qQXJ7xBm8F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0IaVe%2FbtrwnaxIEry%2FRkeO3KDPMK4qQXJ7xBm8F1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;137&quot; height=&quot;145&quot; data-origin-width=&quot;137&quot; data-origin-height=&quot;145&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Database System Concepts&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;T1은 C계좌에서 100달러를 인출한다&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;147&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcQjR2/btrwreeO7Bu/FhiT3JFQIsofsNsaC3gdfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcQjR2/btrwreeO7Bu/FhiT3JFQIsofsNsaC3gdfK/img.png&quot; data-alt=&quot;Database System Concepts&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcQjR2/btrwreeO7Bu/FhiT3JFQIsofsNsaC3gdfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcQjR2%2FbtrwreeO7Bu%2FFhiT3JFQIsofsNsaC3gdfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;147&quot; height=&quot;73&quot; data-origin-width=&quot;147&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Database System Concepts&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;Recovery scheme은 두 recovery 과정을 사용한다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;Redo(Ti)&lt;/b&gt; : 트랜잭션 Ti의 새 값으로 모든 데이터 아이템들의 값을 업데이트 한다. redo에서 업데이트들의 순서는 중요하다. 대부분의 복구 알고리즘에서 redo의 각 트랜잭션을 분리해서 실행하지 않는다.&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;undo(Ti)&lt;/b&gt; : 트랜잭션 Ti의 old value로 모든 데이터 아이템들의 값을 업데이트한다.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;시스템 크래시가 발생하면 시스템은 원자성을 지키기 위해 어떤 트랜잭션이 redo되고 undo되야 하는지 결정한다&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;트랜잭션 Ti가 &amp;lt;Ti start&amp;gt;가 있지만 &amp;lt;Ti commit&amp;gt; 혹은&amp;nbsp; &amp;lt;Ti abort&amp;gt;가 없다면 undo되어야 한다&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;트랜잭션 Ti가 &amp;lt;Ti start&amp;gt;가 있고 &amp;lt;Ti commit&amp;gt; 혹은 &amp;lt;Ti abort&amp;gt;가 있다면 redo되어야 한다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&amp;lt;Ti abort&amp;gt;는 undo operation이 만든 redo only record이다. 그래서 최종 결과는 Ti의 수정을 undo 하는 케이스 일 것이다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;Ti abort는 중복을 만들지만, 복구 알고리즘을 단순화해 전체 복구 시간을 빠르게 해준다.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IrQUX/btrwnPAun2L/MvzknUD4mtmK4sGqgBNAZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IrQUX/btrwnPAun2L/MvzknUD4mtmK4sGqgBNAZK/img.png&quot; data-alt=&quot;Database System Concepts&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IrQUX/btrwnPAun2L/MvzknUD4mtmK4sGqgBNAZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIrQUX%2FbtrwnPAun2L%2FMvzknUD4mtmK4sGqgBNAZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;249&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Database System Concepts&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;19.4.(a) : T0의 write(B) 이후 크래시가 발생했을 때 트랜잭션 T0는 안정한 스토리지에 written 되어 있다. 시스템이 돌아왔을 때 &amp;lt;T0 start&amp;gt;는 찾았지만 &amp;lt;T0 commit&amp;gt; 혹은 &amp;lt;T0 abort&amp;gt;는 찾지 못했으므로 T0는 undo 되어야 한다. 이 결과로 디스크 안의 A와 B는 각각 1000달러, 2000달러로 복구된다.&lt;/div&gt;
&lt;div&gt;19.4.(b) : 트랜잭션 T1의 Wirte(C) 이후 crash가 발생한 시나리오다. 시스템이 돌아왔을 때 두 개의 복구 액션들이 필요하다. undo(T1)과 redo(T0)이 수행되어야 한다.&lt;/div&gt;
&lt;div&gt;19.4.(c) : T1 커밋 이후 crash가 발생한 경우다. redo(T0), redo(T1)
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Checkpoints&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;시스템이 크래시가 나면 &lt;b&gt;로그를 보고 어떤 것을 redo할지, 어떤 것을 undo할 지 결정&lt;/b&gt;해야 한다.&lt;/div&gt;
&lt;div&gt;모든 로그에 대해 작업을 하면 좋겠지만 모든 로그를 보는 것은 시간이 오래 걸리며, &lt;b&gt;redo의 경우 대부분 이미 데이터베이스에 반영이 되어 있다.&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이러한 오버헤드를 줄이기 위하여 &lt;b&gt;checkpoint&lt;/b&gt;를 둔다.&lt;/div&gt;
&lt;div&gt;체크포인트가 수행되면 다음 과정이 일어난다&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;메인 메모리에 존재하는 모든 로그 레코드들을 안정한 스토리지로 output한다.&lt;/b&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;디스크에 모든 수정된 buffer block을 output한다.&lt;/b&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;안정한 스토리지로 &amp;lt;checkpoint L&amp;gt; 형태의 log record를 output한다. L은 checkpoint의 시간에 액티브한 트랜잭션의 리스트이다.&lt;/b&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;b&gt;체크포인트 진행중에는 어떤 업데이트 action을 수반하는 트랜잭션도 수행되면 안된다.&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;시스템이 크래시가 발생하면 가장 최신의 체크포인트 &amp;lt;checkpoint L&amp;gt; 레코드를 찾는다. L에 존재하는 모든 트랜잭션의 redo 또는 undo는 적용이 되어야 하고, L 이후의 모든 트랜잭션도 적용이 되어야 한다. &lt;/b&gt;적용되어야 하는 트랜잭션들의 셋을 T라고 표시하자.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;T의 모든 트랜잭션들 중 커밋 또는 abort가 없는 것들을 undo가 되어야 한다&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;커밋 또는 abort가 있다면 redo가 되어야 한다.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;L의 모든 트랜잭션보다 일찍 시작한 트랜잭션들은 더이상 필요가 없다. (이미 완료가 되었으니까)&lt;/div&gt;
&lt;div&gt;이런 로그 레코드들은 데이터베이스가 공간이 필요할 때 지워진다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>로드맵/DB</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/113</guid>
      <comments>https://woojinger.tistory.com/113#entry113comment</comments>
      <pubDate>Sat, 19 Mar 2022 16:45:51 +0900</pubDate>
    </item>
    <item>
      <title>defer - 실수하기 쉬운 인자 및 body의 값 계산 시점</title>
      <link>https://woojinger.tistory.com/112</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://go.dev/doc/effective_go#defer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://go.dev/doc/effective_go#defer&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1647673726552&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;Effective Go - The Go Programming Language&quot; data-og-description=&quot;Effective Go Introduction Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C&quot; data-og-host=&quot;go.dev&quot; data-og-source-url=&quot;https://go.dev/doc/effective_go#defer&quot; data-og-url=&quot;https://go.dev/doc/effective_go#defer&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://go.dev/doc/effective_go#defer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://go.dev/doc/effective_go#defer&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;Effective Go - The Go Programming Language&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Effective Go Introduction Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;go.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Effective Go를 보면 defer가 실행하는 함수의 인자에 들어가는 값은 &lt;b&gt;defer가 실행이 될 때 정해진다(defer가 함수를 call할 때가 아니라)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;b&gt;defer는 마치 스택처럼 LIFO 순서대로 실행이 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 기억하고, 다음 예제를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647674062961&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package main

import &quot;fmt&quot;

type A struct {
	num int
}

func main() {
	a := A{
		num: 1,
	}
	arr1 := []*A{&amp;amp;a}
	arr2 := []A{a}
	defer func() {
 		fmt.Println((arr1[0]))
	}() // 1st defer
	defer fmt.Println(arr1[0]) // 2nd defer
	defer fmt.Println(arr2[0]) // 3rd defer

	arr1[0].num = 2
	arr2[0].num = 2
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력이 어떻게 될 것 같은가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 함수의 인자 값이 defer를 실행할 때 정해지므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤에 나온 arr1과 arr2의 num을 2로 바꿈과 상관 없이 num을 1로 가지고 있는 a 타입의 객체를 반환할 수 있다고 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 결과 값은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1647674258564&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{1} // 3rd defer
&amp;amp;{2} // 2nd defer
&amp;amp;{2} // 1st defer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;arr1을 출력한 함수들은 num이 2인 객체를 반환하였다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 arr1이 가지고 있는 값이 a 객체가 아니라 a 객체의 주소값이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세번째 defer에서는 arr2가 가진 a 객체를 복사하여 출력을 한 것이고, 첫번째와 두번째 defer은 a 객체의 주소 값을 복사하였기 때문에 num이 2로 바뀐 a 객체를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다음 예제를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647674389096&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package main

import &quot;fmt&quot;

type A struct {
    num int
}

func main() {
    a := A{
        num: 1,
    }
    arr1 := []*A{&amp;amp;a}
    arr2 := []A{a}
    defer func() {
        fmt.Println(*(arr1[0]))
    }() // 1st defer
    defer fmt.Println(*(arr1[0])) // 2nd defer
    defer fmt.Println(arr2[0]) // 3rd defer

    arr1[0].num = 2
    arr2[0].num = 2
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제와 다른 점은 &lt;b&gt;arr1 출력시 reference 하는 객체를 불러온다.&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;함수의 인자에 들어가는 값은 defer 실행시 계산이 되므로 1st defer, 2nd defer 모두 num이 1인 a 객체를 복사 받아 전부 num이 1인 a 객체를 출력했다고 착각하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 결과는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1647674739554&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{1} // 3rd defer
{1} // 2nd defer
{2} // 1st defer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 defer은 여전히 2를 출력한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그 이유는 첫번째 defer의 경우 함수의 인자에 arr1이 있는 것이 아닌 body에 arr1이 있기 때문이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 call되는 시점은 defer를 감싸고 있는 함수가 return되기 직전이기에 arr1[0]이 가리키고 있는 객체의 num이 2가된 후에 call이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>언어/Go</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/112</guid>
      <comments>https://woojinger.tistory.com/112#entry112comment</comments>
      <pubDate>Sat, 19 Mar 2022 16:27:49 +0900</pubDate>
    </item>
    <item>
      <title>유니스왑 -  Concentrated Liquidity 소스 훑어보기 (상세X)</title>
      <link>https://woojinger.tistory.com/111</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 유니스왑 v3 백서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://github.com/Uniswap&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/Uniswap&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://starli.medium.com/uniswap-deep-dive-into-v3s-source-code-b141c1754bae&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://starli.medium.com/uniswap-deep-dive-into-v3s-source-code-b141c1754bae&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니스왑 v3의 라이선스는 오픈소스는 아니다.&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;스크린샷 2022-02-27 오후 2.30.42.png&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;1310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cytNvW/btruvt603K5/evjhpnaRKVzzzxL6a9W0xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cytNvW/btruvt603K5/evjhpnaRKVzzzxL6a9W0xk/img.png&quot; data-alt=&quot;https://starli.medium.com/uniswap-deep-dive-into-v3s-source-code-b141c1754bae&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cytNvW/btruvt603K5/evjhpnaRKVzzzxL6a9W0xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcytNvW%2Fbtruvt603K5%2FevjhpnaRKVzzzxL6a9W0xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1384&quot; height=&quot;1310&quot; data-filename=&quot;스크린샷 2022-02-27 오후 2.30.42.png&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;1310&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://starli.medium.com/uniswap-deep-dive-into-v3s-source-code-b141c1754bae&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니스왑 v3의 코드는 크게 core와 periphery로 나눌 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Periphery 부분은 다시 크게 Position management 부분과 swap router management 부분으로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 Position management 부분과 core 로직을 집중적으로 살펴 볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&amp;nbsp;Concentrated Liquidity란?&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;스크린샷 2022-02-27 오후 2.44.34.png&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caMdQD/btruxjiMps9/kuYkDqWKcqHqNXmOPKSVC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caMdQD/btruxjiMps9/kuYkDqWKcqHqNXmOPKSVC1/img.png&quot; data-alt=&quot;유니스왑 백서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caMdQD/btruxjiMps9/kuYkDqWKcqHqNXmOPKSVC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaMdQD%2FbtruxjiMps9%2FkuYkDqWKcqHqNXmOPKSVC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;586&quot; height=&quot;554&quot; data-filename=&quot;스크린샷 2022-02-27 오후 2.44.34.png&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;750&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;유니스왑 백서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-03-03 오후 9.37.16.png&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biwA54/btru4iQBU6s/Zmt3f80sQG20unGanmGfdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biwA54/btru4iQBU6s/Zmt3f80sQG20unGanmGfdK/img.png&quot; data-alt=&quot;Figure 2의 식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biwA54/btru4iQBU6s/Zmt3f80sQG20unGanmGfdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiwA54%2Fbtru4iQBU6s%2FZmt3f80sQG20unGanmGfdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;356&quot; height=&quot;73&quot; data-filename=&quot;스크린샷 2022-03-03 오후 9.37.16.png&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Figure 2의 식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니스왑 v2에서는 유동성을 공급하면 가격 변동폭이 크지 않은 이상 일부의 유동성만 사용이 된다는 단점이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 한 토큰이 풀에서 끝까지 소모가 되지 않는 이상 항상 쓰이지 않는 토큰들이 풀에 남게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v3에서는 유동성 공급 시 구간을 설정해 유동성을 공급할 수 있게 하였다. 만약 가격이 해당 가격 범위를 넘어서게 되면, 설정한 구간의 유동성은 사용되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 보면 v3에서는 virtual reserves를 평행 이동한 것이 실제 reserve인 것을 확인할 수 있다. 만약 토큰의 가격 변동이 a~b구간에서만 움직인다면 적은 유동성으로 똑같은 효과를 낼 수 있게 된다.&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;Concentrated Liquidity 구현 살펴보기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Pool의 구성&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1645941926674&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// @title NFT positions
/// @notice Wraps Uniswap V3 positions in the ERC721 non-fungible token interface
contract NonfungiblePositionManager is
    INonfungiblePositionManager,
    Multicall,
    ERC721Permit,
    PeripheryImmutableState,
    PoolInitializer,
    LiquidityManagement,
    PeripheryValidation,
    SelfPermit
{
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;periphery 부분의 NonfungiblePositionManager 부분을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v3는 v2와 다르게 각 포지션이 두 개의 틱으로 이루어져 있기 때문에 같은 풀이어도 포지션이 달라 ERC721 토큰으로 포지션을 wrap을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NonfungiblePositionManager가 상속한 PoolInitializer 코드를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1645942336484&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// @title Creates and initializes V3 Pools
abstract contract PoolInitializer is IPoolInitializer, PeripheryImmutableState {
    /// @inheritdoc IPoolInitializer
    function createAndInitializePoolIfNecessary(
        address token0,
        address token1,
        uint24 fee,
        uint160 sqrtPriceX96
    ) external payable override returns (address pool) {
        require(token0 &amp;lt; token1);
        pool = IUniswapV3Factory(factory).getPool(token0, token1, fee);

        if (pool == address(0)) {
            pool = IUniswapV3Factory(factory).createPool(token0, token1, fee);
            IUniswapV3Pool(pool).initialize(sqrtPriceX96);
        } else {
            (uint160 sqrtPriceX96Existing, , , , , , ) = IUniswapV3Pool(pool).slot0();
            if (sqrtPriceX96Existing == 0) {
                IUniswapV3Pool(pool).initialize(sqrtPriceX96);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pool을 만드는 함수를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 sqrtPriceX96은 Pool에서 Price의 제곱근을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀에 사용될 두 개의 토큰과 fee, 그리고 Price를 받아 새로운 pool을 생성한다.&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;스크린샷 2022-02-27 오후 3.14.59.png&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k75TI/btrupIdgRvG/VNPztsUNXwO1LJomurRhk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k75TI/btrupIdgRvG/VNPztsUNXwO1LJomurRhk1/img.png&quot; data-alt=&quot;유니스왑 백서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k75TI/btrupIdgRvG/VNPztsUNXwO1LJomurRhk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk75TI%2FbtrupIdgRvG%2FVNPztsUNXwO1LJomurRhk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;243&quot; data-filename=&quot;스크린샷 2022-02-27 오후 3.14.59.png&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;372&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;유니스왑 v3는 pool의 virtual reserve인 x, y를 각각 트래킹을 하는 대신 liquidity(L)과 sqrtPrice를 트래킹한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-03-03 오후 9.38.08.png&quot; data-origin-width=&quot;115&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I4QZU/btruW98XFtN/5mHAmtUNcD7A4in0tGvXpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I4QZU/btruW98XFtN/5mHAmtUNcD7A4in0tGvXpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I4QZU/btruW98XFtN/5mHAmtUNcD7A4in0tGvXpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI4QZU%2FbtruW98XFtN%2F5mHAmtUNcD7A4in0tGvXpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;115&quot; height=&quot;96&quot; data-filename=&quot;스크린샷 2022-03-03 오후 9.38.08.png&quot; data-origin-width=&quot;115&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x, y는 liquidity와 sqrtPrice를 통해 계산이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sqrtPrice는 swap을 할 때 변하고, liquidity는 민팅을 하거나 liquidity를 burn할 때 변경되므로 동시에 같이 변하는 일이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 변수를 관리하기 편하게 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1645943978559&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
   ...
   
    /// @inheritdoc IUniswapV3PoolImmutables
    address public immutable override factory;
    /// @inheritdoc IUniswapV3PoolImmutables
    address public immutable override token0;
    /// @inheritdoc IUniswapV3PoolImmutables
    address public immutable override token1;
    /// @inheritdoc IUniswapV3PoolImmutables
    uint24 public immutable override fee;

    /// @inheritdoc IUniswapV3PoolImmutables
    int24 public immutable override tickSpacing;
    
   ...
   
   struct Slot0 {
        // the current price
        uint160 sqrtPriceX96;
        // the current tick
        int24 tick;
        // the most-recently updated index of the observations array
        uint16 observationIndex;
        // the current maximum number of observations that are being stored
        uint16 observationCardinality;
        // the next maximum number of observations to store, triggered in observations.write
        uint16 observationCardinalityNext;
        // the current protocol fee as a percentage of the swap fee taken on withdrawal
        // represented as an integer denominator (1/x)%
        uint8 feeProtocol;
        // whether the pool is locked
        bool unlocked;
    }
    /// @inheritdoc IUniswapV3PoolState
    Slot0 public override slot0;
    
    /// @inheritdoc IUniswapV3PoolState
    uint256 public override feeGrowthGlobal0X128;
    /// @inheritdoc IUniswapV3PoolState
    uint256 public override feeGrowthGlobal1X128;

    // accumulated protocol fees in token0/token1 units
    struct ProtocolFees {
        uint128 token0;
        uint128 token1;
    }
    /// @inheritdoc IUniswapV3PoolState
    ProtocolFees public override protocolFees;
    
    uint128 public override liquidity
    ...
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;core에 들어있는 UniswapV3Pool contract에서 sqrtPrice, tick, feeProtocol 등을 담고 있는 slot0와 liquidity를 가지고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tickSpacing&lt;/b&gt;은 tick이 설정될 수 있는 간격을 의미한다. 즉 모든 tick은 tickSpacing으로 나누어져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tickSpacing이 작으면 tick이 촘촘하게 생성이 될 수 있으나 tick을 생성할 때 gas가 들어가므로 gas를 좀 더 많이 소모할 가능성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;fee&lt;/b&gt;는 swapper들이 내야하는 수수료를 나타내고, feeProtocol은 liquidity provider에게 가지 않고 프로토콜에게 가는 fee의 비율을 나타낸다. (default는 0이나 UNI 가버넌스에 의해 변경될 수 있다)&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;feeGrowthGlobal0X128&lt;/b&gt;과 &lt;b&gt;feeGrowthGlobal1X128&lt;/b&gt;은 단위 L당 벌어들인 총 fee를 의미한다. &lt;b&gt;이때 protocol Fee는 제외한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ProtocolFees&lt;/b&gt;는 아직 가져오지 못한 토큰별 &lt;b&gt;프로토콜 fee&lt;/b&gt;를 의미한다. 이 protocol fee들은 UNI 가버넌스가 collectProtocol 함수를 통해 가져올 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.03.14.png&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sA6ID/btruxjb3v1t/vL08aLKQEbBcVenE6wl5d1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sA6ID/btruxjb3v1t/vL08aLKQEbBcVenE6wl5d1/img.png&quot; data-alt=&quot;fg1은 feeGrowthGlobal, fp1은 protocolFee를 의미&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sA6ID/btruxjb3v1t/vL08aLKQEbBcVenE6wl5d1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsA6ID%2Fbtruxjb3v1t%2FvL08aLKQEbBcVenE6wl5d1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;312&quot; height=&quot;136&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.03.14.png&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;fg1은 feeGrowthGlobal, fp1은 protocolFee를 의미&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 식은 싱글 틱에서 스왑이 일어날 때 증가되는 fee의 양이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감마는 수수료율, 파이는 프로토콜 fee에 배정되는 비율을 의미한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.&amp;nbsp; Swap 코드 살펴보기 - 싱글 틱&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스왑되는 &lt;b&gt;양이 많지 않을 때 가격이 다음 tick을 넘어가지 않고 스왑할 수 있다&lt;/b&gt;. 이 상황을&lt;b&gt; 싱글 틱&lt;/b&gt;에서 스왑이라고 하겠다. 이때는 x*y = k가 성립한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yin을 token1이 보내진 양이라고 하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.08.47.png&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;52&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccs6Hs/btruuhZ7YPs/o6UrUL7bpLuk2rm8VelC8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccs6Hs/btruuhZ7YPs/o6UrUL7bpLuk2rm8VelC8K/img.png&quot; data-alt=&quot;y의 증가량&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccs6Hs/btruuhZ7YPs/o6UrUL7bpLuk2rm8VelC8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fccs6Hs%2FbtruuhZ7YPs%2Fo6UrUL7bpLuk2rm8VelC8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;205&quot; height=&quot;41&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.08.47.png&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;52&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;y의 증가량&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;y토큰의 증가한 양은 수수료를 빼서 위 식과 같이 계산이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.12.44.png&quot; data-origin-width=&quot;230&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buiLMC/btruqEIwbj6/9XjguegsEAS1uYBdyM8tpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buiLMC/btruqEIwbj6/9XjguegsEAS1uYBdyM8tpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buiLMC/btruqEIwbj6/9XjguegsEAS1uYBdyM8tpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuiLMC%2FbtruqEIwbj6%2F9XjguegsEAS1uYBdyM8tpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;230&quot; height=&quot;76&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.12.44.png&quot; data-origin-width=&quot;230&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 x도 위 식처럼 계산이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스왑시에는 L이 일정하고, 이때&amp;nbsp; price와 x, y와의 관계식을 사용하면 x및 y변화량을 L과 P로 나타낼 수 있다.&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;스크린샷 2022-02-27 오후 4.16.33.png&quot; data-origin-width=&quot;208&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvyTL4/btruxkB4g1m/QDGpww8dkdWduE3FfPbbyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvyTL4/btruxkB4g1m/QDGpww8dkdWduE3FfPbbyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvyTL4/btruxkB4g1m/QDGpww8dkdWduE3FfPbbyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvyTL4%2FbtruxkB4g1m%2FQDGpww8dkdWduE3FfPbbyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;175&quot; height=&quot;59&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.16.33.png&quot; data-origin-width=&quot;208&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.16.42.png&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXFuT3/btruvtF2LDX/htZmczcOPNe5Jc6nhyhlY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXFuT3/btruvtF2LDX/htZmczcOPNe5Jc6nhyhlY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXFuT3/btruvtF2LDX/htZmczcOPNe5Jc6nhyhlY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXFuT3%2FbtruvtF2LDX%2FhtZmczcOPNe5Jc6nhyhlY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;191&quot; height=&quot;49&quot; data-filename=&quot;스크린샷 2022-02-27 오후 4.16.42.png&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 코드에서 확인해보자&lt;/p&gt;
&lt;pre id=&quot;code_1645946437847&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// v3-periphery SwapRouter.sol

/// @inheritdoc ISwapRouter
    function exactInputSingle(ExactInputSingleParams calldata params)
        external
        payable
        override
        checkDeadline(params.deadline)
        returns (uint256 amountOut)
    {
        amountOut = exactInputInternal(
            params.amountIn,
            params.recipient,
            params.sqrtPriceLimitX96,
            SwapCallbackData({path: abi.encodePacked(params.tokenIn, params.fee, params.tokenOut), payer: msg.sender})
        );
        require(amountOut &amp;gt;= params.amountOutMinimum, 'Too little received');
    }
    
    /// @dev Performs a single exact input swap
    function exactInputInternal(
        uint256 amountIn,
        address recipient,
        uint160 sqrtPriceLimitX96,
        SwapCallbackData memory data
    ) private returns (uint256 amountOut) {
        // allow swapping to the router address with address 0
        if (recipient == address(0)) recipient = address(this);

        (address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();

        bool zeroForOne = tokenIn &amp;lt; tokenOut;

        (int256 amount0, int256 amount1) =
            getPool(tokenIn, tokenOut, fee).swap(
                recipient,
                zeroForOne,
                amountIn.toInt256(),
                sqrtPriceLimitX96 == 0
                    ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
                    : sqrtPriceLimitX96,
                abi.encode(data)
            );

        return uint256(-(zeroForOne ? amount1 : amount0));
    }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1645947313322&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// v3 core UniswapV3Pool.sol

function swap(
    address recipient,
    bool zeroForOne,
    int256 amountSpecified,
    uint160 sqrtPriceLimitX96,
    bytes calldata data
) external override noDelegateCall returns (int256 amount0, int256 amount1) {
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;recipient&lt;/b&gt; : swap을 시도한 from address&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;zeroForOne&lt;/b&gt; : Token0이 Token1로 바뀌는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;amountSpecified&lt;/b&gt; : convert될 양&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sqrtPriceLimitX96&lt;/b&gt; : price의 upper limit&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;swap function의 body에 다음 부분을 확인할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1645947600258&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// compute values to swap to the target tick, price limit, or point where input/output amount is exhausted
            (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
                state.sqrtPriceX96,
                (zeroForOne ? step.sqrtPriceNextX96 &amp;lt; sqrtPriceLimitX96 : step.sqrtPriceNextX96 &amp;gt; sqrtPriceLimitX96)
                    ? sqrtPriceLimitX96
                    : step.sqrtPriceNextX96,
                state.liquidity,
                state.amountSpecifiedRemaining,
                fee
            );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;computeSwapStep 함수를 보자&lt;/p&gt;
&lt;pre id=&quot;code_1645947817194&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive
    /// @param sqrtRatioCurrentX96 The current sqrt price of the pool
    /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred
    /// @param liquidity The usable liquidity
    /// @param amountRemaining How much input or output amount is remaining to be swapped in/out
    /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip
    /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target
    /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap
    /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap
    /// @return feeAmount The amount of input that will be taken as a fee
function computeSwapStep(
        uint160 sqrtRatioCurrentX96,
        uint160 sqrtRatioTargetX96,
        uint128 liquidity,
        int256 amountRemaining,
        uint24 feePips
    )
        internal
        pure
        returns (
            uint160 sqrtRatioNextX96,
            uint256 amountIn,
            uint256 amountOut,
            uint256 feeAmount
        )
    {
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;computeSwapStep에서는 다시 SqrtPriceMath의 getAmount0Delta 함수와 getNextSqrtPriceFromOutput 함수 등을 호출한다.&lt;/p&gt;
&lt;pre id=&quot;code_1645948221062&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    /// @notice Gets the amount0 delta between two prices
    /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
    /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))
    /// @param sqrtRatioAX96 A sqrt price
    /// @param sqrtRatioBX96 Another sqrt price
    /// @param liquidity The amount of usable liquidity
    /// @param roundUp Whether to round the amount up or down
    /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices
    function getAmount0Delta(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity,
        bool roundUp
    ) internal pure returns (uint256 amount0) {
    ...
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getAmount0Delta 함수의 주석 부분을 보면 &lt;b&gt;liquidity / sqrt(lower) - liquidity / sqrt(upper) 부분을 볼 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이는 백서에서 나온 식과 같다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그 이후까지 들어가면 너무 복잡해져서 생략..)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Position&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;position은 lowerTick과&amp;nbsp; upperTick으로 구성된다.&lt;/p&gt;
&lt;pre id=&quot;code_1645949923046&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// @inheritdoc INonfungiblePositionManager
    function mint(MintParams calldata params)
        external
        payable
        override
        checkDeadline(params.deadline)
        returns (
            uint256 tokenId,
            uint128 liquidity,
            uint256 amount0,
            uint256 amount1
        )
    {
        IUniswapV3Pool pool;
        (liquidity, amount0, amount1, pool) = addLiquidity(
            AddLiquidityParams({
                token0: params.token0,
                token1: params.token1,
                fee: params.fee,
                recipient: address(this),
                tickLower: params.tickLower,
                tickUpper: params.tickUpper,
                amount0Desired: params.amount0Desired,
                amount1Desired: params.amount1Desired,
                amount0Min: params.amount0Min,
                amount1Min: params.amount1Min
            })
        );
        
        _mint(params.recipient, (tokenId = _nextId++));

        bytes32 positionKey = PositionKey.compute(address(this), params.tickLower, params.tickUpper);
        (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);

        // idempotent set
        uint80 poolId =
            cachePoolKey(
                address(pool),
                PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee})
            );

        _positions[tokenId] = Position({
            nonce: 0,
            operator: address(0),
            poolId: poolId,
            tickLower: params.tickLower,
            tickUpper: params.tickUpper,
            liquidity: liquidity,
            feeGrowthInside0LastX128: feeGrowthInside0LastX128,
            feeGrowthInside1LastX128: feeGrowthInside1LastX128,
            tokensOwed0: 0,
            tokensOwed1: 0
        });

        emit IncreaseLiquidity(tokenId, liquidity, amount0, amount1);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;periphery&lt;/b&gt;의 &lt;b&gt;NonfungiblePositionManager&lt;/b&gt;의 mint 부분을 보면 포지션을 민팅하는 함수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;addliquidity&lt;/b&gt; 함수를 보면 core 로직의 pool의 민트 함수를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;민트 함수는 &lt;b&gt;_modifyPosition&lt;/b&gt; 함수를 호출한다.&lt;/p&gt;
&lt;pre id=&quot;code_1645950816216&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    /// @dev Effect some changes to a position
    /// @param params the position details and the change to the position's liquidity to effect
    /// @return position a storage pointer referencing the position with the given owner and tick range
    /// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient
    /// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient
    function _modifyPosition(ModifyPositionParams memory params)
        private
        noDelegateCall
        returns (
            Position.Info storage position,
            int256 amount0,
            int256 amount1
        )
    {
    ...
    position = _updatePosition(
            params.owner,
            params.tickLower,
            params.tickUpper,
            params.liquidityDelta,
            _slot0.tick
        );
    ...
    if (params.liquidityDelta != 0) {
            if (_slot0.tick &amp;lt; params.tickLower) {
                // current tick is below the passed range; liquidity can only become in range by crossing from left to
                // right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it
                ...
             } else if (_slot0.tick &amp;lt; params.tickUpper) {
             ...
             liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);
             }
      ...
      }
   }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포지션을 업데이트하고 token0, token1 및 liquidity를 업데이트 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재의 틱이 파라미터의 lower tick과&amp;nbsp; upper tick 사이인지, 아니인지에 따라 계산식이 달라진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. Swap 코드 살펴보기 - Tick-Indexed State&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;스크린샷 2022-03-03 오후 9.46.23.png&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MgbcN/btruZQ1RcXP/7lZQPlWVxCNiUnqIhWEEKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MgbcN/btruZQ1RcXP/7lZQPlWVxCNiUnqIhWEEKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MgbcN/btruZQ1RcXP/7lZQPlWVxCNiUnqIhWEEKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMgbcN%2FbtruZQ1RcXP%2F7lZQPlWVxCNiUnqIhWEEKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;443&quot; data-filename=&quot;스크린샷 2022-03-03 오후 9.46.23.png&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;443&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;pre id=&quot;code_1646311765131&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// v3-core/contracts/interface/poo/IUniswapV3PoolState.sol

interface IUniswapV3PoolState {
   ...
   function ticks(int24 tick)
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint256 feeGrowthOutside0X128,
            uint256 feeGrowthOutside1X128,
            int56 tickCumulativeOutside,
            uint160 secondsPerLiquidityOutsideX128,
            uint32 secondsOutside,
            bool initialized
        );
        ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646312160499&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// v3-core/contracts/UniswapV3Pool.sol

contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
  ...
  /// @inheritdoc IUniswapV3PoolState
  mapping(int24 =&amp;gt; Tick.Info) public override ticks;
  /// @inheritdoc IUniswapV3PoolState
  mapping(int16 =&amp;gt; uint256) public override tickBitmap;
  /// @inheritdoc IUniswapV3PoolState
  mapping(bytes32 =&amp;gt; Position.Info) public override positions;
  /// @inheritdoc IUniswapV3PoolState
  Oracle.Observation[65535] public override observations;
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Uniswap의 pool은 interface인 IUniswapV3PoolState을 implement하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UniswapV3Pool이 int24 tick을 받아 Tick.Info를 반환하는 mapping을 가지고 있음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 tick마다 7개의 tick에 관련된 변수를 트래킹하고 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- LiquidityNet : 틱을 지나갈 때 출입할 유동성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- LiquidityGross : 누적 유동성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- feeGrowthOutside : 주어진 구간에 누적된 fee -&amp;gt; 단위 유동성당 얻은 fee를 계산 가능 (자세한 식은 백서에)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- secondOutside, tickCumulativeOutside, secondsPerLiquidityOutsideX128 : 컨트랙트 내부에서 쓰는 것 아님. 외부 함수를 위해 제공&lt;/p&gt;</description>
      <category>블록체인/오픈소스</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/111</guid>
      <comments>https://woojinger.tistory.com/111#entry111comment</comments>
      <pubDate>Thu, 3 Mar 2022 23:58:37 +0900</pubDate>
    </item>
    <item>
      <title>Map : concurrent 상황일 때 Read도 RLock을 해야 하는 이유</title>
      <link>https://woojinger.tistory.com/110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;레퍼런스&lt;/b&gt;&lt;/p&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;a href=&quot;https://dave.cheney.net/2015/12/07/are-go-maps-sensitive-to-data-races&quot;&gt;https://dave.cheney.net/2015/12/07/are-go-maps-sensitive-to-data-races&lt;/a&gt;&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/19148809/how-to-use-rwmutex&quot;&gt;https://stackoverflow.com/questions/19148809/how-to-use-rwmutex&lt;/a&gt;&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문제 상황&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상황을 한번 가정해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아무 key-value도 가지지 않는 Map을 가지고 있다고 해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 Map에 수많은 고루틴들이 랜덤으로 key를 넣어 Read를 시도한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 value가 있으면 그대로 가져오고, value가 없다면 랜덤으로 만든 value를 key-value 쌍으로 만들어 Map에 넣는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉 각 고루틴은&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Read를 시도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 없으면 Write&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;을 반복한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그렇다면 여기서 Write을 할 때는 Lock을 거는 것이 확실하다. 그런데 Read를 할 때도 Lock을 걸어야 할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉, 한 고루틴이 Map[0]을 읽고 있는데 동시에 다른 고루틴이 Map[1]에 value를 write을 하는 것이 영향을 끼칠 수 있을까?&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Map의 구조&lt;/span&gt;&lt;/h2&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Go Map은 채널이나 슬라이스보다 훨씬 복잡하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단순히 backing array를 보는 것이 아니라 internal state나 map iterator 등을 가지고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Map은 reference variable이 아닌 runtime.hmapstructure에 대한 포인터이다.&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;Screen-Shot-2018-05-20-at-20.25.36-300x169.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tgaXC/btrtBURGZus/U1cwsaR7WfLBEFkREgu571/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tgaXC/btrtBURGZus/U1cwsaR7WfLBEFkREgu571/img.png&quot; data-alt=&quot;https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tgaXC/btrtBURGZus/U1cwsaR7WfLBEFkREgu571/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtgaXC%2FbtrtBURGZus%2FU1cwsaR7WfLBEFkREgu571%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;264&quot; data-filename=&quot;Screen-Shot-2018-05-20-at-20.25.36-300x169.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HashMap의 load factor가 일정 수준을 증가하면 HashMap의 성능이 줄어든다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 Go의 빌트인 자료구조들 중 오직 Map만 data를 내부적으로 움직인다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Map은 O(1)을 보장하기 위해 insert나 delete시 리밸런스를 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 이유 때문에 Map의 value는 addressable하지 않다. (addressable이란? &lt;a href=&quot;https://go.dev/ref/spec#Address_operators&quot;&gt;https://go.dev/ref/spec#Address_operators&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예를 들어 Map[1] = 2를 write하고, a = &amp;amp;Map[1]로 할당했다고 하자. 그럼 우리는 *a가 2의 값을 가질 것이라 생각할 것이다. 그러나 리밸런스가 되면 이것이 2를 가리키지 않을 수 있다는 것이다. Map[1]은 2를 여전히 가리키고 있겠지만.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그렇다면 어떻게 써야할까?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. RWMutex를 이용하여 Map에서 Read를 할 때 RLock을 걸자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. go 1.9버전 이후에는 sync.Map을 이용할 수 있다. 이걸 사용하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>언어/Go</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/110</guid>
      <comments>https://woojinger.tistory.com/110#entry110comment</comments>
      <pubDate>Fri, 18 Feb 2022 00:49:09 +0900</pubDate>
    </item>
    <item>
      <title>Truffle과 KAS 연동(클레이튼)</title>
      <link>https://woojinger.tistory.com/109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Reference&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.docs.klaytn.com/smart-contract/ide-and-tools/truffle&quot;&gt;https://ko.docs.klaytn.com/smart-contract/ide-and-tools/truffle&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;truffle을 클레이튼과 연동하기 위해서 truffle의 truffle-config.js의 provider을 작성해주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자신이 로컬 엔드포인트 노드를 실행해 연동할 수도 있지만 관리하기 쉽지 않으므로 KAS와 연동하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;KAS를 사용하려면 먼저 AccessKey ID와 Secret AccessKey를 발급받아야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 과정은 어렵지 않으므로 패스하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Truffle을 사용해 스마트 컨트랙트를 배포할 것이므로 truffle을 설치한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643798979998&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo npm install -g truffle&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 klaytn docs에서는 v5.0.26까지 지원한다고 하므로 해당 버전에 맞춰서 설치한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;truffle을 설치하고 프로젝트 폴더에 init을 하면 여러 폴더들이 생긴다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643799143837&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ truffle init

...

$ ls
build             contracts         test
migrations        truffle-config.js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;truffle-config.js는 사용할 provider를 결정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;KAS를 이용해 provider를 작성하려면 추가적인 라이브러리(truffle-hdwallet-provider-klaytn, caver-js)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643799292104&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ yarn install truffle-hdwallet-provider-klaytn@1.4.1
$ yarn install caver-js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;truffle-config.js를 작성하자&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643799335742&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const {privateKey, accessKeyId, secretAccessKey } = require('./constants/index.js');
const Caver = require('caver-js');
const HDWalletProvider = require(&quot;truffle-hdwallet-provider-klaytn&quot;);

module.exports = {
  networks: {
    kasBaobab: {
      provider: () =&amp;gt; {
        const option = {
          headers: [
            {
              name: &quot;Authorization&quot;,
              value:
                &quot;Basic &quot; +
                Buffer.from(accessKeyId + &quot;:&quot; + secretAccessKey).toString(
                  &quot;base64&quot;
                ),
            },
            { name: &quot;x-chain-id&quot;, value: &quot;1001&quot; },
          ],
          keepAlive: false,
        };
        return new HDWalletProvider(
          privateKey,
          new Caver.providers.HttpProvider(
            &quot;https://node-api.klaytnapi.com/v1/klaytn&quot;,
            option
          )
        );
      },
      network_id: &quot;1001&quot;, //Klaytn baobab testnet's network id
      gas: &quot;8500000&quot;,
      gasPrice: &quot;25000000000&quot;,
    },
    kasCypress: {
      provider: () =&amp;gt; {
        const option = {
          headers: [
            {
              name: &quot;Authorization&quot;,
              value:
                &quot;Basic &quot; +
                Buffer.from(accessKeyId + &quot;:&quot; + secretAccessKey).toString(
                  &quot;base64&quot;
                ),
            },
            { name: &quot;x-chain-id&quot;, value: &quot;8217&quot; },
          ],
          keepAlive: false,
        };
        return new HDWalletProvider(
          privateKey,
          new Caver.providers.HttpProvider(
            &quot;https://node-api.klaytnapi.com/v1/klaytn&quot;,
            option
          )
        );
      },
      network_id: &quot;8217&quot;, //Klaytn baobab testnet's network id
      gas: &quot;8500000&quot;,
      gasPrice: &quot;25000000000&quot;,
    },
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;kasBaobab과 kasCypress를 작성하였다. kasBaobab은 KAS를 이용해 baobab 테스트넷에 접속, kasCypress는 KAS를 이용해 Cypress 메인넷에 접속한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드에서 privateKey, accessKeyId, secretAccessKey가 사용된다. 하드코딩하지 않고 별도의 파일에 저장하여 사용하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;privateKey는 배포를 하는 주소의 privateKey를 말한다. 스마트 컨트랙트를 배포할 때 수수료가 차감되므로 해당 계정에는 충분한 양의 클레이가 있어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;accessKeyId, secretAccessKey는 KAS에서 발급 받은 key이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;provider의 option을 보면 baobab은 value가 1001, cypress는 8217인데 체인 ID를 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제 스마트 컨트랙트를 작성하고 체인을 골라 배포할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643799807547&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Cypress에 배포
$ truffle deploy --network kasCypress
// Baobab에 배포
$ truffle deploy --network kasBaobab&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Cypress에 배포하는 경우 다음과 같이 터미널에 출력된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643799850141&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Compiling your contracts...
===========================
&amp;gt; Everything is up to date, there is nothing to compile.


Starting migrations...
======================
&amp;gt; Network name:    'kasCypress'
&amp;gt; Network id:      8217
&amp;gt; Block gas limit: 0x0


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   &amp;gt; transaction hash:    0x6e929ebeaea886d2612f90fe9af9e3d009c6fe453df652716e9960d494a87e37
   &amp;gt; Blocks: 0            Seconds: 0
   &amp;gt; contract address:    0x84500Ce85ec97F3Da6E81fba0E97776cF3AaAFb1
   &amp;gt; block number:        81949912
   &amp;gt; block timestamp:     1643799495
   &amp;gt; account:             0xba27e4f32Fdf885368e0C51801C26FF0852ab609
   &amp;gt; balance:             0.72001375
   &amp;gt; gas used:            219143
   &amp;gt; gas price:           25 gwei
   &amp;gt; value sent:          0 ETH
   &amp;gt; total cost:          0.005478575 ETH


   Deploying 'KlaytnGreeter'
   -------------------------
   &amp;gt; transaction hash:    0xa3fa0564d37a8822299b78ff52ef55dea88a1735659fd0192d7794349b98b8bd
   &amp;gt; Blocks: 0            Seconds: 0
   &amp;gt; contract address:    0x23168f59a877FA177Ea1c6Ae35D4bBB37e769685
   &amp;gt; block number:        81949916
   &amp;gt; block timestamp:     1643799499
   &amp;gt; account:             0xba27e4f32Fdf885368e0C51801C26FF0852ab609
   &amp;gt; balance:             0.71332875
   &amp;gt; gas used:            267400
   &amp;gt; gas price:           25 gwei
   &amp;gt; value sent:          0 ETH
   &amp;gt; total cost:          0.006685 ETH


   &amp;gt; Saving migration to chain.
   &amp;gt; Saving artifacts
   -------------------------------------
   &amp;gt; Total cost:         0.012163575 ETH


Summary
=======
&amp;gt; Total deployments:   2
&amp;gt; Final cost:          0.012163575 ETH&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;먼저 스마트 컨트랙트가 컴파일이 되는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 이후 컴파일된 스마트 컨트랙트들이 배포가 되고 transaction hash 및 contract address가 출력된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배포하는데 사용된 cost도 확인할 수 있는데, Klay가 아닌 ETH로 표기되는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이는 truffle이 ETH를 배포하기 위해 사용되었기 때문에 표기만 그런 것으로 추측된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;실제로 해당 트랜잭션 해시로 klaytn scope에 검색해보면 Klay로 표시된 것을 볼 수 있다.&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;스크린샷 2022-02-02 오후 8.06.23.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brOuam/btrsnn7YDBg/Aqy1FFthYlmH3p4fOk0Y3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brOuam/btrsnn7YDBg/Aqy1FFthYlmH3p4fOk0Y3k/img.png&quot; data-alt=&quot;Klaytn Scope&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brOuam/btrsnn7YDBg/Aqy1FFthYlmH3p4fOk0Y3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrOuam%2Fbtrsnn7YDBg%2FAqy1FFthYlmH3p4fOk0Y3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;554&quot; data-filename=&quot;스크린샷 2022-02-02 오후 8.06.23.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Klaytn Scope&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이로서 Klaytn IDE를 사용하지 않고 스마트 컨트랙트를 배포할 수 있게 되었다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>블록체인/솔리디티</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/109</guid>
      <comments>https://woojinger.tistory.com/109#entry109comment</comments>
      <pubDate>Wed, 2 Feb 2022 20:07:33 +0900</pubDate>
    </item>
    <item>
      <title>mongdoDB 내부 캐시</title>
      <link>https://woojinger.tistory.com/108</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/core/wiredtiger/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.mongodb.com/manual/core/wiredtiger/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643291377441&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;WiredTiger Storage Engine &amp;mdash; MongoDB Manual&quot; data-og-description=&quot;Docs Home &amp;rarr; MongoDB ManualStarting in MongoDB 3.2, the WiredTiger storage engine is the default storage engine. For existing deployments, if you do not specify the --storageEngine or the storage.engine setting, the version 3.2+ mongod instance can automa&quot; data-og-host=&quot;docs.mongodb.com&quot; data-og-source-url=&quot;https://docs.mongodb.com/manual/core/wiredtiger/&quot; data-og-url=&quot;https://docs.mongodb.com/manual/core/wiredtiger/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Y9PtM/hyNdjdVR5a/QnfARBWbEkKiE4xomLHQJ1/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/ew6cr/hyNdkcRc06/mSekoM7kYKU9clqPYuGdp0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/core/wiredtiger/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.mongodb.com/manual/core/wiredtiger/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Y9PtM/hyNdjdVR5a/QnfARBWbEkKiE4xomLHQJ1/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/ew6cr/hyNdkcRc06/mSekoM7kYKU9clqPYuGdp0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601');&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;WiredTiger Storage Engine &amp;mdash; MongoDB Manual&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docs Home &amp;rarr; MongoDB ManualStarting in MongoDB 3.2, the WiredTiger storage engine is the default storage engine. For existing deployments, if you do not specify the --storageEngine or the storage.engine setting, the version 3.2+ mongod instance can automa&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.mongodb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WiredTiger Storage Engine&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WiredTiger 엔진은 mongoDB 3.2 버전부터 사용한 기본 엔진이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;몽고DB는 WiredTiger 내부 캐시와 파일 시스템의 캐시를 둘 다 사용한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파일 시스템 캐시를 통해 WiredTiger 캐시나 다른 프로세스가 사용하지 않는 모든 free 메모리를 몽고DB는 사용한다.&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;파일 시스템 캐시는 운영체제가 디스크 I/O를 줄이기 위해서 캐시를 하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 시스템 캐시는 운영체제가 관리하는 것이므로 이 포스팅에서는 다루지 않겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WiredTiger의 default 내부 캐시는 (RAM - 1GB)/2 와 256MB 중 큰 사이즈로 설정이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에서는 기본 값 이상으로 내부 캐시 사이즈 크기를 증가시키는 것을 권장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WiredTiger는 collection과 인덱스를 압축해서 저장을 하고 있는데, 내부 캐시에서 사용을 할 때는 압축이 되어 있지 않은 상태로 사용을 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/reference/program/mongod/#wiredtiger-options&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.mongodb.com/manual/reference/program/mongod/#wiredtiger-options&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643292509760&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;mongod &amp;mdash; MongoDB Manual&quot; data-og-description=&quot;Docs Home &amp;rarr; MongoDB Manualmongod is the primary daemon process for the MongoDB system. It handles data requests, manages data access, and performs background management operations.This document provides a complete overview of all command line options for&quot; data-og-host=&quot;docs.mongodb.com&quot; data-og-source-url=&quot;https://docs.mongodb.com/manual/reference/program/mongod/#wiredtiger-options&quot; data-og-url=&quot;https://docs.mongodb.com/manual/reference/program/mongod/#wiredtiger-options&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/XI8Oi/hyNdqYsAjy/hON9LaLu0tYR1bzYNNsYIk/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/G1nql/hyNdlphK0b/K7NWkpTFgK2pd1o3aC3XKK/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/reference/program/mongod/#wiredtiger-options&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.mongodb.com/manual/reference/program/mongod/#wiredtiger-options&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/XI8Oi/hyNdqYsAjy/hON9LaLu0tYR1bzYNNsYIk/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/G1nql/hyNdlphK0b/K7NWkpTFgK2pd1o3aC3XKK/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601');&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;mongod &amp;mdash; MongoDB Manual&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docs Home &amp;rarr; MongoDB Manualmongod is the primary daemon process for the MongoDB system. It handles data requests, manages data access, and performs background management operations.This document provides a complete overview of all command line options for&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.mongodb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고디비 서버를 띄울 시 옵션을 주어 내부 캐시 사이즈를 변경해서 올릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;리눅스 쉘에서 사용하고 있는 메모리 확인&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1643292711070&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;free
// -b, -k, -m, -g : 바이트, KB, MB, GB 단위로 출력
// -h : human readable
// -s : 몇 초마다 print를 할 것인지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB를 띄운 장비에서 메모리 확인을 해보면 생각보다 많은 메모리를 사용하고 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WiredTiger 캐시가 default 설정으로 메모리 절반을 사용할 수 있기 때문에 오래 몽고DB를 사용했다면 메모리 사용량이 꽤 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;몽고DB 쉘에서 WiredTiger 캐시 메모리 확인&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1643293140935&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.serverStatus().wiredTiger.cache&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고DB 쉘에서 다음 명령을 입력하면 wiredTiger cache에 대한 정보가 나온다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시에 들어가 있는 바이트 : &quot;bytes currently in the cache&quot;&lt;/li&gt;
&lt;li&gt;캐시가 수용할 수 있는 최대 바이트 : &quot;maximum bytes configured&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;몽고DB 쉘에서 현재 컬렉션의 데이터 사이즈 확인&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/reference/method/db.collection.stats/#definition&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.mongodb.com/manual/reference/method/db.collection.stats/#definition&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643293322213&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.collection.stats()
// collection은 원하는 collection 이름&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;storageSize : 컬렉션에 할당된 document 스토리지 byte. 기본 설정에 따라 압축된 사이즈이다&lt;/li&gt;
&lt;li&gt;Size : 컬렉션의 모든 레코드가 압축되지 않았을 때의 사이즈. 컬렉션의 인덱스 사이즈는 포함하지 않는다.&lt;/li&gt;
&lt;li&gt;totalIndexSize : 모든 인덱스 사이즈 크기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WiredTiger 캐시 메모리와 자신의 레코드들의 크기를 비교하면 page fault가 발생할지 안할지 대략적으로 판단을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;간단한 실험&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WiredTiger 캐시에 아무것도 캐싱이 되지 않았을 때와 한번 find를 한 후의 캐시를 비교하는 실험이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 상태에서 다시 find를 했을 때 성능을 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 6000만개의 데이터 셋 중 특정 조건을 만족하는 레코드들을 가져오는 find로 수행하였으며, 성능이 어느 정도 차이가 나는지 비교하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실험은 쉘에서 한 것이 아니라 어플리케이션에서 find를 하고 decode하는 시간까지 포함되어 있어 실행 시간이 훨씬 크게 잡혀있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;캐싱이 되지 않았을 때&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 전 캐시 메모리 :&amp;nbsp; 1.27MB&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 시간 : 10.1s&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 후 캐시 메모리 : 1.5GB&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;캐싱이 한번 되었을 때&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 전 캐시 메모리 : 1.5GB&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 시간 : 9.2s&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 후 캐시 메모리 : 1.5GB&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;</description>
      <category>로드맵/DB</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/108</guid>
      <comments>https://woojinger.tistory.com/108#entry108comment</comments>
      <pubDate>Thu, 27 Jan 2022 23:31:40 +0900</pubDate>
    </item>
    <item>
      <title>mongoDB - 배열 인덱싱</title>
      <link>https://woojinger.tistory.com/107</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/core/index-multikey/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.mongodb.com/manual/core/index-multikey/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643119039140&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;Multikey Indexes &amp;mdash; MongoDB Manual&quot; data-og-description=&quot;Docs Home &amp;rarr; MongoDB ManualTo index a field that holds an array value, MongoDB creates an index key for each element in the array. These multikey indexes support efficient queries against array fields. Multikey indexes can be constructed over arrays that &quot; data-og-host=&quot;docs.mongodb.com&quot; data-og-source-url=&quot;https://docs.mongodb.com/manual/core/index-multikey/&quot; data-og-url=&quot;https://docs.mongodb.com/manual/core/index-multikey/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Sb6tV/hyNdePIPrr/azNRQTNXvuyPWcePPIHAp1/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/OEOwG/hyNdoSkNOS/rEduHHRHwukL0N9YP65N7K/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/core/index-multikey/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.mongodb.com/manual/core/index-multikey/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Sb6tV/hyNdePIPrr/azNRQTNXvuyPWcePPIHAp1/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/OEOwG/hyNdoSkNOS/rEduHHRHwukL0N9YP65N7K/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601');&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;Multikey Indexes &amp;mdash; MongoDB Manual&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docs Home &amp;rarr; MongoDB ManualTo index a field that holds an array value, MongoDB creates an index key for each element in the array. These multikey indexes support efficient queries against array fields. Multikey indexes can be constructed over arrays that&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.mongodb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/tutorial/query-arrays/#std-label-array-match-exact&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.mongodb.com/manual/tutorial/query-arrays/#std-label-array-match-exact&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643119048427&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;Query an Array &amp;mdash; MongoDB Manual&quot; data-og-description=&quot;Docs Home &amp;rarr; MongoDB Manual➤ Use the Select your language drop-down menu in the upper-right to set the language of the following examples.The following example queries for all documents where the field tags value is an array with exactly two elements, &amp;quot;&quot; data-og-host=&quot;docs.mongodb.com&quot; data-og-source-url=&quot;https://docs.mongodb.com/manual/tutorial/query-arrays/#std-label-array-match-exact&quot; data-og-url=&quot;https://docs.mongodb.com/manual/tutorial/query-arrays/#std-label-array-match-exact&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hR9lt/hyNdhZYDnA/OZLHumZnjyvXHnjVKke3mK/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/b0ka5E/hyNdpX1lmM/O8MUezZUPj6WKbVoe5FhL0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601&quot;&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/tutorial/query-arrays/#std-label-array-match-exact&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.mongodb.com/manual/tutorial/query-arrays/#std-label-array-match-exact&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hR9lt/hyNdhZYDnA/OZLHumZnjyvXHnjVKke3mK/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/b0ka5E/hyNdpX1lmM/O8MUezZUPj6WKbVoe5FhL0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601');&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;Query an Array &amp;mdash; MongoDB Manual&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docs Home &amp;rarr; MongoDB Manual➤ Use the Select your language drop-down menu in the upper-right to set the language of the following examples.The following example queries for all documents where the field tags value is an array with exactly two elements, &quot;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.mongodb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/97980005&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://www.yes24.com/Product/Goods/97980005&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643119080327&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;book&quot; data-og-title=&quot;몽고DB 완벽 가이드 - YES24&quot; data-og-description=&quot;NoSQL의 진수 몽고DB 개발부터 관리까지몽고DB 입문자를 위한 기초부터 실제 배포에 적용할 수 있는 실용적이고 깊이 있는 내용까지 담았다. 개정 3판에서는 성능이 강화된 몽고DB 최신 버전을 반&quot; data-og-host=&quot;www.yes24.com&quot; data-og-source-url=&quot;http://www.yes24.com/Product/Goods/97980005&quot; data-og-url=&quot;http://www.yes24.com/Product/Goods/97980005&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cemq41/hyNdr9nv6O/Qgh5jmMzxXnpTk6hbtoD4k/img.jpg?width=934&amp;amp;height=1200&amp;amp;face=0_0_934_1200,https://scrap.kakaocdn.net/dn/AAzYt/hyNdjKg1Df/y0l8kDGvW81r25z3CHhF11/img.jpg?width=934&amp;amp;height=1200&amp;amp;face=0_0_934_1200&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/97980005&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.yes24.com/Product/Goods/97980005&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cemq41/hyNdr9nv6O/Qgh5jmMzxXnpTk6hbtoD4k/img.jpg?width=934&amp;amp;height=1200&amp;amp;face=0_0_934_1200,https://scrap.kakaocdn.net/dn/AAzYt/hyNdjKg1Df/y0l8kDGvW81r25z3CHhF11/img.jpg?width=934&amp;amp;height=1200&amp;amp;face=0_0_934_1200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;몽고DB 완벽 가이드 - YES24&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;NoSQL의 진수 몽고DB 개발부터 관리까지몽고DB 입문자를 위한 기초부터 실제 배포에 적용할 수 있는 실용적이고 깊이 있는 내용까지 담았다. 개정 3판에서는 성능이 강화된 몽고DB 최신 버전을 반&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.yes24.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mongoDB 스키마를 짤 때 고민이 되는 지점이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 필드를 이용해서 검색을 해야 하는 상황이라고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 필드를 따로 분리해서 인덱싱하는 것과 배열에 넣어서 인덱싱하는 것이 큰 차이가 있을까?&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre id=&quot;code_1643119301460&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ _id: 1, key1: &quot;value1&quot;, key2: &quot;value2&quot; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 형식을 가진 컬렉션과&lt;/p&gt;
&lt;pre id=&quot;code_1643119327809&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ _id: 1, key: [ &quot;value1&quot;,&quot;value2&quot; ] }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 배열로 값들을 가지고 있는 컬렉션에 대해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;value1&quot;과 &quot;value2&quot;를 가지고 있는 문서를 find할 때 속도가 차이가 있는지 궁금했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 큰 차이가 없다면 아래의 경우 필드를 추가할 때 확장하기가 용이하고(위 컬렉션의 경우 key3 필드를 추가로 만들어야 한다), 클라이언트가 쿼리를 날리기에도 깔끔하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-01-25 오후 11.11.31.png&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bME2DH/btrrDx6D8ED/dWW09BxQfk7gY9onfoLuAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bME2DH/btrrDx6D8ED/dWW09BxQfk7gY9onfoLuAk/img.png&quot; data-alt=&quot;https://docs.mongodb.com/manual/core/index-multikey/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bME2DH/btrrDx6D8ED/dWW09BxQfk7gY9onfoLuAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbME2DH%2FbtrrDx6D8ED%2FdWW09BxQfk7gY9onfoLuAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;773&quot; height=&quot;532&quot; data-filename=&quot;스크린샷 2022-01-25 오후 11.11.31.png&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.mongodb.com/manual/core/index-multikey/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배열 필드 인덱싱은 배열 자체가 아니라 배열의 각 요소를 인덱싱한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 보면 조금 더 쉽게 이해를 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에 대해 정확한 match를 위한 쿼리를 수행할 때, &lt;b&gt;mongoDB는 다중키 인덱스를 query array의 첫번째 원소에 대해서는 사용하지만 전체 배열을 찾기 위해서는 사용할 수 없다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 배열의&lt;b&gt; 첫번째 원소를 찾기 위해 다중키 인덱스를 사용한 후에 쿼리의 배열과 매치되는 문서를 찾는다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 무슨말인지 다음 예를 따라가 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1643119693212&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ _id: 5, type: &quot;food&quot;, item: &quot;aaa&quot;, ratings: [ 5, 8, 9 ] }
{ _id: 6, type: &quot;food&quot;, item: &quot;bbb&quot;, ratings: [ 5, 9 ] }
{ _id: 7, type: &quot;food&quot;, item: &quot;ccc&quot;, ratings: [ 9, 5, 8 ] }
{ _id: 8, type: &quot;food&quot;, item: &quot;ddd&quot;, ratings: [ 9, 5 ] }
{ _id: 9, type: &quot;food&quot;, item: &quot;eee&quot;, ratings: [ 5, 9, 5 ] }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 5개의 문서가 존재하는 상황이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 다음과 같이 인덱스를 만들었다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1643119734362&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.inventory.createIndex( { ratings: 1 } )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이 상황에서 다음의 쿼리를 날려보자.&lt;/p&gt;
&lt;pre id=&quot;code_1643119763794&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.inventory.find( { ratings: [ 5, 9 ] } )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그러면 mongoDB는 다중키 인덱스로 ratings 배열에 5가 있는 문서를 먼저 찾는다&lt;/b&gt;. &lt;b&gt;즉, 쿼리의 첫번째 원소만 인덱스를 쓰는데 사용한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그 다음 얻은 문서들 중 [5, 9]와 일치하는 것을 필터링한다.&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;즉, 두 번째 원소부터는 Indexing이 큰 효력을 발휘하지 못한다고 예상할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실험 조건&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실험 조건은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1643121051310&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// collection1
{ _id: 1, key1: &quot;value1&quot;, key2: &quot;value2&quot; }
// collection2
{ _id: 1, key: [ &quot;value1&quot;, &quot;value2&quot;] }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 대생 총 문서는 600만 개&lt;/li&gt;
&lt;li&gt;&quot;value1&quot;을 가지고 있는 문서 검색 실험 1번&lt;/li&gt;
&lt;li&gt;&quot;value1&quot;, &quot;value2&quot;을 가지고 있는 문서 검색 실험 2번&lt;/li&gt;
&lt;li&gt;collection1은 key1에 인덱스 한 개, key1, key2에 다중키 인덱스 한 개. 총 두 개의 인덱스를 만든다.&lt;/li&gt;
&lt;li&gt;colleciton2는 key에 인덱스 한 개를 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실험 결과&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;value1&quot; 검색 (필드 한 개 검색) - 검색 결과 문서 170만개&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collection1 : 0.033s&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collection2 : 0.16s&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;value1&quot;, &quot;value2&quot; 검색 (필드 두 개 검색) - 검색 결과 문서 56만개&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collection1 : 0.016s&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collection2 : 0.06s&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예상했던 대로 필드를 분리해서 인덱싱을 거는 것이 성능이 뛰어나다.(4~5배)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이정도 성능이면 크지 않은 규모의 어플리케이션에서는 배열을 인덱스로 사용해도 크게 차이가 나지 않을 것 같긴 하다&lt;/p&gt;</description>
      <category>로드맵/DB</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/107</guid>
      <comments>https://woojinger.tistory.com/107#entry107comment</comments>
      <pubDate>Tue, 25 Jan 2022 23:37:49 +0900</pubDate>
    </item>
    <item>
      <title>이더리움 개발 스택</title>
      <link>https://woojinger.tistory.com/106</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reference&lt;/h4&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;a href=&quot;https://ethereum.org/en/developers/docs/&quot;&gt;https://ethereum.org/en/developers/docs/&lt;/a&gt;
&lt;figure id=&quot;og_1641725566827&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Home | ethereum.org&quot; data-og-description=&quot;Ethereum is a global, decentralized platform for money and new kinds of applications. On Ethereum, you can write code that controls money, and build applications accessible anywhere in the world.&quot; data-og-host=&quot;ethereum.org&quot; data-og-source-url=&quot;https://ethereum.org/en/developers/docs/&quot; data-og-url=&quot;https://ethereum.org&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/caBt8a/hyM1NlRflB/6Kb1KQy3uCrKb79liG6riK/img.png?width=1200&amp;amp;height=505&amp;amp;face=0_0_1200_505,https://scrap.kakaocdn.net/dn/klCXP/hyM1BFIPdN/nm0D7Na21DHgmCxVgBwzqK/img.png?width=1200&amp;amp;height=505&amp;amp;face=0_0_1200_505&quot;&gt;&lt;a href=&quot;https://ethereum.org/en/developers/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ethereum.org/en/developers/docs/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/caBt8a/hyM1NlRflB/6Kb1KQy3uCrKb79liG6riK/img.png?width=1200&amp;amp;height=505&amp;amp;face=0_0_1200_505,https://scrap.kakaocdn.net/dn/klCXP/hyM1BFIPdN/nm0D7Na21DHgmCxVgBwzqK/img.png?width=1200&amp;amp;height=505&amp;amp;face=0_0_1200_505');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Home | ethereum.org&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ethereum is a global, decentralized platform for money and new kinds of applications. On Ethereum, you can write code that controls money, and build applications accessible anywhere in the world.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ethereum.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application&quot;&gt;https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1641725576555&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 Architecture of a Web 3.0 application&quot; data-og-description=&quot;The architecture of Web 3.0 applications are completely different from Web 2.0 applications. But all of that&amp;rsquo;s changing.Blockchain technology has unlocked an exciting new direction for Web 3.0 applications. In this article, we're going to focus on what t&quot; data-og-host=&quot;www.preethikasireddy.com&quot; data-og-source-url=&quot;https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application&quot; data-og-url=&quot;https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cKoJxk/hyM26RC8fm/EykUKPtTWqh2S5zg1OZuK0/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080,https://scrap.kakaocdn.net/dn/Y8ao0/hyM1H61mV1/LBiARFRcL6kET6K9rAxAT0/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080&quot;&gt;&lt;a href=&quot;https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cKoJxk/hyM26RC8fm/EykUKPtTWqh2S5zg1OZuK0/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080,https://scrap.kakaocdn.net/dn/Y8ao0/hyM1H61mV1/LBiARFRcL6kET6K9rAxAT0/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;The Architecture of a Web 3.0 application&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The architecture of Web 3.0 applications are completely different from Web 2.0 applications. But all of that&amp;rsquo;s changing.Blockchain technology has unlocked an exciting new direction for Web 3.0 applications. In this article, we're going to focus on what t&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.preethikasireddy.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ethereum Stack&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이더리움에서 web3는 블록체인에서 돌아가는 탈중앙화 앱을 가리킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;web3는 어플리케이션 상태를 저장하는 중앙화된 데이터베이스나 백엔드 로직이 존재하는 중앙화 웹서버도 없다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-01-09 오후 7.53.26.png&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DwvMl/btrqdS3DUv1/OkPWU44EI2d8MVBZbmlEEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DwvMl/btrqdS3DUv1/OkPWU44EI2d8MVBZbmlEEK/img.png&quot; data-alt=&quot;https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DwvMl/btrqdS3DUv1/OkPWU44EI2d8MVBZbmlEEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDwvMl%2FbtrqdS3DUv1%2FOkPWU44EI2d8MVBZbmlEEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;780&quot; data-filename=&quot;스크린샷 2022-01-09 오후 7.53.26.png&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;780&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;블록체인이 하나의 거대한 state machine으로서 프로그램의 상태를 저장하고, 스마트 컨트랙트가 블록체인에서 돌아가는 프로그램이자 로직을 정의하는 역할을 한다.&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;b&gt;아래 항목들은 web3 어플리케이션을 이루는 스택들에 대해 정리해 보았다.&lt;/b&gt;&lt;/div&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Smart Contract&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;b&gt;이더리움 블록체인에서 실행되는 프로그램을 말한다&lt;/b&gt;. &lt;b&gt;스마트 컨트랙트는 이더리움 account의 한 종류이다&lt;/b&gt;. 즉 밸런스를 가지고 있고, 네트워크를 통해 트랜잭션을 보낼 수 있다. 다만 유저가 아닌 프로그램에 의해 실행된다는 것이 다른 점이다.&lt;/div&gt;
&lt;div&gt;이더리움의 경우 &lt;b&gt;Solidity나 Vyper&lt;/b&gt;와 같이 스마트 컨트랙트를 작성할 수 있는 언어를 지원해준다. &lt;b&gt;어떤 언어를 사용해도 되지만 EVM이 사용할 수 있게 컴파일이 되어야 한다.&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;스마트 컨트랙트 테스트 프레임워크 및 툴 : Waffle, Solidity-Coveragy, hevm 등&lt;/div&gt;
&lt;div&gt;스마트 컨트랙트 배포, 관리 툴 : Remix IDE, Tenderly, Hardhat, Truffle 등&lt;/div&gt;
&lt;div&gt;스마트 컨트랙트 보안 관리 툴 : Slither, MythX, Mythril 등&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개발망&lt;/b&gt;&lt;/h4&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;DApp에 사용할 스마트 컨트랙트를 만들 때 &lt;b&gt;배포 전 로컬 네트워크에 실행&lt;/b&gt;해보고 싶을 수가 있다.&lt;/div&gt;
&lt;div&gt;이더리움 노드를 로컬로 띄울 수도 있지만, &lt;b&gt;개발에 최적화된 네트워크가 존재&lt;/b&gt;한다. ETH 잔액이 있는 계정을 쓸 수 있다거나 딜레이 없이 트랜잭션을 보내고 받는 등의 기능을 사용할 수 있다.&lt;/div&gt;
&lt;div&gt;개발망 프레임워크는 Ganache, Hardhat Network 등이 있다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Providers(이더리움 클라이언트, 노드)&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;b&gt;클라이언트는 각 블록의 트랜잭션을 검증해 데이터를 정확하게 유지하는 역할을 한다. 클라이언트 소프트웨어를 실행하는 컴퓨터가 바로 노드가 된다.&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;프론트엔드 코드는 이더리움 블록체인에 있는 데이터와 코드를 사용하고, 트랜잭션을 실행하기 위해 블록체인의 노드들 중 하나와 상호작용&lt;/b&gt;을 해야 한다. 노드를 사용하는 방법은 크게 두 가지가 있다.&lt;/div&gt;
&lt;div&gt;(1) 본인의 노드를 만들어 사용한다&lt;/div&gt;
&lt;div&gt;(2) Infura, Alchemy, Quicknode 같은 노드를 사용할 수 있는 third-party 서비스를 이용한다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;third-party 서비스를 사용할 때의 장점은 본인이 노드를 셋업하는 복잡한 과정을 할 필요가 없다&lt;/b&gt;는 것이다. 특히 Dapp이 커질수록 전체 이더리움 블록체인을 저장하는데 들어가는 코스트가 높아지고, 이는 더 많은 노드를 필요로 해 인프라를 관리하는게 더욱 복잡해진다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;직접 노드를 실행시키면 다른 노드에 의존하지 않고 직접 트랜잭션을 검증할 수 있고, 본인의 주소나 밸런스 등을 다른 노드에게 유출하지 않아도 된다는 장점이 있다. 디앱을 배포할 때 이러한 점이 안정성에 도움&lt;/b&gt;이 된다.&lt;/div&gt;
&lt;div&gt;클라이언트 소프트웨언어는 다양한 언어로 여러 오픈 소스가 있다. Geth, OpenEthereum, Nevermind, Besu 등이 있다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Signer&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;Provider가 있으면 블록체인에 접속을 하고 블록체인에 저장된 상태를 읽을 수 있다. 그러나 &lt;b&gt;트랜잭션을 블록체인에 실행시켜 상태를 바꾸려면 한 가지 과정이 더 필요한데, 프리이빗 키로 트랜잭션을 Sign하는 과정&lt;/b&gt;이다.&lt;/div&gt;
&lt;div&gt;블로그 DApp을 상상해보자. 특정 작가가 쓴 블로그 포스트들을 검색하고 읽는 것은 블록체인에서 읽는 과정이기에 유저가 sign을 할 필요가 없다. 그러나 유저가 새로운 포스트를 체인에 올리고 싶으면, &lt;b&gt;DApp은 유저에게 프리이빗 키로 트랜잭션을 사인할 것을 요청&lt;/b&gt;해야 한다.&lt;/div&gt;
&lt;div&gt;트랜잭션을 사인하는 툴은 Metamask를 많이 사용한다. Metamask는 유저의 프라이빗 키를 브라우저에 저장해 프론트엔드가 유저에게 트랜잭션을 사인할 것을 요청할 때 대신해준다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Storage(IPFS, SWARM)&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;블록체인에 데이터를 추가하는 것은 비싸고 시간도 든다. 이는 좋은 유저 경험을 주지 못하기 때문에 IPFS나 SWARM 같은 탈중앙화 오프 체인 스토리지 솔루션을 사용하기도 한다.&lt;/div&gt;
&lt;div&gt;탈중앙화 스토리지 시스템은 peer to peer 네트워크로 이루어져 있다. 블록체인 베이스일수도 있고, 별개의 p2p 네트워크일수도 있다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;지속성 메커니즘 / 인센티브 구조에 따른 분류&lt;/b&gt;&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;블록체인 베이스&lt;/b&gt;&lt;/h4&gt;
&lt;div&gt;이더리움 자체가 탈중앙화 스토리지 시스템이지만 대용량의 데이터를 저장하기에는 적합하지 않다. 모든 노드가 블록체인의 데이터를 저장할 수 있어야 하기 때문이다.&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;컨트랙트 베이스&lt;/b&gt;&lt;/h4&gt;
&lt;div&gt;모든 노드가 데이터를 복사하고 영원히 보관할 수 없다는 것을 인정하고, 컨트랙트의 동의로 데이터를 유지한다. &lt;b&gt;컨트랙트 동의는 여러 노드들이 데이터의 일부를 특정 기간 동안 보관을 한다는 것이다. 데이터의 계약이 끝날때마다 갱신 혹은 중단해야 한다&lt;/b&gt;.&lt;/div&gt;
&lt;div&gt;대부분의 경우 모든 데이터를 체인에 저장하는 대신 데이터가 체인에 위치된 곳의 해시가 저장된다.&lt;/div&gt;
&lt;div&gt;컨트랙트 베이스의 플랫폼은 filecoin, Skynet, Storj, 0Chain 등이 있다.&lt;/div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;기타&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;IPFS는 데이터, 어플리케이션, 웹사이트, 파일 등을 저장하고 접근하기 위한 분산 시스템이다. 인센티브 시스템은 없지만 위의 컨트렉트 베이스 인센티브 솔루션과 함께 사용할 수 있다.&lt;/div&gt;
&lt;div&gt;IPFS에서 데이터를 유지하기 위한 또다른 방법은 고정 서비스(pinning service)를 사용하는 것이다. 자체 IPFS 노드를 가동시켜 네트워크에 데이터를 기여할 수 있다.&lt;/div&gt;
&lt;div&gt;IPFS, Pinata (IPFS pinning service), web3.storage(IPFS/Filecoin pinning service), Infura (IPFS pinning service)&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프론트엔드&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;Web2에서는 프론트엔드를 AWS에 호스트 한다거나 자체 웹서버를 운영해 배포하였었다. &lt;b&gt;Web3에서 이 형태를 똑같이 가져가면 프론트엔드에서 중앙화된 부분이 생기는 것이다(AWS 및 자체 서버가 죽는다면? AWS가 app을 검열한다면?)&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;그래서 &lt;b&gt;프론트엔드 코드도 IPFS/SWARM을 써서 탈중앙화 스토리지 솔루션으로 배포&lt;/b&gt;한다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이더리움 클라이언트 API&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;&lt;b&gt;이더리움 클라이언트 API를 사용하면 이더리움 노드와 연결하여 블록체인과 상호작용&lt;/b&gt;할 수 있다. &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;이를 위해 모든 이더리움 클라이언트는 JSON-RPC 명세를 지원한다.&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;라이브러리를 사용하면 좀 더 쉽게 다양한 기능을 사용할 수 있다.&lt;/div&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;ETH와 WEI 변동 등의 유틸리티 함수 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;대표적인 라이브러리로는 Web3.js, Ethers.js, The Graph, light.js 등이 있다.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;The Graph&lt;/b&gt;&lt;/h3&gt;
&lt;div data-pm-slice=&quot;1 1 []&quot; data-en-clipboard=&quot;true&quot;&gt;&lt;b&gt;블록체인 위에 올려진 스마트 컨트랙트에서 데이터를 읽어야 할 때&lt;/b&gt;가 있다. 예를 들어 결제 스마트 컨트랙트라면 결제가 일어날 때 프론트엔드가 알 수 있게 해야한다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;스마트 컨트랙트에서 데이터를 읽는 방법은 &lt;b&gt;이벤트와 그래프 방법&lt;/b&gt;이 있다.&lt;/div&gt;
&lt;div&gt;먼저 &lt;b&gt;이벤트는 스마트 컨트랙트가 실행이 될 때 이벤트를 내보내 프론트엔드가 이 이벤트를 받아 정해진 동작을 할 수 있게 하는 것&lt;/b&gt;이다.&lt;/div&gt;
&lt;div&gt;하지만 스마트 컨트랙트가 이미 배포되면 수정을 하기가 어렵다. 즉, 이벤트를 수정하거나 추가하려면 &lt;b&gt;재배포를 해야만 한다&lt;/b&gt;.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;b&gt;The Graph는 이러한 단점을 해결하기 위해 나온 오프체인 솔루션&lt;/b&gt;이다. 탈중앙화 인덱싱 프로토콜이고, 개발자가 서버리스 어플리케이션을 만들 수 있게 도와준다.&lt;/div&gt;
&lt;div&gt;The Graph는 인덱스할 스마트 컨트랙트의 함수 및 이벤트를 정의하면 프론트엔드가 사용할 수 있게 엔티티로 바꿔 소비할 수 있게 해준다. 개발자들이 GraphQL을 사용해 서브 그래프로부터 쿼리를 해 DApp에 사용할 정보를 얻어올 수 있게 해준다.&lt;/div&gt;</description>
      <category>블록체인</category>
      <author>woojinger</author>
      <guid isPermaLink="true">https://woojinger.tistory.com/106</guid>
      <comments>https://woojinger.tistory.com/106#entry106comment</comments>
      <pubDate>Sun, 9 Jan 2022 20:06:12 +0900</pubDate>
    </item>
  </channel>
</rss>