<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>처음처럼</title>
    <link>https://hellominchan.tistory.com/</link>
    <description>Though you should not fear failure, You should do your very best to avoid it.</description>
    <language>ko</language>
    <pubDate>Tue, 19 May 2026 23:35:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>HelloMinchan</managingEditor>
    <image>
      <title>처음처럼</title>
      <url>https://tistory1.daumcdn.net/tistory/3657216/attach/14f9e5acf2ad43f68183b23ac97c58d9</url>
      <link>https://hellominchan.tistory.com</link>
    </image>
    <item>
      <title>[객체지향의 사실과 오해] 인사이트 정리 겸 회고</title>
      <link>https://hellominchan.tistory.com/385</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r1q9K/btsAborcYO8/tKrwqqkbzAp15z7U412EH0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r1q9K/btsAborcYO8/tKrwqqkbzAp15z7U412EH0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r1q9K/btsAborcYO8/tKrwqqkbzAp15z7U412EH0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr1q9K%2FbtsAborcYO8%2FtKrwqqkbzAp15z7U412EH0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;628&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001628109&quot;&gt;객체지향의 사실과 오해&lt;/a&gt;를 읽고 인사이트 정리 겸 회고&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 시작한 지는 꽤 되었지만, 이런저런 이유로 미루고 미루다 회사에서 객체지향 프레임워크도 다루기 시작했고 읽고 싶었던 책을 읽기 위해 밀린 책을 쳐낼 겸 독파했다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밀렸던 블로그도 이를 계기로 다시 시작할 예정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 보여주기식이 아닌 진짜 일기장처럼 끄적임으로 작성해야겠다고 다짐ㅋㅅㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초중반 부분은 객체지향의 패러다임과 객체지향 사고의 관점을 이해시키기 위해 카페의 생리(손님 &amp;lt;-&amp;gt; 점원 &amp;lt;-&amp;gt; 바리스타)와, 이상한 나라의 앨리스에서 재판을 받는 부분(왕 &amp;lt;-&amp;gt; 하얀 토끼 &amp;lt;-&amp;gt; 모자장수)을 비유 삼아 이야기를 풀어나간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 객체지향은 학교 수업만 듣고 자꾸 클래스에 한정 지어 생각하고 개념적으로 붕 떠있는 부분들이 있었는데, 이러한 비유들로 접근하니 좀 더 구체적으로 와닿아 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 이 책의 핵심은 부제에 스포가 되어있듯이 객체지향 프로그래밍을 하려면&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;책임&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;협력&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 3가지 관점에서 고민하고 설계를 해야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카페로 예시를 들면 &lt;b&gt;역할&lt;/b&gt;에 따라 손님, 점원, 바리스타 이 3가지의 객체가 존재하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손님은 주문을 하는 &lt;b&gt;역할&lt;/b&gt;, 점원은 주문을 받는 &lt;b&gt;역할&lt;/b&gt;, 바리스타는 주문받은 커피를 만드는 &lt;b&gt;역할&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 손님은 커피를 주문하고 기다렸다가 점원에게 주문한 커피를 받아 돌아가야 할 &lt;b&gt;책임&lt;/b&gt;, 점원은 손님의 주문을 받고 바리스타에게 주문받은 커피를 알리며 바리스타가 만든 커피를 손님에게 전달해야 할 &lt;b&gt;책임&lt;/b&gt;, 바리스타는 점원으로부터 부탁받은 커피를 만들어야 할 &lt;b&gt;책임&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 &lt;b&gt;역할&lt;/b&gt;과 &lt;b&gt;책임&lt;/b&gt;에 따라 3가지 객체(손님, 점원, 바리스타)가 각기 &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;앞으로 객체지향 프레임워크를 사용하거나 코드를 칠 때 위 3가지 관점을 녹여 내려 노력해야겠다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중후반 이후에는 좀 더 추상적인 개발관점에 대해 많은 이야기가 서술되어 있었고, 현재 내 지식수준과 1 회독 만으로는 받아들일 수 있는 인사이트의 한계가 있었지만 정리를 해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;인터페이스나 메서드(함수) 설계 시 메시지(파라미터)에 초점을 두어 설계하자&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;메시지가 객체의 공용 인터페이스의 모양을 암시 =&amp;gt; 책임-주도 설계 방식의 What/Who 사이클과 관련된다.&lt;br /&gt;하다못해 네이밍이나 기능을 함수로 찢을 때에도 나는 로직이나 리턴할 값에 초점을 두었었는데 메시지를 먼저 생각하려는 습관을 들여야겠다고 생각했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;객체지향적인 사고방식을 이해하고 잘 녹이려면 3가지 원칙을 떠올리자&lt;/b&gt;&lt;/u&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;추상적인 인터페이스를 설계하자&lt;/li&gt;
&lt;li&gt;인터페이스 최소화 하자&lt;/li&gt;
&lt;li&gt;인터페이스와 구현 간에 차이를 두고 이를 인식하자&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;객체지향의 강력함을 누리기 위한 출발점은 책임을 자율적으로 만드는 것이다&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;예를 들어 판사와 목격자가 있을 때 판사가 목격자에게 지나치게 상세한 수준의 메시지(목격했던 장면을 떠올려주세요, 떠오르는 기억을 시간순으로 재구성해서 알려주세요, 말로 간결하게 표현해 주세요 등)를 보내는 것은 객체의 자율성을 저해하게 된다.&lt;br /&gt;따라서, 추상화된 메시지(증언해 주세요.)를 사용하는 것이 수신자의 자율성이 보장되므로 더 좋은 인터페이스 설계이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;미래에 대비하는 가장 좋은 방법은 변경을 예측하는 것이 아니라 변경을 수용하는 것이다&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;헤라클레이토스의 명언을 인용한다. (유일하게 변하지 않는 것은 모든 것이 변한다는 사실뿐이다.)&lt;br /&gt;훌륭한 설계자는 어떤 변경이 발생할지 예측하는 것이 아니라, 어떻게 변경이 발생할지 알 수없으며 언젠가 변경이 발생한다는 사실을 겸허히 받아들이는 사람이다.&lt;br /&gt;따라서 좋은 설계 = 나중에 일어나는 변경을 할 수 있게 여지를 남겨놓는 설계이다.&lt;br /&gt;설계의 목적은 나중에 일어날 변경으로 인한 재설계를 허용하는 것이고, 일차 목표는 변경에 대한 소요 비용을 낮추는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;모델링 관점 (유스케이스, 도메인, 멘탈)&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;길을 잃었을 때 목적지를 찾아가는 방법에 비유를 한다.&lt;br /&gt;지나가는 사람에게 길을 물어 방법(기능적, 유스케이스 모델링)과 지도를 보는 방법(구조적, 도메인 모델링)이 있다.&lt;br /&gt;하지만 유스케이스 모델링과 도메인 모델링 말고 멘탈 모델링 또한 중요하다.&lt;br /&gt;멘탈 모델이란 이해관계자들이 상호작용을 하는 사물들에 갖는 일종의 모형이다.&lt;br /&gt;예를 들어, 은행업무 종사자는 은행 도메인을 고객과 계좌 사이의 돈의 흐름이라고 생각할 것이고,&lt;br /&gt;중고 자동차 판매상은 자동차 도메인을 구매되는 자동차와 판매되는 자동차의 교환 흐름이라고 생각할 것이다.&lt;br /&gt;이처럼 실제 세계의 모델링을 할 때에는 도메인 모델링뿐 아니라 이해관계자들의 멘탈 모델링 또한 부합시키는 것이 중요하다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>독서/객체지향의 사실과 오해</category>
      <category>객체지향의 사실과 오해</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/385</guid>
      <comments>https://hellominchan.tistory.com/385#entry385comment</comments>
      <pubDate>Sat, 11 Nov 2023 18:38:38 +0900</pubDate>
    </item>
    <item>
      <title>[OOP] SOLID에 대한 이해</title>
      <link>https://hellominchan.tistory.com/384</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;로버트 마틴 아저씨가 설계한 객체 지향 프로그래밍의 원칙(SOLID)을 이해해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SOLID&lt;/a&gt;는 5가지 기본 원칙의 시작 글자를 합친 것이며, 5가지는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 책임 원칙&lt;/b&gt; (Single responsibility principle, SRP)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개방-폐쇄 원칙&lt;/b&gt; (Open/closed principle, OCP)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리스코프 치환 원칙&lt;/b&gt; (Liskov substitution principle, LSP)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인터페이스 분리 원칙&lt;/b&gt; (Interface segregation principle, ISP)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의존관계 역전 원칙&lt;/b&gt; (Dependency inversion principle, DIP)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 원칙들은 유기적으로 연관되어 있지만, 이해하기 쉽게 각각 하나씩 분할 정복해 보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 id=&quot;%--%--hibernate-ddl-auto%--%EC%--%-D%EC%--%B-&quot; data-ke-size=&quot;size26&quot;&gt;# 단일&amp;nbsp;책임&amp;nbsp;원칙&amp;nbsp;(Single&amp;nbsp;responsibility&amp;nbsp;principle,&amp;nbsp;SRP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 책임 원칙은 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 클래스는 오직 하나의 책임만 가져야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 이해하기 어려운 건 &lt;b&gt;책임&lt;/b&gt;이라는 게 &lt;b&gt;모호&lt;/b&gt;하기 때문인데, 이 &lt;b&gt;책임&lt;/b&gt;은 상황에 따라 클 수도, 작을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, 단일 책임 원칙을 잘 지키기 위한 중요한 기준은 바로 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;변경&lt;/span&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_1677590837683&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class 남자 {

    public void 효도하기() {
        System.out.println(&quot;부모님에게 효도를 함.&quot;);
    }

    public void 데이트하기() {
        System.out.println(&quot;여자친구와 데이트를 함.&quot;);
    }

    public void 출근하기() {
        System.out.println(&quot;회사에 출근 함.&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;&lt;span&gt;이 클래스는 남자가 가질 수 있는 여러&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;책임&lt;/b&gt;&lt;span&gt;들을 모두 가지고 있기에 특정 기능이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffff00;&quot;&gt;변경&lt;/span&gt;될때마다&lt;/span&gt; 클래스가 계속 수정될 것이며,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;i&gt;하나의 모듈은 하나의, 오직 하나의 액터에 대해서만 책임져야 한다.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;* 모듈 : 소스 파일(함수와 데이터 구조로 구성된 응집된 집합)&lt;/i&gt;&lt;br /&gt;&lt;i&gt;* 액터 : 변경을 요청하는 한 명 이상의 사람들&lt;/i&gt;&lt;/blockquote&gt;
&lt;p data-ke-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;따라서, SRP 관점으로 남자 클래스를 리팩토링 한다면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1677591431532&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class 아들 {

    public void 효도하기() {
        System.out.println(&quot;부모님에게 효도를 함.&quot;);
    }
}

class 남자친구 {

    public void 데이트하기() {
        System.out.println(&quot;여자친구와 데이트를 함.&quot;);
    }
}

class 직장인 {

    public void 출근하기() {
        System.out.println(&quot;회사에 출근 함.&quot;);
    }
}&lt;/code&gt;&lt;/pre&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;span style=&quot;background-color: #ffff00;&quot;&gt;변경&lt;/span&gt;이 일어나도 해당 클래스만 변경해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 단일 책임 원칙을 지키면 코드의 가독성 향상과 유지 보수가 용이해지는 이점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 분리할 이유가 없는데 과도하게 분리할 경우 오히려 복잡성이 증가하게 되므로 주의해야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 id=&quot;%--%--hibernate-ddl-auto%--%EC%--%-D%EC%--%B-&quot; data-ke-size=&quot;size26&quot;&gt;# 개방-폐쇄&amp;nbsp;원칙&amp;nbsp;(Open/closed&amp;nbsp;principle,&amp;nbsp;OCP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개방 폐쇄 원칙은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;확장&lt;/b&gt;에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;열려&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;있으나,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;변경&lt;/b&gt;에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;닫혀&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;있어야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 자세히 풀어보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;열린 확장&lt;/b&gt;이란 새로운 클래스를 추가함으로써 기능을 확장하는 걸 의미하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;닫힌 변경&lt;/b&gt;이란 확장이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;발생했을때&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;발생했을 때&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar1&quot; data-grammar-focus=&quot;false&quot;&gt;발생했을 때&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;해당 코드를 사용하는 쪽에서 변경이 없어야 한다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 코드는 이해하는 걸 최우선 목표로 아주&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;린하게&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;긴하게&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;spell&amp;quot;}&quot; data-grammar-id=&quot;grammar2&quot; data-grammar-focus=&quot;false&quot;&gt;린하게&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;구성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 역할이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;주어질때&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;주어질 때&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar3&quot; data-grammar-focus=&quot;false&quot;&gt;주어질 때&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;해당 서비스를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;콜하는&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;콜 하는&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar4&quot; data-grammar-focus=&quot;false&quot;&gt;콜 하는&lt;/span&gt;&lt;span&gt;&amp;nbsp;간단한&amp;nbsp;&lt;/span&gt;스프링 Controller를 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, OCP를 생각하지 않고 Controller를 짠다면 이렇게 될 게 뻔한데.. 바로 안티 패턴이 나타난다.&lt;/p&gt;
&lt;pre id=&quot;code_1677762014717&quot; class=&quot;less&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/playrole&quot;)
public void playRole(@RequestParam String 역할) {
    if(역할.equals(&quot;아들&quot;)) {
        아들Service.do();
    } else if (역할.equals(&quot;남자친구&quot;)) {
        남자친구Service.do();
    }
    // 다른 역할 계속 추가..?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 기존 역할 이외에 &quot;직장인&quot;이라는 역할이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;확장&lt;/b&gt;될 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;직장인Service뿐&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;직장인 Service뿐&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar5&quot; data-grammar-focus=&quot;false&quot;&gt;직장인Service뿐&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;아니라 해당 Service를 사용하는 Controller까지 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;변경&lt;/b&gt;되기 때문이다. (+ 무한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;if ?)&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;if?)&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar6&quot; data-grammar-focus=&quot;false&quot;&gt;if ?)&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;따라서, OCP 관점에서 리팩토링을 한다면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1677762014717&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/playrole&quot;)
public void playRole(@RequestParam String 역할) {
    final 역할Service인터페이스 역할Service = 역할Factory.createService(역할);
    
    역할Service.do();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 앞으로 역할이 확장되어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;수 많은&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;수많은&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar7&quot; data-grammar-focus=&quot;false&quot;&gt;수많은&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Service가 추가되더라도 Controller의 코드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;변경 되지&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;변경되지&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar8&quot; data-grammar-focus=&quot;false&quot;&gt;변경되지&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;않는 걸 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 바로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;확장&lt;/b&gt;에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;열려&lt;/b&gt;&amp;nbsp;있으나,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;변경&lt;/b&gt;에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;닫혀&lt;/b&gt;있는 OCP가 지켜진 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 역할에 맞는 Service를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;선택해주는&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;선택해 주는&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar9&quot; data-grammar-focus=&quot;false&quot;&gt;선택해 주는&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;역할Factory&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;역할 Factory&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar10&quot; data-grammar-focus=&quot;false&quot;&gt;역할Factory&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;클래스가 별도로 추가되었는데, 해당 부분은 디자인 패턴 중&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;팩토리&lt;/a&gt;와 연관되므로 이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;글 에서는&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;글에서는&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar11&quot; data-grammar-focus=&quot;false&quot;&gt;글에서는&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;설명하지 않겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 id=&quot;%--%--hibernate-ddl-auto%--%EC%--%-D%EC%--%B-&quot; data-ke-size=&quot;size26&quot;&gt;# 리스코프&amp;nbsp;치환&amp;nbsp;원칙&amp;nbsp;(Liskov&amp;nbsp;substitution&amp;nbsp;principle,&amp;nbsp;LSP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스코프 치환 원칙은 &lt;b&gt;상위 타입&lt;/b&gt;의 클래스가 &lt;b&gt;하위 타입&lt;/b&gt;의 클래스로 &lt;b&gt;치환&lt;/b&gt;되어도 &lt;b&gt;동일한 동작&lt;/b&gt;을 보장해야 한다는 원칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 당연하지만 매우 중요한데, 객체지향의 &lt;a href=&quot;https://en.wikipedia.org/wiki/Polymorphism_(computer_science)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다형성&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;다형성을 위해 하위 클래스는 인터페이스의 규약을 지켜야 하고, 이 자체로 LSP를 지키는 것이라 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 LSP를 잘 적용한 예시는 Java의 &lt;a href=&quot;https://en.wikipedia.org/wiki/Java_collections_framework&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;컬렉션 프레임워크&lt;/a&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1799&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mgVdF/btr1URIoUc5/gKqES3fEg8gMpTJqKgaPMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mgVdF/btr1URIoUc5/gKqES3fEg8gMpTJqKgaPMK/img.png&quot; data-alt=&quot;출처 : https://en.wikipedia.org/wiki/Java_collections_framework&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mgVdF/btr1URIoUc5/gKqES3fEg8gMpTJqKgaPMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmgVdF%2Fbtr1URIoUc5%2FgKqES3fEg8gMpTJqKgaPMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;656&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1799&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://en.wikipedia.org/wiki/Java_collections_framework&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 계층도를 보면 상위 타입에 속하는 어떤 하위 타입의 클래스를 대입해도 동일하게 동작하는 걸 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Map 자료구조만 놓고 자세히 확인해 보면,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XZAT5/btr1USURVY9/qsTJvsfrOOafs1Om6ZriR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XZAT5/btr1USURVY9/qsTJvsfrOOafs1Om6ZriR0/img.png&quot; data-alt=&quot;출처 : https://en.wikipedia.org/wiki/Java_collections_framework&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XZAT5/btr1USURVY9/qsTJvsfrOOafs1Om6ZriR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXZAT5%2Fbtr1USURVY9%2FqsTJvsfrOOafs1Om6ZriR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;481&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1978&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://en.wikipedia.org/wiki/Java_collections_framework&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최상위 Map 타입에 Hashtable, HashMap, TreeMap 등 어떤 하위 클래스를 대입할지라도 LSP가 잘 지켜져 있기에 동일한 Map 자료구조의 동작이 수행됨을 보장한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 id=&quot;%--%--hibernate-ddl-auto%--%EC%--%-D%EC%--%B-&quot; data-ke-size=&quot;size26&quot;&gt;# 인터페이스&amp;nbsp;분리&amp;nbsp;원칙&amp;nbsp;(Interface&amp;nbsp;segregation&amp;nbsp;principle,&amp;nbsp;ISP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스 분리 원칙은 SRP와 비슷한데 클래스가 아닌 인터페이스에 초점이 맞춰져 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는 사용하지 않는 인터페이스에 강제로 의존하면 안 된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 코드로 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, ISP가 지켜지지 않은 예시이다.&lt;/p&gt;
&lt;pre id=&quot;code_1678077657138&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface 동물 {

    void 고기를먹음();

    void 풀을먹음();
}

class 호랑이 implements 동물 {

    @Override
    public void 고기를먹음() {
        System.out.println(&quot;고기를 먹는다.&quot;);
    }

    @Override
    public void 풀을먹음() {
        System.out.println(&quot;풀을 먹는다.&quot;);
    }
}

class 사슴 implements 동물 {

    @Override
    public void 고기를먹음() {
        // 사용할 수 없음!!
        new Exception(&quot;고기를 먹을 수 없음...&quot;);
    }

    @Override
    public void 풀을먹음() {
        System.out.println(&quot;풀을 먹는다.&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;호랑이는 고기와 풀을 둘 다 먹을 수 있으므로 두 가지 행동 모두 만족하지만, 사슴의 경우 초식동물이기에 고기를 먹지 못하므로 에러가 발생하며 이는 ISP에 위배된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, ISP를 지키려면 다음과 같이 리팩토링을 해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678078382905&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface 육식동물 {

    void 고기를먹음();
}

class 호랑이 implements 육식동물 {

    @Override
    public void 고기를먹음() {
        System.out.println(&quot;고기를 먹는다.&quot;);
    }
}

interface 초식동물 {

    void 풀을먹음();
}

class 사슴 implements 초식동물 {

    @Override
    public void 풀을먹음() {
        System.out.println(&quot;풀을 먹는다.&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동물 인터페이스를 클라이언트별로 세분화하여 육식 동물, 초식 동물로 분리함으로써 ISP를 만족하게 되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 id=&quot;%--%--hibernate-ddl-auto%--%EC%--%-D%EC%--%B-&quot; data-ke-size=&quot;size26&quot;&gt;# 의존관계 역전 원칙 (Dependency inversion principle, DIP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOLID의 마지막, 의존관계 역전 원칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;i&gt;Depend upon Abstractions. Do not depend upon concretions.&lt;/i&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 인용구를 모토로 흔히 말하는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Dependency_injection&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DI(의존성 주입)&lt;/a&gt;가 이 원칙을 따르는 방법 중 하나이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해 구현 클래스에 의존하지 말고 인터페이스에 의존하라는 뜻이다. (따라서 LSP, OCP와 매우 밀접한 관련이 있다.)&lt;/p&gt;
&lt;p data-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_1678084089880&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class 대리기사 {

    private K5 k5;
    private Sonata sonata;
    // 다른 자동차 계속 추가..?

    public void setK5(K5 k5) {
        this.k5 = k5;
    }

    public void setSonata(Sonata sonata) {
        this.sonata = sonata;
    }
    
    // 다른 자동차 세터 계속 추가..?

    public void driveK5() {
        k5.drive();
    }

    public void driveSonata() {
        sonata.drive();
    }
    
    // 다른 자동차 관련 메서드 계속 추가..?
}

public class Main {

    public static void main(String[] args) {
        K5 k5 = new K5();
        Sonata sonata = new Sonata();

        대리기사 이수근 = new 대리기사();
        이수근.setK5(k5);
        이수근.driveK5();

        이수근.setSonata(sonata);
        이수근.driveSonata();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편의상 K5, Sonata 같은 자동차 클래스 코드는 생략했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대리기사 클래스를 보면 K5와 Sonata라는 구현 클래스에 의존하고 있는 모습을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언뜻 보기엔 별문제 없어 보이지만, 만약 대리기사가 벤츠를 몰아야 한다면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벤츠 클래스를 만들고, 대리기사 클래스에도 벤츠 관련 코드가 추가되어야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대리기사가 운전하게 될 차가 오직 저 두 종류뿐만이 아니기에, 이후 차 종류가 추가될수록 대리기사 클래스 또한 계속 변경되는 악순환이 발생하며 이는 DIP를 위배한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, DIP를 지키기 위해 리팩토링을 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1678085839086&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class 대리기사 {

    private Car car;

    public void setCar(Car car) {
        this.car = car;
    }

    public void driveCar() {
        car.drive();
    }
}

public class Main {

    public static void main(String[] args) {
        Car k5 = new K5();
        Car sonata = new Sonata();
        Car benz = new Benz();

        대리기사 이수근 = new 대리기사();

        이수근.setCar(k5);
        이수근.driveCar();

        이수근.setCar(sonata);
        이수근.driveCar();

        이수근.setCar(benz);
        이수근.driveCar();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 편의상 자동차 관련 클래스는 생략했고, Car는 인터페이스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대리기사 클래스에서 구체화된 구현 클래스가 아닌 추상화된 인터페이스에 의존하므로 DIP를 만족하게 되었고, 이제 자동차가 추가되더라도 대리기사 클래스는 코드 변경이 일어나지 않는 것을 알 수 있다.&lt;/p&gt;</description>
      <category>Computer Science/OOP</category>
      <category>DIP</category>
      <category>iSP</category>
      <category>lsp</category>
      <category>OCP</category>
      <category>Solid</category>
      <category>SRP</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/384</guid>
      <comments>https://hellominchan.tistory.com/384#entry384comment</comments>
      <pubDate>Wed, 1 Mar 2023 04:23:53 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Lombok 사전</title>
      <link>https://hellominchan.tistory.com/383</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mIQvx/btr06Wctfx0/DkmK3QjUo5TUoaYYQElTTk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mIQvx/btr06Wctfx0/DkmK3QjUo5TUoaYYQElTTk/img.jpg&quot; data-alt=&quot;출처 : https://github.com/projectlombok&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mIQvx/btr06Wctfx0/DkmK3QjUo5TUoaYYQElTTk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmIQvx%2Fbtr06Wctfx0%2FDkmK3QjUo5TUoaYYQElTTk%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;400&quot; height=&quot;400&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://github.com/projectlombok&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lombok 라이브러리 관련 용어 정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 쓰는거 계속 업데이트해서 사전처럼 쓸 예정..&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 id=&quot;%--%--Annotation&quot; data-ke-size=&quot;size26&quot;&gt;# Annotation&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@Getter&lt;/b&gt; : 게터 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@Setter&lt;/b&gt; : 세터 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@NorgsConstructor&lt;/b&gt; : 기본 생성자 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@AllArgsConstructor&lt;/b&gt; : 모든 필드 값을 파라미터로 받는 생성자 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@RequiredArgsConstructor&lt;/b&gt; : final이나 @Nonull 필드 만 파라미터로 받는 생성자 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@EqualsAndHashCode&lt;/b&gt; : 동등성(두 객체의 내용이 같은지), 동일성(두 객체가 같은지)을 위한 equals, hashcode 메서드 자동 생성&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/Spring</category>
      <category>Lombok</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/383</guid>
      <comments>https://hellominchan.tistory.com/383#entry383comment</comments>
      <pubDate>Fri, 24 Feb 2023 19:46:26 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] JPA 사전</title>
      <link>https://hellominchan.tistory.com/382</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 JPA 관련 용어 정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 쓰는거 계속 업데이트해서 사전처럼 쓸 예정..&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 id=&quot;%--%--Stream%--%EC%-D%B-%EB%-E%--%-F&quot; data-ke-size=&quot;size26&quot;&gt;# hibernate.ddl-auto 속성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;create&lt;/b&gt; : 기존 테이블 삭제 후 새로 생성 (drop -&amp;gt; create)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;create-drop&lt;/b&gt; : create에서 종료할때 drop 추가 (drop -&amp;gt; create -&amp;gt; drop)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;update&lt;/b&gt; : DB 테이블과 엔티티 매핑 비교해서 변경 사항만 업데이트 (테이블이 없으면 create)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;validate&lt;/b&gt; : DB 테이블과 엔티티 매핑 비교해서 차이가 있으면 경고 후 앱 실행하지 않음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;none&lt;/b&gt; : 자동 생성 기능 사용 안함&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;%--%--Stream%--%EC%-D%B-%EB%-E%--%-F&quot; data-ke-size=&quot;size26&quot;&gt;# Annotation&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@Table&lt;/b&gt; : 테이블 관련 설정 (name, &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;uniqueConstraints, &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;indexes 등)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;b&gt;@Column&lt;/b&gt; : 컬럼 관련 설정 (name, nullable, unique 등)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@DynamicInsert&lt;/b&gt;&amp;nbsp;&lt;/span&gt;: insert query시 null인 필드 제외&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@DynamicUpdate&lt;/b&gt;&amp;nbsp;&lt;/span&gt;: update query시 null인 필드 제외&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;b&gt;@MappedSuperclass&lt;/b&gt; : 공통 매핑 필드 선언 시 사용&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;b&gt;@EntityListeners(AutoCloseable.class)&lt;/b&gt; : Auditing 추가 (시간 자동 주입)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;@CreatedDate&lt;/b&gt; : 엔티티 생성 시각 주입&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;@LastModifiedDate&lt;/b&gt; : 엔티티 변경 시각 주입&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;@Embeddable, @&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;Embedded&lt;/b&gt; : 객체 컬럼 사용 시 사용&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/Spring</category>
      <category>JPA</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/382</guid>
      <comments>https://hellominchan.tistory.com/382#entry382comment</comments>
      <pubDate>Fri, 24 Feb 2023 16:23:30 +0900</pubDate>
    </item>
    <item>
      <title>[데이터 중심 애플리케이션 설계] 저장소와 검색</title>
      <link>https://hellominchan.tistory.com/381</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDNDRN/btrWVKUA5tR/6F132yKF6QnfTPKjmoShl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDNDRN/btrWVKUA5tR/6F132yKF6QnfTPKjmoShl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDNDRN/btrWVKUA5tR/6F132yKF6QnfTPKjmoShl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDNDRN%2FbtrWVKUA5tR%2F6F132yKF6QnfTPKjmoShl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;561&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001766328&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;데이터 중심 애플리케이션 설계&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;를 독파하며 정리하는 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 데이터베이스를 강력하게 만드는 데이터 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세상에서 제일 간단한 데이터베이스를 구현해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1674548674181&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

db_set () {
	echo &quot;$1,$2&quot; &amp;gt;&amp;gt; database
}

db_get () {
	grep &quot;л$1,&quot; database | sed -e &quot;s/A$1,//&quot; | tail -n 1
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키-값 저장소를 함수 두 개로 구현한 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식으로 데이터베이스를 구성할 경우 &lt;b&gt;db_set()&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;실제로 많은 데이터베이스의 로그는 이런 append-only 방식의 데이터 파일을 사용한다.&lt;/p&gt;
&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_get()&lt;/b&gt;의 경우 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스에 많은 레코드가 존재할 경우 키를 찾을 때 걸리는 비용이 O(n)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스의 레코드 수가 두 배로 늘면 검색도 두 배 오래 걸린다는 소리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 이는 바람직하지 않은 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 효율적으로 개선하기 위해서는 다른 데이터 구조가 필요하며, 그것이 바로 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;색인&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색인의 일반적인 개념은 부가적인 메타데이터를 유지하는 것이며, 이것이 이정표역할을 함으로써 데이터의 위치를 찾는데 도움을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 색인에도 트레이드오프가 존재하는데 색인을 잘 선택한다면 읽기 속도가 향상되겠지만, 반대로 쓰기 속도는 떨어지게 된다.&lt;/p&gt;
&lt;p data-ke-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;size18&quot;&gt;&lt;b&gt;해시 색인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키-값 저장소는 대부분의 프로그래밍 언어에서 볼 수 있는 사전 타입(dictionary type)과 유사하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 해시 맵(hash map), 해시 테이블(hash table)로 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키를 데이터 파일의 바이트 오프셋에 매핑해 인메모리 해시 맵을 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 조회할 시 해시 맵을 이용 해 데이터 파일에서 오프셋을 찾아 해당 위치를 구하고 값을 읽는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lwy8y/btrWS1WVk9j/04Nn1JSKTyoqqzKI0ylEFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lwy8y/btrWS1WVk9j/04Nn1JSKTyoqqzKI0ylEFK/img.png&quot; data-alt=&quot;참조 :&amp;amp;amp;nbsp;https://ebrary.net/64644/computer_science/hash_indexes#101&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lwy8y/btrWS1WVk9j/04Nn1JSKTyoqqzKI0ylEFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flwy8y%2FbtrWS1WVk9j%2F04Nn1JSKTyoqqzKI0ylEFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;352&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 :&amp;amp;nbsp;https://ebrary.net/64644/computer_science/hash_indexes#101&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 단순하지만 실제로 많이 사용하는 접근법이지만, 파일에 항상 추가만 하게 되면 결국 디스크 공간이 부족해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 특정 크기의 세그먼트로 로그를 나누는 방식을 사용하며, 특정 크기에 도달할 시 세그먼트 파일을 닫고 새로운 세그먼트 파일에 이후 쓰기를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 중복된 키를 버리고 각 키의 최신 갱신 값만 유지하는 컴팩션(compaction) 작업을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 해시 색인도 물론 제한 사항이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해시 테이블을 메모리에 저장해야 하므로 키가 너무 많으면 문제가 발생한다. 디스크에 해시 맵을 유지할 수 있긴 하지만 성능이 좋지 않고, 무작위 접근 I/O가 많이 필요하며 디스크가 가득 찼을 때 확장하는 비용이 비싸다. 또한 &lt;a href=&quot;https://en.wikipedia.org/wiki/Hash_collision&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해시 충돌&lt;/a&gt;을 막기 위해 별도의 로직이 필요하다.&lt;/li&gt;
&lt;li&gt;범위 질의(range query)에 효율적이지 않다. 예를 들어 member0001 ~~ member0100 사이의 모든 키를 쉽게 스캔할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;SS테이블과 LSM 트리&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;이 세그먼트 파일의 형식에 키-값 쌍을 키로 정렬하게 되면 정렬된 문자열 테이블(Sorted String Table, SS테이블)이라 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순차적으로 로그를 쌓기 때문에 정렬이 불가능할 것 같지만 가능하며, 이 SS테이블은 해시 인덱싱을 가진 로그 세그먼트보다 키가 존재할 범위를 예상하고 해당 영역에서만 값을 조회하여 찾을 수 있다는 큰 장점이 있다.&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;SS테이블 생성과 유지&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;디스크에 정렬된 구조를 유지하는 일은 가능하지만(B-tree), 메모리에 유지하는 편이 훨씬 쉬우며(레드 블랙 트리, AVL 트리) 이런 데이터 구조를 이용하면 임의 순서로 키를 삽입하고 정렬된 순서로 해당 키를 다시 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장소 엔진의 동작 방식은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쓰기 요청이 들어오면 인메모리 균형트리 데이터 구조(레드 블랙트리, AVL 트리)에 추가한다. (멤테이블, Memtable이라고도 함.)&lt;/li&gt;
&lt;li&gt;새로운 SS테이블 파일은 DB에서 가장 최신 세그먼트가 되고 SS테이블을 디스크에 기록하는 동안 쓰기는 새로운 멤테이블 인스턴스에 기록한다.&lt;/li&gt;
&lt;li&gt;읽기 요청을 제공하려면 먼저 멤테이블에서 키를 찾는다. 그다음 디스크 상의 가장 최신 세그먼트 -&amp;gt; 두 번째 -&amp;gt; N 번째 세그먼트 순서로 찾는다.&lt;/li&gt;
&lt;li&gt;그리고 중간중간 백그라운드로 병합과 컴팩션을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허나 한 가지 문제가 있는데, 만약 DB가 고장 나면 아직 디스크에 기록되지 않고 멤테이블에 존재하는 가장 최신의 쓰기가 손실된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 피하기 위해 매번 쓰기를 즉시 추가할 수 있게 분리된 로그를 디스크 상에 유지해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이 로그는 손상 후 멤테이블을 복원할 때만 필요하기 때문에 순서가 정렬되지 않아도 문제 되지 않으며, 멤테이블을 SS테이블로 기록하고 난 후 해당 로그를 삭제한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 위 알고리즘으로 동작하는 색인 구조를 로그 구조화 병합 트리(Log-Structured Merge-Tree, &lt;b&gt;LSM트리&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;size18&quot;&gt;&lt;b&gt;성능 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSM 트리 알고리즘은 DB에 존재하지 않는 키를 찾는 경우 느릴 수 있는데, 만약 키가 존재하지 않으면 멤테이블을 확인한 후 가장 오래된 세그먼트까지 거슬러 올라가야 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 종류의 접근을 최적화하기 위해 보통 블룸 필터(Bloom Filter)를 추가로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 블룸 필터는 키가 DB에 존재하지 않음을 알려줘서 불필요한 디스크 읽기를 절약할 수 있다.&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;B 트리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSM 트리가 점점 보편화되고 있지만, 가장 널리 사용되는 색인 구조는 B 트리(B-tree)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1970년대에 등장해 거의 대부분의 관계형 데이터베이스에서 표준 색인 구현으로 B 트리를 사용할 뿐 아니라 많은 비관계형 데이터베이스에서도 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 트리는 SS테이블처럼 키로 정렬된 키-값 쌍을 유지하므로, 검색과 범위 질의에 효율적이지만 이 점을 제외하곤 비슷한 점이 없으며 설계철학이 매우 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 트리는 고정 크기 블록으로 배열된 디스크에 전통적으로 4KB의 고정 크기 페이지로 나누고 한 번에 하나의 페이지에 읽기 쓰기 작업을 한다. (근본적으로 하드웨어와 밀접한 관계가 있다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r2LCU/btrXrUn7v1W/RgM2BPnHfX2FKA1ETSD3Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r2LCU/btrXrUn7v1W/RgM2BPnHfX2FKA1ETSD3Q1/img.png&quot; data-alt=&quot;참조 :&amp;amp;amp;nbsp;https://ebrary.net/64649/computer_science/trees#689&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r2LCU/btrXrUn7v1W/RgM2BPnHfX2FKA1ETSD3Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr2LCU%2FbtrXrUn7v1W%2FRgM2BPnHfX2FKA1ETSD3Q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;395&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 :&amp;amp;nbsp;https://ebrary.net/64649/computer_science/trees#689&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 페이지는 주소나 위치를 이용해 식별이 가능하며, 하나의 페이지가 다른 페이지를 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중 맨 위 페이지가 루트(root)로 지정되고, 여러 키와 하위 페이지의 주소 값을 포함한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 트리는 계속 균형을 유지함을 보장하며, n개의 키를 가진 B 트리는 깊이가 항상 O(log n)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 데이터베이스는 깊이가 3이나 4단계 정도면 충분하므로 많은 페이지 참조를 따라가지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(분기계수가 500인 4KB 페이지의 4단계 트리는 256TB까지 저장할 수 있다.)&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;신뢰할 수 있는 B 트리 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 트리의 기본적인 쓰기 동작은 새로운 데이터를 디스크 상의 페이지에 덮어쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 동작은 덮어쓰기가 페이지 위치를 변경하지 않는다고 가정하며, 페이지를 덮어쓰더라도 페이지를 가리키는 모든 참조가 유지된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(LSM 트리와는 대조적으로, LSM 트리는 파일에 추가만 할 뿐 같은 위치의 파일은 변경하지 않는다. 또한 합병과 컴팩션을 거치며 특정 키의 주소가 지속적으로 변경될 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 트리 구조의 DB가 고장 난 상황에서 스스로 복구할 수 있게 만들려면, 일반적으로 디스크 상에 쓰기 전 로그(Write-ahead log, WAL)라고 하는 데이터 구조를 추가해 B 트리를 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WAL은 트리 페이지에 변경된 내용을 적용하기 이전에 모든 B 트리의 변경 사항을 기록하는 추가 전용 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 DB가 고장 난 이후 복구할 시 일관성 있는 B 트리로 복원하는 데 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복구 시 주의 사항이 몇 가지 있는데, 같은 자리의 페이지를 갱신하는 작업은 쉽지가 않다. 특히, 다중 스레드 접근이 가능하다면 동시성 제어까지 해야 한다. (그렇지 않으면 일관성이 깨진 상태의 데이터를 가진 B 트리에 접근되므로)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 동시성 제어는 래치(latch)로 데이터 구조를 보호한다.&lt;/p&gt;
&lt;p data-ke-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;size18&quot;&gt;&lt;b&gt;B 트리와 LSM 트리 비교&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;읽기 속도 : B 트리 &amp;gt; LSM 트리&lt;br /&gt;쓰기 속도 : B 트리 &amp;lt; LSM 트리&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 트리가 LSM 트리에 비해 읽기가 빠른 이유는 LSM 트리는 컴팩션 단계에 있는 여러 데이터 구조와 SS 테이블을 확인하는데 비용이 들기 때문이다. (하지만 실제 다루는 데이터마다 성능이 달라질 수 있으므로 시스템 부하 테스트 후 선정하는 것이 좋다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSM 트리의 장점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;쓰기 증폭&lt;/b&gt; (DB 쓰기 요청 1번을 수행할 때 디스크에 N번 작업이 수행되는 효과)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;B 트리 인덱스는 모든 데이터 조작을 최소 2번 기록해야 한다. (쓰기 전 로그 1번 + 트리 페이지에 1번)&lt;/li&gt;
&lt;li&gt;또한, 페이지 내 몇 바이트만 변경이 일어나더라도 한 번에 전체 페이지를 기록해야 하는 오버헤드도 존재한다.&lt;/li&gt;
&lt;li&gt;SSD의 경우 수명이 다할 때까지 블록 덮어쓰기 횟수가 제한되므로 이 쓰기 증폭이 큰 관심사이다.&lt;/li&gt;
&lt;li&gt;쓰기가 많은 어플리케이션에 성능 병목은 DB가 디스크에 쓰는 속도 일 수도 있으며 이 경우 쓰기 증폭이 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;성능 비용&lt;/span&gt;이 된다.&lt;/li&gt;
&lt;li&gt;LSM 트리 또한 SS테이블의 반복된 컴팩션과 병합으로 인해 N번 데이터를 다시 쓰긴 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쓰기 처리량&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;LSM 트리가 B 트리에 비해 상대적으로 쓰기 증폭이 더 낮고, 트리에서 페이지를 덮어쓰는 것이 아닌 순차적으로 컴팩션된 SS테이블을 쓰기 때문이다.&lt;/li&gt;
&lt;li&gt;HDD에서 특히 중요한데 HDD에 데이터를 기록할 때 순차 쓰기가 임의 쓰기보다 훨씬 빠르다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;압축률&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;LSM 트리는 페이지 지향적이지 않고 주기적으로 파편화를 없애기 위해 SS테이블을 다시 기록하므로 저장소 오버헤드가 더 낮다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSM 트리의 단점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;컴팩션 과정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;컴팩션 과정이 때로는 진행 중인 읽기 쓰기 작업의 성능에 영향을 준다. (디스크에서 컴팩션 연산이 끝날 때까지 요청을 기다려야 하는 상황이 발생하기 쉬움)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;높은 쓰기 처리량&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;디스크의 쓰기 대역폭은 유한한데, 쓰기 처리량이 높아 DB가 점점 커질수록 컴팩션을 위해 더 많은 디스크 대역폭이 필요하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴팩션 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;컴팩션 설정을 주의 깊게 하지 않으면 컴팩션이 쓰기 요청 속도를 못 따라간다.&lt;/li&gt;
&lt;li&gt;보통 SS테이블 기반 저장소 엔진은 컴팩션이 유입 속도를 따라가지 못해도 유입 쓰기의 속도를 조절하지 않기에, 이런 상황을 감지하기 위한 명시적 모니터링이&amp;nbsp; 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;기타 색인 구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키-값 색인의 대표적인 예는 관계형 모델의 기본키(primary key) 색인이다.&lt;/p&gt;
&lt;p data-ke-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;보조 색인(secondary index)을 사용하는 방식도 일반적이다.&lt;/p&gt;
&lt;p data-ke-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;size18&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;실제 데이터이거나 실제 데이터가 위치한 로우를 가리키는 참조 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후자의 경우 로우가 저장된 곳을 힙 파일(heap file)이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙 파일의 접근 방식은 키를 변경하지 않고 값을 갱신할 때 효율적이며, 새로운 값이 많은 공간을 필요로 하지 않으면 제자리에 덮어쓰고 그렇지 않다면 힙에서 충분한 공간이 있는 새로운 곳으로 위치를 이동시킨 후 변경된 값과 관련이 있는 참조 값들을 모두 새로운 위치를 가리키도록 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Clusterd Index&lt;/b&gt; : 인덱스의 값을 읽고 다시 힙 파일로 가는 것은 읽기 성능이 떨어진다. 그래서 인덱스 값으로 찾고자 하는 로우 자체를 저장하기도 하며 이를 클러스터드 색인(Clustered Index)이라고 한다. (MySQL의 InnoDB 저장소 엔진에서는 테이블의 기본키가 Clustered Index이다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Covering Index&lt;/b&gt; : 클러스터드 색인과 비클러스터드 색인 사이의 절충안으로 색인 안에 테이블의 컬럼 일부를 저장한다. 이렇게 하면 색인만 사용해 일부 질의를 처리할 수 있어지고 이런 경우를 &quot;인덱스가 질의를 커버했다&quot;라고 표현한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Concatenated Index&lt;/b&gt; : 가장 일반적인 유형인 결합 색인이다. 하나의 컬럼에 다른 컬럼을 추가하는 방식으로 하나의 키에 여러 필드를 결합한다.&amp;nbsp; 이 방법은 키를 성과 이름값을 전화번호로 하는 색인을 제공하는 전화번호부와 비슷하다. (특정 성 이름 조합을 가진 사람을 찾을 때 유용하지만, 특정 이름을 가진 모든 사람을 찾을 때는 쓸모가 없다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fuzzy Index&lt;/b&gt; : 여타 다른 색인들처럼 정확한 데이터를 대상으로 질의하는 것이 아닌 철자가 틀린 단어처럼 애매모호한(fuzzy) 질의를 하는 색인이다. 트라이(Trie) 자료구조와 유사하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;디스크는 메인 메모리와 비교해 다루기 어려우며 HDD 혹은 SSD 사용 시 읽기/쓰기 작업에서 좋은 성능을 위해 디스크에 데이터를 잘 배치해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 까다로운데도 불구하고 디스크를 사용하는 이유로는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;지속성 (전원이 꺼져도 손실되지 않음)&lt;/li&gt;
&lt;li&gt;저렴한 가격&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 요즘 들어 램 가격이 저렴해지면서 메모리에 데이터를 저장하는 방식도 현실적으로 가능해졌고, 인메모리 DB가 개발되기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인메모리 DB의 장점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디스크에서 데이터를 읽지 않아도 된다. (하지만 디스크 기반 저장소 엔진도 OS가 최근에 사용한 디스크 블록을 메모리에 캐시 하며, 오히려 인메모리 데이터 구조를 디스크에 기록하기 위한 형태로 부호화하는 오버헤드를 피할 수 있어 더 빠를 수도 있다.)&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Priority Queue, Set과 같이 디스크 기반 색인으로 구현하기 어려운 데이터 모델을 제공한다. (Redis)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인메모리 DB의 단점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB가 재시작되는 경우 특수한 하드웨어를 사용하지 않는다면 디스크나 복제본에서 데이터를 다시 인메모리에 적재해야 한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>독서/데이터 중심 애플리케이션 설계</category>
      <category>B 트리</category>
      <category>LSM 트리</category>
      <category>데이터 중심 애플리케이션 설계</category>
      <category>해시</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/381</guid>
      <comments>https://hellominchan.tistory.com/381#entry381comment</comments>
      <pubDate>Sun, 29 Jan 2023 19:43:26 +0900</pubDate>
    </item>
    <item>
      <title>[Career] SW마에스트로 13기</title>
      <link>https://hellominchan.tistory.com/380</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SWM_Logos_Color_CMYK2.png&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CvtO8/btrWRDhAm7w/JYGfm7u13sAMwfMh5WkNI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CvtO8/btrWRDhAm7w/JYGfm7u13sAMwfMh5WkNI0/img.png&quot; data-alt=&quot;https://www.swmaestro.org/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CvtO8/btrWRDhAm7w/JYGfm7u13sAMwfMh5WkNI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCvtO8%2FbtrWRDhAm7w%2FJYGfm7u13sAMwfMh5WkNI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;301&quot; data-filename=&quot;SWM_Logos_Color_CMYK2.png&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.swmaestro.org/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 인생에서 제일 스펙타클했던 2022년이 끝나고 미루고 미루던 소마 13기 회고를 드디어 해본다..&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 지원 계기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소마에 대해서는 지원하기 한참 전부터 이미 알고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소마에 들어가면 얻을 수 있는 지원과 혜택이 너무 좋았고, 주위에 소마 출신인 분들이 몇 분 계셨는데 모두 꼭 들어가라고 입을 모아 얘기해 주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 나는 컴공에 들어온 이후 창업을 도전해 보고 싶었기에 소마는 나에게 최선의 선택지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 학교도 졸업할 겸, 2022년 1월 &lt;a href=&quot;https://hellominchan.tistory.com/362&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;잘 다니던 회사&lt;/a&gt;를 퇴사하고 내 꿈을 도전하러 소마를 선택했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 준비와 합격&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 준비과정은 별 다를 게 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 대기업 IT직군 채용 프로세스와 유사한 걸 넘어서 그냥 똑같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번의 코테와, 1번의 심층 면접을 통과하면 합격이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 당시 나는 무조건 합격한다는 마인드였던 터라 지원서 넣기도 전에 회사를 퇴사해서..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤가 없었기에 2022년 초를 소마 준비에 올인했다.&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;278143300_655771938816873_9160357333657213049_n.jpeg&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rqG7W/btrWYLEWhOE/sKPF3WrMGZ1HkPegTjnCx0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rqG7W/btrWYLEWhOE/sKPF3WrMGZ1HkPegTjnCx0/img.jpg&quot; data-alt=&quot;1차 코테 합격&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rqG7W/btrWYLEWhOE/sKPF3WrMGZ1HkPegTjnCx0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrqG7W%2FbtrWYLEWhOE%2FsKPF3WrMGZ1HkPegTjnCx0%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;500&quot; height=&quot;500&quot; data-filename=&quot;278143300_655771938816873_9160357333657213049_n.jpeg&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1차 코테 합격&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차 코테는 알고리즘 5문제와 SQL 1문제, 웹 1문제였던 걸로 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(학교 후배들한테 들었는데 이번 14기부터는 웹이 빠졌다고 한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 알고리즘 1문제를 제외하고 전부 풀었는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같이 준비하던 친구들과 복기할 때 SQL문제에서 실수로 부등호를 반대로 썼단 걸 깨달았었다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 7문제 중 5솔로 통과 한 것 같다.&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;278117739_346316180641616_3530457914410420791_n.jpeg&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uHX5C/btrWVKfupDe/7FWkcE3ZslPDjcCJhgAUCK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uHX5C/btrWVKfupDe/7FWkcE3ZslPDjcCJhgAUCK/img.jpg&quot; data-alt=&quot;2차 코테 합격&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uHX5C/btrWVKfupDe/7FWkcE3ZslPDjcCJhgAUCK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuHX5C%2FbtrWVKfupDe%2F7FWkcE3ZslPDjcCJhgAUCK%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;500&quot; height=&quot;500&quot; data-filename=&quot;278117739_346316180641616_3530457914410420791_n.jpeg&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2차 코테 합격&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차 코테는 알고리즘 3문제와 SQL 1문제, 웹 1문제였던 걸로 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 5문제로 문제수는 줄었지만 알고리즘 난이도가 훨씬 올라갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 체감 백준 solved.ac기준 골드 ~ 플레정도의 난이도였던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차에서도 알고리즘 1문제를 제외하고 다 풀었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 문제 중 2번이었나 잘 기억이 안 나지만 문제 보자마자 이건 풀면 시간 다 쓴다 싶어서 쳐다보지도 않았었다. ㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 오면 합격 메일에서도 &lt;b&gt;지원자님&lt;/b&gt;에서 &lt;b&gt;정민찬님&lt;/b&gt;으로 이름을 불러주기 시작하는 듯..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;277990194_168141522226684_3724029837698050563_n.jpeg&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;945&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sZwDF/btrWYNv0Z7U/0yYk7G62QXvbC6lByMp3b1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sZwDF/btrWYNv0Z7U/0yYk7G62QXvbC6lByMp3b1/img.jpg&quot; data-alt=&quot;심층 면접, 최종 합격&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sZwDF/btrWYNv0Z7U/0yYk7G62QXvbC6lByMp3b1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsZwDF%2FbtrWYNv0Z7U%2F0yYk7G62QXvbC6lByMp3b1%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;500&quot; height=&quot;500&quot; data-filename=&quot;277990194_168141522226684_3724029837698050563_n.jpeg&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;945&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;2차 코테를 합격하면 심층 면접인데 코테를 넘었다고 안심하면 경기도 오산이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접이 인성 면접이 아닌 말 그대로 심층 면접이라 준비를 철저히 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라떼 기준 3분 자기소개 스피칭과 제출했던 포트폴리오 기반 사용 기술, CS 질문이 날카롭게 들어온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접은 분과로 나누어진 다대다 면접인데 고르게 질문이 들어오므로 정신줄 놓고 있으면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후에 합격자들과 얘기를 나눠보니 특정 분과마다 CS 질문이 많은 분과, 포트폴리오 기반 질문이 많은 분과로 나뉘는 것 같았는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 포트폴리오 기반 질문이 많은 분과였고, 면접관 분들이 깃허브까지 라이브로 열어보셨다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 기회는 준비된 자에게 온다고 하는 게 맞는지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설마 이렇게까지 딥하게 물어보겠어?라고 생각했지만 그래도 완벽하게 준비하자 하고 공부했던 것이 질문으로 들어와 잘 대답할 수 있었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;준비된 지원자시네요&quot;라는 면접관님의 그린 라이트를 듣고 웃으며 면접장을 나갈 수 있었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 프로젝트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴사 전부터 하고 싶었던 아이템을 미리 구상하고 소마에 들어왔던 터라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 주도적으로 필요한 직군의 팀원들을 모집해 프로젝트를 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템은 맛집 추천 및 기록 플랫폼인데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 살아가는데 필요한 &lt;b&gt;의&lt;/b&gt;, &lt;b&gt;식&lt;/b&gt;, &lt;b&gt;주&lt;/b&gt; 이 3가지 도메인 중 &lt;b&gt;주&lt;/b&gt;는 &lt;a href=&quot;https://hellominchan.tistory.com/179&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이미 해봤고&lt;/a&gt; &lt;b&gt;의&lt;/b&gt;는 자신이 없어서 &lt;b&gt;식&lt;/b&gt;내에서 문제를 찾아 해결해 보기 위한 아이템이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 해결하고 싶었던 문제는 &quot;외식비 부담이 점점 커지는 상황 속에서 만족스러운 맛집을 찾고 싶다&quot; 였는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 나뿐만이 아닌 많은 사람들이 공감하는 니즈였고, 사람마다 입맛이 다른데 기존의 평점이나 별점으로 음식점을 찾아가는 시스템이 적합하지 않다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 성격유형검사인 엠비티아이에 착안하여 직접 만든 입맛유형검사인 푸이티아이를 기반으로 개개인의 입맛에 맞게 음식점을 추천하는 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;푸이&lt;/span&gt; 프로젝트를 리드했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(푸이는 fooiy이고 fooiy is yummy의 준말이다.. 여러 서비스명 후보 중 닷컴 도메인이 살아있어서 fooiy로 확정하게 되었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과물&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;홈페이지 : &lt;a href=&quot;http://www.fooiy.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2150&quot; data-origin-height=&quot;1602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuhcbu/btrWUb5y8yr/QgIzQ4ugaGUNU3gGMG6Rh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuhcbu/btrWUb5y8yr/QgIzQ4ugaGUNU3gGMG6Rh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuhcbu/btrWUb5y8yr/QgIzQ4ugaGUNU3gGMG6Rh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcuhcbu%2FbtrWUb5y8yr%2FQgIzQ4ugaGUNU3gGMG6Rh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;522&quot; data-origin-width=&quot;2150&quot; data-origin-height=&quot;1602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구글 플레이 : &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.fooiy.fooiy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1674467787412&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;푸이 - 내 손안의 모든 음식점 - Google Play 앱&quot; data-og-description=&quot;내 입맛에 딱! 맞는 음식점&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.fooiy.fooiy&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.fooiy.fooiy&amp;amp;hl=ko&amp;amp;gl=KR&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bgu0PL/hyRm77xMKT/mutomAeNWn0WwxaaWqyRk1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/LbZhi/hyRmYQimOG/WVuhFkhbbdpQKBUkPCyidk/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/GNVSM/hyRm9Ehbzi/gOjm8PSvffv8GZdN6nEcH0/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.fooiy.fooiy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.fooiy.fooiy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bgu0PL/hyRm77xMKT/mutomAeNWn0WwxaaWqyRk1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/LbZhi/hyRmYQimOG/WVuhFkhbbdpQKBUkPCyidk/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/GNVSM/hyRm9Ehbzi/gOjm8PSvffv8GZdN6nEcH0/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;푸이 - 내 손안의 모든 음식점 - Google Play 앱&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;내 입맛에 딱! 맞는 음식점&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;play.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱스토어 : &lt;a href=&quot;https://apps.apple.com/us/app/%ED%91%B8%EC%9D%B4-%EB%82%B4-%EC%86%90%EC%95%88%EC%9D%98-%EB%AA%A8%EB%93%A0-%EC%9D%8C%EC%8B%9D%EC%A0%90/id1640024571&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1011&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SDV3Y/btrWTJIcgyW/ME9u1vdEfcKAdwNPj35lKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SDV3Y/btrWTJIcgyW/ME9u1vdEfcKAdwNPj35lKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SDV3Y/btrWTJIcgyW/ME9u1vdEfcKAdwNPj35lKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSDV3Y%2FbtrWTJIcgyW%2FME9u1vdEfcKAdwNPj35lKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;591&quot; data-origin-width=&quot;1011&quot; data-origin-height=&quot;854&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;출시 기사 : &lt;a href=&quot;https://news.mt.co.kr/mtview.php?no=2022091716551984413#:~:text=%ED%91%B8%EC%9D%B4(%EB%8C%80%ED%91%9C%20%EC%A0%95%EB%AF%BC%EC%B0%AC)%EA%B0%80%20%EC%8B%A0%EA%B0%9C%EB%85%90,%EC%9D%84%20%EC%86%8C%EA%B0%9C%ED%95%98%EB%8A%94%20%EC%95%B1%EC%9D%B4%EB%8B%A4.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1674468396897&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;푸이, 신개념 맛집 소개 앱 '푸이'(fooiy) 출시 - 머니투데이&quot; data-og-description=&quot;푸이(대표 정민찬)가 신개념 맛집 소개 앱(애플리케이션) &amp;quot;푸이&amp;quot;(fooiy)를 출시했다고 17일 밝혔다.푸이는 성격유형 검사 &amp;quot;MBTI&amp;amp;qu...&quot; data-og-host=&quot;news.mt.co.kr&quot; data-og-source-url=&quot;https://news.mt.co.kr/mtview.php?no=2022091716551984413#:~:text=%ED%91%B8%EC%9D%B4(%EB%8C%80%ED%91%9C%20%EC%A0%95%EB%AF%BC%EC%B0%AC)%EA%B0%80%20%EC%8B%A0%EA%B0%9C%EB%85%90,%EC%9D%84%20%EC%86%8C%EA%B0%9C%ED%95%98%EB%8A%94%20%EC%95%B1%EC%9D%B4%EB%8B%A4.&quot; data-og-url=&quot;https://news.mt.co.kr/mtview.php?no=2022091716551984413&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfdKts/hyRm68DOzW/v7Njad2bSPNZxjevSbLBP1/img.jpg?width=1024&amp;amp;height=500&amp;amp;face=0_0_1024_500,https://scrap.kakaocdn.net/dn/bS89z4/hyRm10zdSH/LO5mMi9eWCW0dVnQw81GoK/img.jpg?width=1200&amp;amp;height=586&amp;amp;face=0_0_1200_586&quot;&gt;&lt;a href=&quot;https://news.mt.co.kr/mtview.php?no=2022091716551984413#:~:text=%ED%91%B8%EC%9D%B4(%EB%8C%80%ED%91%9C%20%EC%A0%95%EB%AF%BC%EC%B0%AC)%EA%B0%80%20%EC%8B%A0%EA%B0%9C%EB%85%90,%EC%9D%84%20%EC%86%8C%EA%B0%9C%ED%95%98%EB%8A%94%20%EC%95%B1%EC%9D%B4%EB%8B%A4.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://news.mt.co.kr/mtview.php?no=2022091716551984413#:~:text=%ED%91%B8%EC%9D%B4(%EB%8C%80%ED%91%9C%20%EC%A0%95%EB%AF%BC%EC%B0%AC)%EA%B0%80%20%EC%8B%A0%EA%B0%9C%EB%85%90,%EC%9D%84%20%EC%86%8C%EA%B0%9C%ED%95%98%EB%8A%94%20%EC%95%B1%EC%9D%B4%EB%8B%A4.&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfdKts/hyRm68DOzW/v7Njad2bSPNZxjevSbLBP1/img.jpg?width=1024&amp;amp;height=500&amp;amp;face=0_0_1024_500,https://scrap.kakaocdn.net/dn/bS89z4/hyRm10zdSH/LO5mMi9eWCW0dVnQw81GoK/img.jpg?width=1200&amp;amp;height=586&amp;amp;face=0_0_1200_586');&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;푸이, 신개념 맛집 소개 앱 '푸이'(fooiy) 출시 - 머니투데이&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;푸이(대표 정민찬)가 신개념 맛집 소개 앱(애플리케이션) &quot;푸이&quot;(fooiy)를 출시했다고 17일 밝혔다.푸이는 성격유형 검사 &quot;MBTI&amp;amp;qu...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;news.mt.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공식 인스타 : &lt;a href=&quot;https://www.instagram.com/fooiy_official/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;푸이에서 나는 팀의 대표로서 팀에 필요한 전반적인 업무(마케팅, 서류 작업, 발표 등)를 책임졌고, 개발적으론 백엔드 및 인프라를 담당했으며 웹 프론트도 개발했다.&lt;/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;fooiy infrastructure.drawio.png&quot; data-origin-width=&quot;1655&quot; data-origin-height=&quot;1029&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FezIK/btrW0PAmZS4/wBaVYpL1cjRVxjkqDLm721/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FezIK/btrW0PAmZS4/wBaVYpL1cjRVxjkqDLm721/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FezIK/btrW0PAmZS4/wBaVYpL1cjRVxjkqDLm721/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFezIK%2FbtrW0PAmZS4%2FwBaVYpL1cjRVxjkqDLm721%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;435&quot; data-filename=&quot;fooiy infrastructure.drawio.png&quot; data-origin-width=&quot;1655&quot; data-origin-height=&quot;1029&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소마에서 얻을 수 있는 혜택 중 내가 생각하기에 가장 좋은 점은 팀마다 멘토님 3분을 모실 수 있다는 것인데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하며 겪는 수많은 문제들을 업계 최고이신 멘토님들의 도움을 받아 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 이는 돈으로 매길 수 없는 가치라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀의 경우 3명을 모실 수 있는 카드를 창업, 프론트, 백엔드 이렇게 각각 특화하여 전략적으로 모셨는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창업의 경우 엑싯 하시고 카카오 모빌리티에서 이사님으로 계신 조재화 멘토님,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트의 경우 네이버를 거쳐 쿠팡에 계셨던 김종찬 멘토님 (현재는 &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;Presto Labs로 이직하심)&lt;/span&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드의 경우 구글을 거쳐 Pala에서 백엔드 리드를 하고 계시는 박주람 멘토님까지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 분 덕분에 푸이를 무사히 출시할 수 있었고 많이 배울 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 우리 팀의 경우 취업보단 창업을 목적으로 소마 과정을 달려왔기에 서비스를 개발하기 바빠 다른 팀처럼 활발하게 멘토님들과 교류하지는 못했지만, 중간중간 주시는 인사이트와 경험은 어디서도 얻지 못할 그런 엄청난 것들이었다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 백엔드를 담당한 나로선 메인 멘토이신 박주람 멘토님에게 단순히 코드적인 도움뿐만 아니라 구글의 문화와 개발 방식, 소프트웨어 엔지니어로서의 관점 등 많은 영감을 받고 성장할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(김종찬 멘토님 또한 프론트의 관점에서 백엔드의 역할을 깊게 생각해 보는 계기가 되었고, 조재화 멘토님 또한 서비스를 생각하는 사고방식을 더욱 성숙하게 만들어 주셨다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 수료&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말하기엔 너무 많지만 어찌저찌 역경들을 헤쳐나가며, 결국 소마 13기 수료했다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SODA1671095357.JPG&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZlncX/btrWU6QvTfB/eQR3HX84urVehlhYzwKUjK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZlncX/btrWU6QvTfB/eQR3HX84urVehlhYzwKUjK/img.jpg&quot; data-alt=&quot;수료식장에서 셀카 ㅎ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZlncX/btrWU6QvTfB/eQR3HX84urVehlhYzwKUjK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZlncX%2FbtrWU6QvTfB%2FeQR3HX84urVehlhYzwKUjK%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;500&quot; height=&quot;500&quot; data-filename=&quot;SODA1671095357.JPG&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2160&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;IMG_8007.JPG&quot; data-origin-width=&quot;2845&quot; data-origin-height=&quot;2845&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cF2HTp/btrWVLlfYhN/iKvDVI0kcIDGnCnR2YU7Fk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cF2HTp/btrWVLlfYhN/iKvDVI0kcIDGnCnR2YU7Fk/img.jpg&quot; data-alt=&quot;세 얼간이&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cF2HTp/btrWVLlfYhN/iKvDVI0kcIDGnCnR2YU7Fk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcF2HTp%2FbtrWVLlfYhN%2FiKvDVI0kcIDGnCnR2YU7Fk%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;500&quot; height=&quot;500&quot; data-filename=&quot;IMG_8007.JPG&quot; data-origin-width=&quot;2845&quot; data-origin-height=&quot;2845&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;IMG_8008.JPG&quot; data-origin-width=&quot;2547&quot; data-origin-height=&quot;2547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EDFI7/btrWYLE0yI9/FRG2COlwrm6LpxbbmD8rf0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EDFI7/btrWYLE0yI9/FRG2COlwrm6LpxbbmD8rf0/img.jpg&quot; data-alt=&quot;졸업도 안했는데 학사모 던짐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EDFI7/btrWYLE0yI9/FRG2COlwrm6LpxbbmD8rf0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEDFI7%2FbtrWYLE0yI9%2FFRG2COlwrm6LpxbbmD8rf0%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;500&quot; height=&quot;500&quot; data-filename=&quot;IMG_8008.JPG&quot; data-origin-width=&quot;2547&quot; data-origin-height=&quot;2547&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;IMG_8006.JPG&quot; data-origin-width=&quot;2850&quot; data-origin-height=&quot;2850&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaSPDn/btrWTJ2woU7/Q0w4tlNkgCmwl5QHwXmMKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaSPDn/btrWTJ2woU7/Q0w4tlNkgCmwl5QHwXmMKK/img.jpg&quot; data-alt=&quot;너도 고생했다 ㅋㅋㅋ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaSPDn/btrWTJ2woU7/Q0w4tlNkgCmwl5QHwXmMKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaSPDn%2FbtrWTJ2woU7%2FQ0w4tlNkgCmwl5QHwXmMKK%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;500&quot; height=&quot;500&quot; data-filename=&quot;IMG_8006.JPG&quot; data-origin-width=&quot;2850&quot; data-origin-height=&quot;2850&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-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5igyK/btrWU8t2HRi/WfqtiHWkKcWtD2qVoxWKO0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5igyK/btrWU8t2HRi/WfqtiHWkKcWtD2qVoxWKO0/img.jpg&quot; data-alt=&quot;회자정리 거자필반&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5igyK/btrWU8t2HRi/WfqtiHWkKcWtD2qVoxWKO0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5igyK%2FbtrWU8t2HRi%2FWfqtiHWkKcWtD2qVoxWKO0%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;500&quot; height=&quot;500&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회자정리 거자필반&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고/Career</category>
      <category>SW마에스트로</category>
      <category>소마 13기</category>
      <category>소프트웨어 마에스트로</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/380</guid>
      <comments>https://hellominchan.tistory.com/380#entry380comment</comments>
      <pubDate>Mon, 23 Jan 2023 20:19:35 +0900</pubDate>
    </item>
    <item>
      <title>[Operating Systems] 프로세스와 쓰레드</title>
      <link>https://hellominchan.tistory.com/379</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스와 쓰레드에 대해 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 프로세스(Process)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 프로세스란 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;실행 중인 프로그램&lt;/span&gt;을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 디스크로 부터 메모리에 적재되어 CPU 할당을 받을 수 있게 되며, 운영체제로 부터 주소 공간, 파일, 메모리 등을 할당받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 프로세스의 특징으로는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스는 각각 독립된 메모리 영역을 할당받는다 (아래의 메모리 구조 참고)&lt;/li&gt;
&lt;li&gt;기본적으로 프로세스당 최소 1개의 쓰레드(메인 쓰레드)를 가지고 있다.&lt;/li&gt;
&lt;li&gt;각 프로세스는 독립적인 메모리 공간에서 실행되므로 다른 프로세스에 접근이 불가능하다.&lt;/li&gt;
&lt;li&gt;프로세스 간 정보를 주고받으려면 IPC(Inter-Process Communication)를 사용해야 한다. (파이프, 공유 메모리, 소켓 등을 이용한 통신)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDDfhU/btrVU90ZVOS/EmW33gOoQpi1kB0Ks0cmEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDDfhU/btrVU90ZVOS/EmW33gOoQpi1kB0Ks0cmEK/img.png&quot; data-alt=&quot;참조 :&amp;amp;nbsp;https://gabrieletolomei.wordpress.com/miscellanea/operating-systems/in-memory-layout/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDDfhU/btrVU90ZVOS/EmW33gOoQpi1kB0Ks0cmEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDDfhU%2FbtrVU90ZVOS%2FEmW33gOoQpi1kB0Ks0cmEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;475&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 :&amp;nbsp;https://gabrieletolomei.wordpress.com/miscellanea/operating-systems/in-memory-layout/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Stack&lt;/b&gt; : 지역 변수와 함수 호출에 필요한 데이터가 저장되는 영역이다. (automatic 변수, 임시 변수, 함수 리턴시 돌아갈 주소값)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Heap&lt;/b&gt; : 동적 메모리 할당이 일어나는 영역으로, 런타임에 크기가 결정된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BSS&lt;/b&gt; : 초기화되지 않은 data 영역으로 해당 영역의 데이터는 프로그램 실행 전 OS 커널에 의해 0으로 초기화된다. (초기화 되지 않은 전역, 정적 변수)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Data&lt;/b&gt; : 초기화 된 data 영역이다. (초기화된 전역, 정적 변수)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Code(Text)&lt;/b&gt; : 실행가능한 명령어(소스코드)를 저장하고 있는 영역이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;프로세스의 상태&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스의 상태는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ulhNk/btrVTUwG4Ew/v1IW0fbXif2dhrA6QF1ws0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ulhNk/btrVTUwG4Ew/v1IW0fbXif2dhrA6QF1ws0/img.png&quot; data-alt=&quot;참조 :&amp;amp;nbsp;https://www.geeksforgeeks.org/states-of-a-process-in-operating-systems/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ulhNk/btrVTUwG4Ew/v1IW0fbXif2dhrA6QF1ws0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FulhNk%2FbtrVTUwG4Ew%2Fv1IW0fbXif2dhrA6QF1ws0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;395&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 :&amp;nbsp;https://www.geeksforgeeks.org/states-of-a-process-in-operating-systems/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;New&lt;/b&gt; : 프로세스가 생성 중인 상태&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ready&lt;/b&gt; : CPU의 할당을 기다리는 상태&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Running&lt;/b&gt; : CPU를 할당받아 instruction을 수행 중인 상태&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Blocked (wait, sleep)&lt;/b&gt; : I/O 등의 이벤트를 스스로 기다리는 상태 ex) 디스크에서 file 읽어 오기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Suspended (stopped)&lt;/b&gt; : 외부 이유로 프로세스의 수행이 정지된 상태 ex) 리눅스의 ctrl + z&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blocked 상태의 경우 자신이 요청한 이벤트가 완료되면 Ready 상태로 바뀌지만, Suspended 상태의 경우 외부에서 resume 명령을 내려야 Ready 상태로 전환된다.&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;PCB(Process Control Block)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCB란 프로세스에 대한 중요한 정보를 저장하고 있는 운영체제의 자료구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 프로세스의 생성과 동시에 고유한 PCB를 생성하며, 프로세스의 진행상황을 저장하고 불러오는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCB에 저장되는 정보들은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OS가 관리상 사용하는 정보
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Process state (New, Ready, Running 등의 프로세스 상태)&lt;/li&gt;
&lt;li&gt;Process ID (프로세스 식별번호)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CPU 수행 관련 하드웨어 값
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Program Counter (프로세스가 다음에 실행할 명령어의 주소)&lt;/li&gt;
&lt;li&gt;registers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메모리 관련 (code, data, stack 영역의 위치 정보)&lt;/li&gt;
&lt;li&gt;파일 관련 (Open file descriptors)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;문맥 교환 (Context Switch)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문맥 교환이란 CPU를 한 프로세스에서 다른 프로세스로 넘겨주는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문맥 교환은 cache memory flush 등 오버헤드가 많은 작업이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU가 다른 프로세스에게 넘어갈 때 운영체제가 수행하는 일은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CPU를 내어주는 프로세스의 상태를 그 프로세스의 PCB에 저장&lt;/li&gt;
&lt;li&gt;CPU를 새롭게 얻는 프로세스의 상태를 PCB에서 읽어옴&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;스케줄러 (Scheduler)&lt;/b&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;&lt;b&gt;Job Queue&lt;/b&gt; : 현재 시스템 내에 있는 모든 프로세스의 집합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ready Queue&lt;/b&gt; : 현재 메모리 내에 있으면서 CPU를 잡아 실행되길 기다리는 프로세스의 집합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Device Queue&lt;/b&gt; : Device I/O 작업을 대기하고 있는 프로세스의 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스들은 각 큐들을 오가며 수행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 큐에 프로세스들을 넣고 빼주는 스케줄러에도 세 가지 종류가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Long-term scheduler&lt;/b&gt; (장기 스케줄러, job scheduler)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;시작되는 프로세스들 중 어떤 것을 Ready Queue에 보낼지 결정한다.&lt;/li&gt;
&lt;li&gt;프로세스에 메모리를 할당하는 문제를 담당한다.&lt;/li&gt;
&lt;li&gt;degree of Multiprogramming을 제어한다. (실행 중인 프로세스의 수 제어)&lt;/li&gt;
&lt;li&gt;time sharing system에서는 보통 장기 스케줄러가 없다. (그냥 곧바로 메모리에 올라가 Ready 상태가 되기 때문)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Short-term scheduler&lt;/b&gt; (단기 스케줄러, CPU scheduler)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Ready Queue에 존재하는 프로세스 중 어떤 프로세스를 Running 상태로 바꿀지 결정한다.&lt;/li&gt;
&lt;li&gt;프로세스에 CPU를 할당하는 문제를 담당한다.&lt;/li&gt;
&lt;li&gt;millisecond 단위로 충분히 빨라야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Medium-term scheduler&lt;/b&gt; (중기 스케줄러, Swapper)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;여유 공간 마련을 위해 프로세스를 통째로 메모리에서 디스크로 쫓아낸다.&lt;/li&gt;
&lt;li&gt;프로세스에게서 메모리를 뺏는 문제를 담당한다.&lt;/li&gt;
&lt;li&gt;degree of Multiprogramming을 제어한다.&lt;/li&gt;
&lt;li&gt;현 시스템에서 메모리에 너무 많은 프로그램이 동시에 올라가는 것을 조절하는 스케줄러이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 쓰레드(Thread)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드란 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;프로세스의 실행 단위&lt;/span&gt;를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 프로세스 내에서 동작되는 여러 실행 흐름으로 프로세스 내의 주소 공간이나 자원을 공유함으로써 같은 일을 하고자 할 때 자원 낭비를 방지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드의 독립적인 구성요소는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Thread ID (쓰레드 식별번호)&lt;/li&gt;
&lt;li&gt;Program Counter&lt;/li&gt;
&lt;li&gt;register set&lt;/li&gt;
&lt;li&gt;stack space&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드가 동료 쓰레드와 공유하는 부분은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;code section&lt;/li&gt;
&lt;li&gt;data section&lt;/li&gt;
&lt;li&gt;heap section&lt;/li&gt;
&lt;li&gt;OS resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;stack space를 쓰레드마다 독립적으로 할당하는 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack space는 함수 호출 시 전달되는 인자와 되돌아갈 주소값 및 함수 내 변수 등을 저장하기 위한 메모리 공간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, stack space가 독립적이어야 독립적인 함수 호출이 가능해지며, 이를 통해 독립적인 실행 흐름이 추가될 수 있기 때문이다.&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;Program Counter를 쓰레드마다 독립적으로 할당하는 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program Counter는 instruction이 어디까지 수행되었는지를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드는 CPU를 할당받아도 스케줄러에 의해 다시 뺏기게 되므로 작업이 연속적으로 수행되지 않기 때문에 어느 부분까지 수행했는지 기억할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, Program Counter를 쓰레드마다 독립적으로 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 쓰레드의 장점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 쓰레드가 Blocked 상태여도 다른 쓰레드는 실행을 계속하는 것이 가능하다.&lt;/li&gt;
&lt;li&gt;쓰레드들끼리 프로세스의 리소스를 공유한다.&lt;/li&gt;
&lt;li&gt;쓰레드를 생성하는 것이 프로세스를 생성하는 것보다 비용이 적게들며, 문맥 교환(Context Switch)의 오버헤드가 적다.&lt;/li&gt;
&lt;li&gt;멀티 프로세서 구조의 이점을 활용할 수 있다. (각각의 쓰레드를 다른 프로세서에서 병렬로 수행할 수 있다.)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Operating Systems</category>
      <category>Operating Systems</category>
      <category>OS</category>
      <category>쓰레드</category>
      <category>운영체제</category>
      <category>프로세스</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/379</guid>
      <comments>https://hellominchan.tistory.com/379#entry379comment</comments>
      <pubDate>Tue, 10 Jan 2023 18:15:37 +0900</pubDate>
    </item>
    <item>
      <title>[Operating Systems] 인터럽트(Interrupt)</title>
      <link>https://hellominchan.tistory.com/378</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EugQV/btrVuorj0o5/Se6txrdnKfMk8XF9ckKgIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EugQV/btrVuorj0o5/Se6txrdnKfMk8XF9ckKgIK/img.png&quot; data-alt=&quot;참조 : https://www.javatpoint.com/what-is-interrupt-in-os&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EugQV/btrVuorj0o5/Se6txrdnKfMk8XF9ckKgIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEugQV%2FbtrVuorj0o5%2FSe6txrdnKfMk8XF9ckKgIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;240&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 : https://www.javatpoint.com/what-is-interrupt-in-os&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS의 인터럽트를 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 인터럽트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터럽트란 CPU가 프로그램을 실행하고 있을 때, 입출력 하드웨어 등의 장치에 예외 상황이 발생하여 처리가 필요할 경우에 CPU에게 알려 처리할 수 있도록 하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, CPU를 점유한 프로그램이 프린터에 명령을 내려 프린팅을 요청해야 하는 상황을 가정해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 프린터가 준비완료될 때까지 기다린다면 가장 비싼 자원인 CPU가 낭비되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;낭비를 막고 사용 효율을 높일 수 있는 방법은 프린터가 준비되면 CPU에게 준비됐음을 알리는 신호를 보내고, 그전까지 CPU는 다른 작업을 계속 수행하는 것인데 이것이 바로 인터럽트 메카니즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 운영체제는 이러한 인터럽트에 의해 구동되는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 인터럽트가 들어와야 CPU를 점유하여 사용하며, 그렇지 않은 대부분의 시간은 사용자 프로그램이 CPU를 점유하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 인터럽트에는 다음과 같은 두 가지 종류가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;하드웨어 인터럽트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;전원 이상 인터럽트 : 정전, 파워 이상 등&lt;/li&gt;
&lt;li&gt;기계 착오 인터럽트 : CPU의 기능 오류&lt;/li&gt;
&lt;li&gt;외부 신호 인터럽트 : 타이머, 키보드로 인터럽트 키를 누른 경우(Window에서 Control + Alt + Delete) 등&lt;/li&gt;
&lt;li&gt;I/O 컨트롤러 인터럽트 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;소프트웨어 인터럽트(Trap)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;프로그램이 커널 함수를 호출(시스템 콜)하는 경우&lt;/li&gt;
&lt;li&gt;Exception이 발생한 경우 (나누기 0 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Operating Systems</category>
      <category>interrupt</category>
      <category>Operating Systems</category>
      <category>OS</category>
      <category>운영체제</category>
      <category>인터럽트</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/378</guid>
      <comments>https://hellominchan.tistory.com/378#entry378comment</comments>
      <pubDate>Fri, 6 Jan 2023 01:00:56 +0900</pubDate>
    </item>
    <item>
      <title>[데이터 중심 애플리케이션 설계] 데이터 모델과 질의 언어</title>
      <link>https://hellominchan.tistory.com/377</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNUWGC/btrU43WwSKu/AQ0XK1oLkTi2sgqgj02d41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNUWGC/btrU43WwSKu/AQ0XK1oLkTi2sgqgj02d41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNUWGC/btrU43WwSKu/AQ0XK1oLkTi2sgqgj02d41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNUWGC%2FbtrU43WwSKu%2FAQ0XK1oLkTi2sgqgj02d41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;561&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001766328&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;데이터 중심 애플리케이션 설계&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;를 독파하며 정리하는 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 모델은 소프트웨어 개발에서 중요한 부분임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어가 어떻게 작성됐는지 뿐만 아니라, 해결하려는 문제를 어떻게 생각해야 하는지에 대해서도 영향을 미치기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 데이터 모델은 그 위에서 소프트웨어가 할 수 있는 일과 할 수 없는 일에 영향을 주므로 애플리케이션에 적합한 데이터 모델을 선택하는 작업이 상당히 중요함.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 관계형 모델과 문서 모델&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘날 가장 잘 알려진 데이터 모델은 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;관계형 모델&lt;/span&gt;을 기반으로 한 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;SQL&lt;/span&gt;임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL에서 데이터는 &lt;b&gt;테이블(table)&lt;/b&gt;이라 불리는 &lt;b&gt;관계(relation)&lt;/b&gt;로 구성되고, 각 관계는 &lt;b&gt;로우(row)&lt;/b&gt;라 불리는 순서 없는 &lt;b&gt;튜플(tuple)&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;트랜잭션 처리&lt;/b&gt; : 영업이나 은행 거래, 항공 예약, 창고에 재고 보관&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일괄 처리&lt;/b&gt; : 고객 송장 작성, 급여 지불, 보고&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 데이터 베이스의 목표는 정리된 인터페이스 뒤로 구현 세부 사항을 숨기는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-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;size18&quot;&gt;&lt;b&gt;NoSQL의 탄생&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2010년대에 관계형 모델의 우위를 뒤집기 위해 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;NoSQL&lt;/span&gt;이 등장했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름에 대해 말이 많은데 NoSQL은 원래 오픈소스, 분산 환경, 비관계형 데이터베이스 밋업용 인기 트위터 해시태그 였고, 이게 확산되어 인기 있는 여러 데이터베이스 시스템과 NoSQL이 연관되어 버림.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;그래서 Not Only SQL로 재해석됨.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL 데이터베이스가 채택된 데는 다음과 같은 이유가 있음.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관계형 모델에서 지원하지 않는 특수 질의 동작&lt;/li&gt;
&lt;li&gt;관계형 스키마의 제한에 대한 불만과 더욱 동적이고 표현력이 풍부한 데이터 모델의 니즈&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션은 저마다 요구사항이 다르므로, 미래에는 관계형 데이터베이스가 폭넓은 다양함을 가진 비관계형 데이터베이스와 함께 사용될 것임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 개념을 &lt;b&gt;다중 저장소 지속성(polyglot persistence)&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;size18&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;그에 따라 데이터를 관계형 테이블에 저장하려면 애플리케이션 코드와 데이터베이스 모델 객체(테이블, 로우, 칼럼) 사이에 거추장스러운 전환 계층이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 모델 사이의 분리를 &lt;b&gt;임피던스 불일치(impedance mismatch)&lt;/b&gt;라고 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ActiveRecord나 Hibernate 같은 ORM 프레임워크가 전환 계층에 필요한 boilerplate 코드의 양을 줄이지만, 두 모델 간의 차이를 완벽히 숨길수는 없음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/de2eqI/btrVf8heNqe/Igv5N6KYSmXLHllgtp1VH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/de2eqI/btrVf8heNqe/Igv5N6KYSmXLHllgtp1VH1/img.png&quot; data-alt=&quot;참조 :&amp;amp;amp;nbsp;https://ebrary.net/64616/computer_science/object_relational_mismatch#648&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/de2eqI/btrVf8heNqe/Igv5N6KYSmXLHllgtp1VH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fde2eqI%2FbtrVf8heNqe%2FIgv5N6KYSmXLHllgtp1VH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;680&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 :&amp;amp;nbsp;https://ebrary.net/64616/computer_science/object_relational_mismatch#648&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은 링크드인 프로필로 관계형 스키마에서 이력서를 어떻게 표현하는지 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로필 전체는 고유 식별자인 user_id로 식별한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;first_name과 last_name 같은 필드는 사용자마다 정확하게 하나씩 존재해 users 테이블의 칼럼으로 모델링이 가능하지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경력과 학력 기간, 연락처 정보는 하나 이상인&amp;nbsp;&lt;span style=&quot;background-color: #ffff00;&quot;&gt;일대다(one-to-many)&lt;/span&gt; 관계여서 first_name, last_name처럼 모델링이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 관계를 구현하기 위한 방법으로는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경력, 학력 기간, 연락처 정보를 개별 테이블로 만들어 외래키로 users 테이블을 참조함. (일반적인 방법)&lt;/li&gt;
&lt;li&gt;SQL 표준 마지막 버전에서 구조화된 데이터타입과 XML 데이터에 대한 지원이 추가되어, 단일 로우에 다중 값을 저장할 수 있음.&lt;/li&gt;
&lt;li&gt;JSON이나 XML로 부호화해서 데이터베이스에 저장한 다음 애플리케이션이 구조와 내용을 해석하게 함. (데이터베이스에서 부호화된 칼럼 값 질의 불가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데, 이런 이력서와 같은 데이터 구조는 모든 내용을 갖추고 있는 문서라서 JSON 표현에 매우 적합함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고 DB, 리싱크 DB, 카우치 DB 같은 문서 지향 데이터베이스는 JSON 데이터 모델을 지원하므로, 다음과 같이 데이터를 저장할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1672664306323&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;user_id&quot;: 251,
  &quot;first_name&quot;: &quot;Bill&quot;,
  &quot;last_name&quot;: &quot;Gates&quot;,
  &quot;summary&quot;: &quot;Co-chair of the Bill &amp;amp; Melinda Gates... Active blogger.&quot;,
  &quot;region_id&quot;: &quot;us:91&quot;,
  &quot;industry_id&quot;: 131,
  &quot;photo_url&quot;: &quot;/p/7/000/253/05b/308dd6e.jpg&quot;,
  &quot;positions&quot;: [
    {
      &quot;job_title&quot;: &quot;Co-chair&quot;,
      &quot;organization&quot;: &quot;Bill &amp;amp; Melinda Gates Foundation&quot;
    },
    {
      &quot;job_title&quot;: &quot;Co-founder, Chairman&quot;,
      &quot;organization&quot;: &quot;Microsoft&quot;
    }
  ],
  &quot;education&quot;: [
    {
      &quot;school_name&quot;: &quot;Harvard University&quot;,
      &quot;start&quot;: 1973,
      &quot;end&quot;: 1975
    },
    {
      &quot;school_name&quot;: &quot;Lakeside School, Seattle&quot;,
      &quot;start&quot;: null,
      &quot;end&quot;: null
    }
  ],
  &quot;contact_info&quot;: {
    &quot;blog&quot;: &quot;http://thegatesnotes.com&quot;,
    &quot;twitter&quot;: &quot;http://twitter.com/BillGates&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 JSON 표현은 다중 테이블 스키마보다 더 나은 &lt;b&gt;지역성(locality)을&lt;/b&gt; 갖는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 예제에서 프로필을 가져오려면 다중 질의(각 테이블마다 user_id로 질의)하거나, users 테이블과 그 하위 테이블 간에 다중 조인을 수행해야 한다. 그러나 JSON 표현에서는 모든 관련 정보가 한 곳에 있어 질의 하나로 충분하다.&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;앞전 그림을 보면 region_id와 industry_id는 문자열 데이터가 아닌 ID로 주어진 걸 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 지역과 업계를 입력하는 텍스트 필드가 있다면 문자열 저장이 합리적이지만, 드롭다운 리스트나 자동 완성 기능으로 표준 목록을 제공하여 사용자가 선택하게 하기 위해서이다. 이 방식의 장점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로필 간 일관된 스타일과 철자&lt;/li&gt;
&lt;li&gt;모호함 회피 (이름이 같은 여러 도시가 있는 경우)&lt;/li&gt;
&lt;li&gt;갱신의 편의성 (이름이 한 곳에만 저장되므로 변경하기 쉽다)&lt;/li&gt;
&lt;li&gt;현지화 지원 (다른 언어로 번역할 때)&lt;/li&gt;
&lt;li&gt;더 나은 검색 (시애틀이 워싱턴에 있다는 사실을 부호화, 시애틀이 워싱턴에 있지만, 그레이터 시애틀 구역이라는 문자열만으로는 워싱턴을 식별하지 못함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 ID와 텍스트 문자열의 저장 여부는 중복의 문제임.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ID 사용&lt;/b&gt; : 의미 있는 정보를 한 곳에 저장한 후 그것을 참조하는 곳들에 해당 ID 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;텍스트 문자열 사용&lt;/b&gt; : 해당 정보를 사용하는 모든 레코드에서 중복 저장됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 ID를 사용할지라도 ID자체가 의미를 가지는 경우 ID 자체를 변경해야 할 수도 있으며, 정보가 중복돼 있을 시 모든 중복 항복을 변경해야 한다. (이는 쓰기 오버헤드와 불일치의 위험이 있으며, 이런 중복을 제거하는 것이 데이터 정규화의 핵심 개념이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 데이터를 정규화하려면 &lt;b&gt;다대일(many-to-one)&lt;/b&gt; 관계가 필요한데, 다대일 관계는 문서 모델에 적합하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스에선 조인을 쓰는 방식이 쉽고 일반적이지만, 문서 데이터베이스는 조인 지원을 잘 안 해주기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(대신 문서 데이터베이스에서는 일대다 트리 구조를 위해 조인이 필요하지 않다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 데이터베이스 자체가 조인을 지원하지 않는다면 다중 질의를 통해 애플리케이션 코드단에서 조인 로직을 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6vuJ4/btrVjPhZHZS/kcHAcJJp5GuF03wnU6ORT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6vuJ4/btrVjPhZHZS/kcHAcJJp5GuF03wnU6ORT1/img.png&quot; data-alt=&quot;참조 :&amp;amp;amp;nbsp;https://ebrary.net/64618/computer_science/organizations_schools_entities#980&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6vuJ4/btrVjPhZHZS/kcHAcJJp5GuF03wnU6ORT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6vuJ4%2FbtrVjPhZHZS%2FkcHAcJJp5GuF03wnU6ORT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;751&quot; height=&quot;514&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 :&amp;amp;nbsp;https://ebrary.net/64618/computer_science/organizations_schools_entities#980&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은 &lt;b&gt;다대다(many-to-many)&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;size18&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;우선 데이터 모델의 차이점에만 집중해 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스를 선호하는 주요 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조인, 다대일, 다대다 관계를 더 잘 지원함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 데이터베이스를 선호하는 주요 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스키마 유연성, 지역성에 기인한 더 나은 성능&lt;/li&gt;
&lt;li&gt;애플리케이션에 사용하는 데이터 구조와 가까움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션에서 데이터가 문서와 비슷한 구조(일대다 관계 트리로 보통 한 번에 전체 트리를 적재)라면 문서 모델이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서와 비슷한 구조를 여러 테이블로 나누어 찢는 관계형 기법은 다루기 힘든 스키마와 불필요하게 복잡한 애플리케이션 코드를 발생시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 문서 모델에는 제한이 있는데 문서 내 중첩 항복을 바로 참조할 수 없고, 조인 지원이 미흡하기에 적용할 애플리케이션에 적합한지 따져봐야 한다. (예를 들어 이벤트를 기록하는 분석 애플리케이션에서는 다대다 관계가 필요하지 않다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 다대다 관계를 사용한다면 문서 모델은 매력이 떨어지는데, 비정규화로 조인의 필요성을 줄일 순 있지만 애플리케이션 코드 단에서 비정규화된 데이터의 일관성 유지를 위해 추가 작업이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 조인 또한 애플리케이션 코드단에서 흉내 낼 수 있지만 복잡도가 애플리케이션으로 이동하고 데이터베이스에 특화된 코드로 수행되는 조인보다 성능이 안 좋기 때문이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;어떤 데이터 모델이 애플리케이션에 적합한지는 데이터 항목 간에 존재하는 관계 유형에 따라 다르다.&lt;br /&gt;상호 연결이 많은 데이터의 경우 문서 모델은 별로고, 관계형 모델은 무난하며 그래프 모델이 적합하다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;문서 모델에서의 스키마 유연성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 데이터베이스는 종종 스키마리스(schemaless)로 불리지만 오해의 소지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조의 유형을 어느 정도 가정한 암묵적인 스키마가 있지만 강요하지 않을 뿐이고, 조금 더 정확한 용어로는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;쓰기 스키마(schema-on-write)&lt;/b&gt; : 관계형 데이터베이스의 전통적인 접근 방식으로 스키마는 명시적이고 데이터 베이스는 쓰여진&amp;nbsp;모든 데이터가 스키마를 따르고 있음을 보장한다. (정적(컴파일 타임) 타입 확인과 유사)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;읽기 스키마(schema-on-read)&lt;/b&gt; : 데이터 구조는 암묵적이고 데이터를 읽을 때만 해석된다. (동적(런타임) 타입 확인과 유사)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 확인에 관해 서로 지지하는 논쟁이 있는 것처럼, 스키마 강제도 논쟁의 여지가 있으며 옳고 그른 정답은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘의 차이는 애플리케이션이 데이터 타입을 변경할 때 더 뚜렷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 데이터베이스의 경우 새로운 필드를 가진 새 문서를 작성하고 애플리케이션에는 예전 문서를 읽은 경우를 처리하는 코드만 있으면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1672740722551&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (user &amp;amp;&amp;amp; user.name &amp;amp;&amp;amp; !user.first_name) {
	// Documents written before Dec 8, 2013 don't have first_name
	user.first_name = user.name.sptit(&quot; &quot;)[ ];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, 정적 타입의 데이터베이스 스키마에서는 다음과 같이 &lt;b&gt;마이그레이션(migration)&lt;/b&gt;을 수행해야만 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1672740833739&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ALTER TABLE users ADD COLUMN first_name text;

UPDATE users SET first_name = sptit_part(name, ' ', ); -- PostgreSQL

UPDATE users SET first_name = substring_index(name, ' ', ); -- MySQL&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 스키마 변경은 느리고 중단시간을 요구하는 문제가 있다. (현재 대부분의 관계형 데이터베이스 시스템은 ALTER TABLE문을 수 밀리초 안에 수행한다.)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;MySQL은 예외적으로 ALTER TABLE시에 전체 테이블을 복사해(COPY 방식의 ALGORITHM) 큰 테이블을 변경할 때 수 분에서 수 시간까지 중단시간이 발생할 수도 있다.&lt;br /&gt;다만 5.6 이상의 버전에서는 LOCK(NONE)과 ALGORITHM(INPLACE) 절을 이용하여 온라인 스키마 변경이 가능하다.&lt;br /&gt;(default는 INPLACE이며, INPLACE로 처리가 불가능할 시 COPY방식을 사용한다.)&lt;/blockquote&gt;
&lt;p data-ke-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;size18&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;관계형 데이터베이스인 PostgreSQL은 9.3 버전부터 MySQL은 5.7 버전부터 JSON 문서 지원 기능을 제공하고, 문서 데이터베이스인 RethinkDB는 질의 언어에서 관계형 조인을, MongoDB 드라이버는 자동으로 데이터베이스 참조를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 관계형과 문서의 혼합 모델(데이터를 문서처럼 다룰 수 있고 관계형 질의를 수행함)은 애플리케이션이 필요에 따라 가장 적합한 기능을 조합해 사용할 수 있게 하며, 미래 데이터베이스들이 가야 할 올바른 길로써 긍정적이라 볼 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 데이터를 위한 질의 언어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 모델이 등장했을 때 선언형 질의 언어인 SQL과 같이 데이터를 질의하는 새로운 방법도 함께 나타남.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 많이 사용하는 프로그래밍 언어는 &lt;b&gt;명령형 언어&lt;/b&gt;로, 특정 순서로 특정 연산을 수행하게끔 컴퓨터에게 지시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL이나 관계 대수 같은 &lt;b&gt;선언형 질의 언어&lt;/b&gt;에서는 목표를 달성하기 위한 방법이 아니라 알고자 하는 데이터의 패턴 같이 결과가 충족해야 하는 조건과 데이터를 어떻게 변환(정렬, 그룹화, 집계)할지를 정하기만 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 색인과 어떤 조인 함수를 사용할지, 어떤 순서로 질의를 실행할지는 데이터베이스 시스템의 &lt;b&gt;질의 최적화기(query optimizer)&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령형 API보다 더 간결하고 쉽게 작업할 수 있다.&lt;/li&gt;
&lt;li&gt;데이터베이스 엔진의 상세 구현이 숨겨져 있어 질의를 변경하지 않고 데이터베이스 시스템의 성능을 향상시킬 수 있다.&lt;/li&gt;
&lt;li&gt;병렬 실행에 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령형과 선언형 질의 언어의 차이를 좀 더 이해하기 위해, 웹에서의 선언형 접근방식과 명령형 접근방식을 비교해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(선언형 질의 언어의 장점은 완전히 다른 환경인 웹 브라우저 같이 데이터베이스에만 국한되지 않는다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바다에 사는 동물에 대한 웹사이트가 있고, 현재 상어 내비게이션 항목을 선택한 상황이라 가정하자.&lt;/p&gt;
&lt;pre id=&quot;code_1672753517263&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
	&amp;lt;li class=&quot;selected&quot;&amp;gt;
    	&amp;lt;p&amp;gt;Sharks&amp;lt;/p&amp;gt;
        &amp;lt;ul&amp;gt;
        	&amp;lt;li&amp;gt;Great White Shark&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;Tiger Shark&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;Hammerhead Shark&amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;
    	&amp;lt;p&amp;gt;Whales&amp;lt;/p&amp;gt;
        &amp;lt;ul&amp;gt;
        	&amp;lt;li&amp;gt;Blue Whale&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;Humpback Whale&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;Fin Whale&amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 선택한 페이지의 제목을 파란색 배경으로 표시하고 싶을 경우를 놓고 보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언형 접근 방식의 경우 CSS를 사용해 쉽게 개발할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1672754199107&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;li.selected &amp;gt; p {
	background-color: blue;
}&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;만약, 명령형 접근 방식을 사용한다면 JavaScript를 사용해 DOM API로 접근해야 할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1672757741658&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var liElements = document.getElementsByTagName(&quot;li&quot;);
for (var i = 0; i &amp;lt; liElements.length; i++) {
	if (liElements[i].className === &quot;selected&quot;) {
    	var children = liElements[i].childNodes;
        for (var j = 0; j &amp;lt; children.length; j++) {
        	var child = children[j];
            if (child.nodeType === Node.ELEMENT_NODE &amp;amp;&amp;amp; child.tagName === &quot;P&quot;) {
                child.setAttribute(&quot;style&quot;, &quot;background-color: blue&quot;);
            }
		}
	}
}&lt;/code&gt;&lt;/pre&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;selected 클래스가 삭제된 경우 (사용자가 다른 페이지를 클릭한 경우) 코드가 재실행되더라도 파란색이 삭제되지 않는다.&lt;/li&gt;
&lt;li&gt;Document.getElementsByClassName()이나 document.evaluate()와 같은 새로운 API의 장점(성능 향상과 같은)을 취하고 싶다면 코드를 재작성해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 처럼 선언형 접근 방식이 명령형 접근 방식보다 이점이 많고, 데이터베이스에서 또한 SQL 같은 선언형 질의 언어가 명령형 질의 API보다 훨씬 좋다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# 그래프형 데이터 모델&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 봤듯이 애플리케이션이 주로 일대다 관계(트리 구조)이거나 레코드 간 관계가 없다면 문서 모델이 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 만약 데이터에서 다대다 관계가 매우 일반적이고 복잡해질 경우엔 문서 모델도 관계형 모델도 아닌 그래프로 데이터를 모델링하는 것이 자연스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프로 모델링할 수 있는 데이터는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;소셜 그래프&lt;/b&gt; : 정점은 사람이고 간선은 사람들이 서로 알고 있음을 나타냄&lt;/li&gt;
&lt;li&gt;&lt;b&gt;웹 그래프&lt;/b&gt; : 정점은 웹 페이지고 간선은 다른 페이지에 대한 HTML 링크를 나타냄&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도로나 철도 네트워크&lt;/b&gt; : 정점은 교차로이고 간선은 교차로 간 도로나 철로 선을 나타냄&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿐만 아니라 그래프는 이런 동종 데이터에 국한되지 않고 표현이 가능한데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이스북의 경우 사람, 장소, 이벤트, 체크인, 사용자가 작성한 코멘트를 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;정점&lt;/span&gt;으로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친구 관계, 체크인 발생 위치 등을 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;간선&lt;/span&gt;으로 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 그림은 그래프 구조의 데이터 예시를 나타낸다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7r7ha/btrVeya9VsI/U2zBOiSuFqN6AY7rMH3vr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7r7ha/btrVeya9VsI/U2zBOiSuFqN6AY7rMH3vr0/img.png&quot; data-alt=&quot;참조 :&amp;amp;amp;nbsp;https://ebrary.net/64630/computer_science/graph_data_models#985&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7r7ha/btrVeya9VsI/U2zBOiSuFqN6AY7rMH3vr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7r7ha%2FbtrVeya9VsI%2FU2zBOiSuFqN6AY7rMH3vr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;751&quot; height=&quot;457&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 :&amp;amp;nbsp;https://ebrary.net/64630/computer_science/graph_data_models#985&lt;/figcaption&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;속성 그래프 모델&lt;/li&gt;
&lt;li&gt;트리플 저장소 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프용 선언형 질의 언어에는 다음과 같은 세 가지가 존재한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이퍼(Cypher)&lt;/li&gt;
&lt;li&gt;스파클(SPARQL)&lt;/li&gt;
&lt;li&gt;데이터로그(Datalog)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>독서/데이터 중심 애플리케이션 설계</category>
      <category>nosql</category>
      <category>관계형 모델</category>
      <category>그래프형 데이터 모델</category>
      <category>데이터 중심 애플리케이션 설계</category>
      <category>문서 모델</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/377</guid>
      <comments>https://hellominchan.tistory.com/377#entry377comment</comments>
      <pubDate>Mon, 2 Jan 2023 22:29:36 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Stream API</title>
      <link>https://hellominchan.tistory.com/376</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java 8에서 큰 변경점 중 하나인 Stream API를 공부해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(스크립트 언어를 주로 쓰다 Java로 넘어오니 답답한 부분들이 많았는데 조금이나마 해소되었다 :D)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# Stream 이란?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjK2Ce/btrUXpKrMkq/BKYg6k9ESKR2YPbTGqPWk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjK2Ce/btrUXpKrMkq/BKYg6k9ESKR2YPbTGqPWk0/img.png&quot; data-alt=&quot;참조 : https://www.geeksforgeeks.org/java-8-stream-tutorial/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjK2Ce/btrUXpKrMkq/BKYg6k9ESKR2YPbTGqPWk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjK2Ce%2FbtrUXpKrMkq%2FBKYg6k9ESKR2YPbTGqPWk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;298&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조 : https://www.geeksforgeeks.org/java-8-stream-tutorial/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서는 많은 양의 데이터를 저장하기 위해 배열이나 컬렉션을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이렇게 저장된 데이터에 접근하려면 반복문(for, while)이나 반복자(iterator)를 사용해야만 했는데,&lt;/p&gt;
&lt;p data-ke-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;또한, 내가 Java를 처음 공부하며 &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;List를 정렬할 때는 Collections.sort()를 쓰고, 배열을 정렬할 때는 Arrays.sort() 쓰고?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;문제점&lt;/span&gt;들을 해결하기 위해 Java SE 8부터 Stream API가 도입되었다. (2014.03.18 출시)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;해결된 문제점 : 장황함, 재사용 불가, 중복 정의 된 기능&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Stream API의 특징은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컬렉션과 다르게 내부 반복(internal iteration)을 통해 작업함. (forEach() 안에 람다식 사용)&lt;/li&gt;
&lt;li&gt;재사용이 가능한 컬렉션과 달리 한 번만 사용 가능함. (최종 연산 후 스트림이 닫혀 다시 사용 불가)&lt;/li&gt;
&lt;li&gt;원본 데이터를 변경하지 않음. (데이터 소스로 부터 오직 읽기만 함, 필요하면 컬렉션이나 배열에 반환 가능)&lt;/li&gt;
&lt;li&gt;지연(lazy)된 연산을 통해 성능을 최적화함. (sort()나 distinct() 같은 중간 연산을 최종 연산 전까지 수행하지 않음)&lt;/li&gt;
&lt;li&gt;parallelStream() 메서드를 통해 병렬 처리를 지원함. (기본은 sequential()이라 호출 안 해도 됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 Stream 연산은 크게 &lt;span style=&quot;background-color: #ffff00;&quot;&gt;중간 연산&lt;/span&gt;과, &lt;span style=&quot;background-color: #ffff00;&quot;&gt;최종 연산&lt;/span&gt; 두가지로 구분된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;중간 연산&lt;/b&gt; : 연산 결과가 스트림인 연산. (연속해서 중간 연산 가능)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;distinct&lt;/b&gt; : 중복 제거&lt;/li&gt;
&lt;li&gt;&lt;b&gt;filter&lt;/b&gt; : 조건에 안 맞는 요소 필터링&lt;/li&gt;
&lt;li&gt;&lt;b&gt;limit&lt;/b&gt; : 요소 길이 제한&lt;/li&gt;
&lt;li&gt;&lt;b&gt;skip&lt;/b&gt; : 일부 건너뛰기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;peek&lt;/b&gt; : 요소에 작업&lt;/li&gt;
&lt;li&gt;&lt;b&gt;sorted&lt;/b&gt; : 정렬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;map, mapToDouble, mapToInt, mapToLong&lt;/b&gt; : 스트림 요소 변환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;flatMap, flatMapToDouble, flatMapToInt, flatMapToLong&lt;/b&gt; : 스트림 요소 변환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최종 연산&lt;/b&gt; : 연산 결과가 스트림이 아닌 연산. (스트림의 요소를 소모하므로 한 번만 가능)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;forEach, forEachOrdered&lt;/b&gt; : 각 요소에 작업 수행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;count&lt;/b&gt; : 요소 개수 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;max, min&lt;/b&gt; : 최대, 최솟값 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;findAny, findFirst&lt;/b&gt; : 아무거나, 첫 번째 요소 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;allMatch, anyMatch, noneMatch&lt;/b&gt; : 주어진 조건을 모두, 하나라도, 불만족 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;toArray&lt;/b&gt; : 스트림 요소를 배열로 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;reduce&lt;/b&gt; : 요소를 리듀싱(누산)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;collect&lt;/b&gt; : 컬렉션에 담아 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1672331650588&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;stream
.distinct() // 중간 연산
.limit(7) // 중간 연산
.sorted() // 중간 연산
.forEach(System.out::println) // 최종 연산&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# Stream 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream 선언은 Stream&amp;lt;T&amp;gt;이지만 오토박싱, 언박싱 비효율을 줄이기 위해 IntStream 같은 기본형 Stream도 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 &lt;b&gt;킹&lt;/b&gt; 받는 것은 Boolean형 Stream은 제공하지 않는다. (Stream&amp;lt;Boolean&amp;gt;은 가능함)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;사실 메서드 시그니처만 보고 감으로 Stream API 쓰다가 BooleanStream이 없어서 공부하게 되었다,,&lt;br /&gt;&lt;a href=&quot;https://stackoverflow.com/a/42225051&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;BooleanStream 제공 안 하는 이유&lt;/a&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1672333730231&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.stream.*;

Stream&amp;lt;T&amp;gt; stream = Stream.of(T...);
Stream&amp;lt;T&amp;gt; stream = Stream.of(T[]);
Stream&amp;lt;T&amp;gt; stream = Arrays.stream(T[]);
Stream&amp;lt;T&amp;gt; stream = Arrays.stream(T[], 포함할 시작 인덱스, 제외할 시작 인덱스);

// 기본형 스트림
IntStream intStream = IntStream.of(T...);
IntStream intStream = IntStream.of(T...);
IntStream intStream = Arrays.stream(int[]);
IntStream intStream = Arrays.stream(int[], 포함할 시작 인덱스, 제외할 시작 인덱스);

LongStream LongStream = ...; (IntStream에서 LongStream 사용 외 같음)

DoubleStream doubleStream = ...; (IntStream에서 DoubleStream 사용 외 같음)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;# Stream 변환&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강타입 언어 특성상 형변환이 제일 빡세다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream 역시 마찬가지므로 자주 변환할 것 같은 리스트를 정리해 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Stream &amp;rarr; 기본형 Stream&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;map() 파생 메서드를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1672337982434&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Stream&amp;lt;Integer&amp;gt; intStream = Stream.of(1, 2, 3);
IntStream primitiveIntStream = intStream.mapToInt(el -&amp;gt; el); // 람다식
IntStream primitiveIntStream = intStream.mapToInt(Integer::valueOf); // 메서드 참조로도 가능

Stream&amp;lt;Long&amp;gt; longStream = Stream.of(1L, 2L, 3L);
LongStream primitiveLongStream = longStream.mapToLong(el -&amp;gt; el);
LongStream primitiveLongStream = longStream.mapToLong(Long::valueOf);

Stream&amp;lt;Double&amp;gt; doubleStream = Stream.of(1.0, 2.0, 3.0);
DoubleStream primitiveDoubleStream = doubleStream.mapToDouble(el -&amp;gt; el);
DoubleStream primitiveDoubleStream = doubleStream.mapToDouble(Double::valueOf);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 기본형 Stream &amp;rarr; Stream&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;boxed() 메서드를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1672339128121&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IntStream primitiveIntStream = IntStream.of(1, 2, 3);
Stream&amp;lt;Integer&amp;gt; intStream = primitiveIntStream.boxed();

LongStream primitiveLongStream = LongStream.of(1L, 2L, 3L);
Stream&amp;lt;Long&amp;gt; longStream = primitiveLongStream.boxed();

DoubleStream primitiveDoubleStream = DoubleStream.of(1.0, 2.0, 3.0);
Stream&amp;lt;Double&amp;gt; doubleStream = primitiveDoubleStream.boxed();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Stream &amp;rarr; 컬렉션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collect() 메서드를 사용한다. (기본형 Stream은 boxed()로 박싱 해야 함)&lt;/p&gt;
&lt;pre id=&quot;code_1672339976007&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// List
Stream&amp;lt;Integer&amp;gt; intStream = Stream.of(1, 2, 3);
List&amp;lt;Integer&amp;gt; intList = intStream.collect(Collectors.toList());

IntStream primitiveIntStream = IntStream.of(1, 2, 3);
List&amp;lt;Integer&amp;gt; intList = primitiveIntStream.boxed().collect(Collectors.toList());

// Set
Stream&amp;lt;Integer&amp;gt; intSetStream = Stream.of(1, 2, 3);
Set&amp;lt;Integer&amp;gt; intSet = intSetStream.collect(Collectors.toSet());

IntStream primitiveIntStream = IntStream.of(1, 2, 3);
Set&amp;lt;Integer&amp;gt; intSet = primitiveIntStream.boxed().collect(Collectors.toSet());

// Map
Stream&amp;lt;Object[]&amp;gt; mapStream = Stream.of(new Object[][]{{&quot;apple&quot;, 13}, {&quot;banana&quot;, 10}});
Map&amp;lt;String, Integer&amp;gt; map = mapStream.collect(
    Collectors.toMap(el -&amp;gt; (String) el[0], el -&amp;gt; (Integer) el[1]));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 컬렉션 &amp;rarr; Stream&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stream() 메서드를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1672341579347&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Stream&amp;lt;Integer&amp;gt; intStreamFromArrayList = new ArrayList&amp;lt;Integer&amp;gt;().stream();

Stream&amp;lt;Integer&amp;gt; intStreamFromSet = new HashSet&amp;lt;Integer&amp;gt;().stream();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. Stream &amp;rarr; 배열&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;toArray() 메서드를 사용한다. (기본형 Stream이 아닐 경우 1번처럼 변환해주어야 함)&lt;/p&gt;
&lt;pre id=&quot;code_1672342046165&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Stream&amp;lt;Integer&amp;gt; intStream = Stream.of(1, 2, 3);
int[] intArray = intStream.mapToInt(el -&amp;gt; el).toArray(); // 람다식
int[] intArray = intStream.mapToInt(Integer::valueOf).toArray(); // 메서드 참조로도 가능

IntStream primitiveIntStream = IntStream.of(1, 2, 3, 4, 5);
int[] intArray = primitiveIntStream.toArray();&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language &amp;amp; Runtime/Java</category>
      <category>java</category>
      <category>Stream</category>
      <category>스트림</category>
      <author>HelloMinchan</author>
      <guid isPermaLink="true">https://hellominchan.tistory.com/376</guid>
      <comments>https://hellominchan.tistory.com/376#entry376comment</comments>
      <pubDate>Fri, 30 Dec 2022 04:30:05 +0900</pubDate>
    </item>
  </channel>
</rss>