<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ej_shin.log</title>
        <link>https://velog.io/</link>
        <description>호그와트 장학생</description>
        <lastBuildDate>Mon, 13 Mar 2023 12:23:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ej_shin.log</title>
            <url>https://images.velog.io/images/ej_shin/profile/9abd3fc5-0bfe-4656-b1c1-98b9ec462ab6/KakaoTalk_Photo_2021-09-06-05-19-44.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ej_shin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ej_shin" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[배달의 민족은 장바구니를 어떻게 만들었을까? (작성중)]]></title>
            <link>https://velog.io/@ej_shin/%EB%B0%B0%EB%8B%AC%EC%9D%98-%EB%AF%BC%EC%A1%B1%EC%9D%80-%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%A7%8C%EB%93%A4%EC%97%88%EC%9D%84%EA%B9%8C-%EC%9E%91%EC%84%B1%EC%A4%91</link>
            <guid>https://velog.io/@ej_shin/%EB%B0%B0%EB%8B%AC%EC%9D%98-%EB%AF%BC%EC%A1%B1%EC%9D%80-%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%A7%8C%EB%93%A4%EC%97%88%EC%9D%84%EA%B9%8C-%EC%9E%91%EC%84%B1%EC%A4%91</guid>
            <pubDate>Mon, 13 Mar 2023 12:23:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/cdccb560-5bdc-4579-936d-8cfa164a5d5f/image.gif" alt=""></p>
<h3 id="1️⃣-발단">1️⃣ 발단</h3>
<p>공차를 주문하려고 배달의 민족에 들어갔다. 
블랙 밀크티 펄 추가 당도 100짜리 밀크티를 장바구니에 담다가 문득 궁금해졌다. </p>
<blockquote>
<p>🤔 배달 장바구니랑 배민1 장바구니는 따로인가?</p>
</blockquote>
<h3 id="2️⃣-전개">2️⃣ 전개</h3>
<p>담아봤다. 같은 가게의 메뉴만 담을 수 있다는 경고문이 뜬다. 
동일한 가게라도 배달 서비스와 배민1 서비스의 장바구니를 공유할 수 없다.</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/e48d270c-1664-4aa5-a78e-8a2aed3e4499/image.jpg" alt="">
애초에 배달 서비스 입점 가게와 배민 1 서비스 입점 가게를 다른 가게로 분류하고 있다. 단, 같은 가게라면 함께 주문 서비스는 동일 장바구니를 이용한다.
그럼 B마트는 어떨까?</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/cca84785-dfaa-4b50-9a64-8bc22f6863fc/image.jpg" alt=""></p>
<p>담아진다. (+ 간편식/밀키트 서비스는 B마트 장바구니에 담아진다.)
그렇다면 배민스토어는?</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/0563838f-cdb4-46fe-86da-aff964bcd944/image.jpeg" alt=""></p>
<p>담아진다. 배달/배민1과 B마트와는 다른 장바구니가 생긴다.
전국 별미도 마찬가지로 별개의 장바구니에 담아진다. 
선물하기 서비스는 구매 후 바로 선물로 넘어가기에 장바구니가 따로 없다.</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/0b7da0f5-7994-4db3-a192-a9d6b1140c94/image.jpeg" alt=""></p>
<p>대강 정리해보면 이렇게 된다. </p>
<ul>
<li><p>배달 서비스와 배민 1 서비스는 같은 장바구니를 공유한다.</p>
<ul>
<li>배달 서비스 가게의 메뉴를 장바구니에 담은 경우, 배민 1의 메뉴는 추가 불가능</li>
<li>배달 서비스의 가게와 배민 1 서비스의 가게는 다른 가게로 취급</li>
<li>함께주문 서비스는 배달/배민1 서비스와 동일 장바구니를 사용한다</li>
</ul>
</li>
<li><p>B마트 서비스엔 별도의 장바구니가 존재한다.</p>
</li>
<li><p>배민 스토어 서비스엔 별도의 장바구니가 존재한다.</p>
</li>
<li><p>전국 별미 서비스에도 별도의 장바구니가 존재한다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/4f4bd2b4-cd64-484d-bf72-30ea5dbc82d2/image.gif" alt=""></p>
<blockquote>
<p>장바구니.. 짱많아...!</p>
</blockquote>
<p>그럼 이제 몇 가지 의문이 생긴다. </p>
<p><strong>[ 서비스 관련 의문 ]</strong></p>
<ul>
<li>배민 1은 왜 별도의 장바구니를 갖지 않는가?<ul>
<li>배민 1은 나중에 추가된 서비스다.</li>
<li>배민 1은 일반 배달 서비스와는 배달비 책정 방식, 배달 시간, 쿠폰 적용 등등이 전부 다르게 적용된다.</li>
<li>배민 1은 배달 서비스와 별도의 카테고리로 분류된다.  </li>
<li>B마트, 배민스토어 등과 동일하게 별도의 장바구니를 만들어도 되지 않나?</li>
</ul>
</li>
</ul>
<p><strong>[ 개발 관련 의문 ]</strong></p>
<ul>
<li>장바구니 서비스 내부에서 도메인별 장바구니를 구분하는 방법은 뭘까? <ul>
<li>배달의 민족은 각 도메인을 마이크로 서비스로 분리한 MSA 구조로 설계되어 있다(고 들었다.)</li>
<li>그럼 장바구니 서비스도 아마 마이크로 서비스로 분리되어 있을 것이다.</li>
</ul>
</li>
<li>특정 도메인 (ex. B마트)에 어떻게 해당 도메인의 장바구니를 제공해줄 수 있을까? <ul>
<li>API를 분리하나? 근데 그럼 클라쪽에서 도메인별 장바구니 맵핑 로직을 관리해야하는걸?</li>
<li>테이블을 다 분리하나? </li>
<li>아니면 하나의 테이블 안에서 컬럼으로 구분해? </li>
<li>RDB가 맞긴 할까? </li>
</ul>
</li>
<li>신규 서비스가 런칭되면 또 장바구니 종류를 추가해야 한다. 기존 장바구니들을 건드리지 않으면서 신규 장바구니를 추가하는 방법은 뭘까?</li>
</ul>
<br>

<p>배달의 민족 (우아한형제들)은 기술 블로그가 정말 잘 되어있다. 한번 장바구니 서비스를 검색해보자.</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/374daf77-1e85-435e-8c8d-612bbdacd2f6/image.png" alt=""></p>
<p>아쉽지만 장바구니 관련 게시글은 없었다. (근데 왜 페이지 사이즈를 3으로 정한걸까?)
그럼 이제 뇌피셜을 펼쳐보자. </p>
<h3 id="3️⃣-위기">3️⃣ 위기</h3>
<p>일단 RDB를 쓴다고 가정하자. 1. 도메인별 관계가 명확하고 2. 스키마가 고정적이기 때문이다. 요구사항은 아래와 같다. </p>
<ul>
<li>유저는 도메인별 장바구니를 가질 수 있다 (배달, 배민스토어, B마트 등) </li>
<li>각 장바구니는 가게와 1:1 관계를 가진다 (한 장바구니에는 같은 가게 메뉴만 담을 수 있음)</li>
<li>장바구니에는 여러 메뉴가 담길 수 있고, 담을 수 있는 메뉴에 개수 제한은 없다. </li>
<li>장바구니에 담은 메뉴는 당장 주문하지 않더라도 삭제할 때까지 장바구니에 남아있어야 한다.</li>
</ul>
<p>유저가 도메인별 장바구니를 가지게 하는 방법을 몇 가지 고민해보자.</p>
<h4 id="1-1n-관계를-만들고-장바구니-테이블에-도메인-구분-컬럼을-갖게-한다">1. 1:N 관계를 만들고 장바구니 테이블에 도메인 구분 컬럼을 갖게 한다.</h4>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/09a3169c-7f4f-4dc9-a3d3-07af554b02c0/image.png" alt=""></p>
<p>깊게 생각 안하고 바로 그려봤다. 
boolean 타입의 컬럼으로 어떤 도메인에 해당하는 장바구니인지를 구분하는 방식이다. </p>
<p>괜찮은데? 싶을지 몰라도 문제가 아주 많다. </p>
<p>(1) <strong>새로운 서비스를 런칭하려면 ddl를 이용해야 한다.</strong> 
기존 저장된 모든 데이터 레코드 구조를 변경해야 한다.
유저의 수 x n개 만큼의 장바구니 레코드가 있을텐데, 이 데이터를 한꺼번에 변경하는 것은 위험하고, 시간이 얼마나 걸릴지도 알 수 없는 일이다. </p>
<p>물론 운영 환경과 동일한 테스트 환경을 만들고 예상 시간을 측정해볼 수도 있지만, 혹시라도 예상치 못한 이슈가 발생할 수 있고, 그로 인해 데이터 무결성이 손상될 수도 있다는 점을 생각하면 딱히 고르고 싶은 선택지는 아니다.</p>
<p>운영 환경에서 발생할 수 있는 모든 잠재적 위험성을 체크하고, 유저들에게 서버 점검 공지를 날리고, ddl을 수행하고... 일단 난 심장이 떨린다ㅋㅋ</p>
<p>(2) *<em>중복 값이 너무 많아진다. *</em>
위 예시는 PK, FK, 구분컬럼만 뒀지만 실제로는 더 많은 정보가 필요할 것이다.
구분자 컬럼에 true, false 값을 할당하기 위해 유저별로 구분 컬럼의 개수만큼 레코드가 필요하고, 따라서 그만큼 데이터가 중복된다.</p>
<p>(3) <strong>장바구니를 초기화하는 방식이 더러워진다.</strong></p>
<p>신규 유저가 회원가입을 하면, 모든 도메인별로 장바구니 데이터를 추가해 초기화해야한다. (그래야 유저가 장바구니에 아이템을 바로바로 담을 수 있을테니까)</p>
<p>위 설계에선 케이스별로 CART 생성자를 만들어야하며, 신규 서비스가 런칭이 될 경우 기존 만들었던 생성자들을 전부 수정해줘야 한다. </p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/0929342b-7950-4340-9d97-b0ecaee24c89/image.png" alt=""></p>
<p>정적 팩토리 메소드를 이용한 예시. 해당 메소드를 호출하는 클라이언트단을 수정할 필요는 없겠지만, 그래도 팩토리 메소드 하나하나를 수정해줘야 하는건 동일하다.</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/cb07d16b-0cf7-44a4-93fb-9e6c659c4294/image.png" alt=""></p>
<p>그래서 이번엔 boolean이 아니라 enum 컬럼을 이용해 장바구니를 구별해봤다.
위에서 발생했던 문제들을 하나하나 되짚어보자.</p>
<p>(1) <strong>새로운 서비스를 런칭하려면 ddl를 이용해야 한다.</strong> 
=&gt; 해결 안됨
=&gt; 기존 레코드에 값을 Update 쳐 줄 필요는 없지만 enum 선택지를 늘려주기 위해 ddl을 이용해야하는 건 마찬가지다.
=&gt; boolean 사용 방식보단 나아졌지만 여전히 완벽한 대안은 아니다.</p>
<p>(2) <strong>중복 값이 너무 많아진다.</strong>
=&gt; 해결 안됨
=&gt; type (도메인) 수 만큼 레코드를 추가해야하므로 데이터 중복은 여전하다.</p>
<p>(3) <strong>장바구니를 초기화하는 방식이 더러워진다.</strong>
=&gt; 해결?!</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/7dc288a2-8f31-46bf-a314-ed2c518ac454/image.png" alt=""></p>
<p>이제 신규 서비스가 런칭되었다고 생성자를 수정할 필요는 없다. Type 값만 넘겨주면 되니까!</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/0a0e0162-1bb5-4bcb-9b98-d729b1799e3d/image.png" alt=""></p>
<p>도메인별 장바구니 생성하는 것도 간단하다. enum을 순회돌면서 cart를 생성하면 끝.
따라서 enum 클래스에 신규 값만 추가해주면 그 외 코드는 수정하지 않아도 된다.</p>
<p>언뜻 보면 해결된 것 같지만, (1), (2)의 문제가 그대로 남아있다.
그러니 enum을 이용한 1:N 관계는 일단 킵해보자.</p>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/4dd5fc97-2263-4ba9-924d-f3f7ca323548/image.png" alt=""></p>
<h4 id="2-장바구니-테이블을-도메인별로-분리한다">2. 장바구니 테이블을 도메인별로 분리한다.</h4>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/27c32dc2-a57f-4634-801c-5ee73432a3ba/image.png" alt=""></p>
<p>음 일단 테이블이 엄청나게 늘어났다ㅋㅋ</p>
<h3 id="4️⃣-결말">4️⃣ 결말</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CH6] 키-값 저장소 설계]]></title>
            <link>https://velog.io/@ej_shin/CH6-%ED%82%A4-%EA%B0%92-%EC%A0%80%EC%9E%A5%EC%86%8C-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@ej_shin/CH6-%ED%82%A4-%EA%B0%92-%EC%A0%80%EC%9E%A5%EC%86%8C-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Sat, 04 Feb 2023 05:58:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/c35f7cb2-5fd0-4010-9024-d846c45dd1c8/image.jpeg" alt="">
<a href="http://www.yes24.com/Product/Goods/102819435">가상 면접 사례로 배우는 대규모 시스템 설계 기초 (알렉스 쉬 저)</a>를 읽고 정리합니다. </p>
</blockquote>
<blockquote>
<p>📢 <code>키-값 저장소</code> : 고유 식별자 키에 매칭되는 값의 키-값 쌍(pair) 구조의 비 관계형 데이터베이스</p>
</blockquote>
<ul>
<li>키는 유일하며, 키를 통해서만 값에 접근할 수 있다.<ul>
<li>키는 텍스트 또는 해시 값</li>
<li>키가 짧을 수록 성능 상 좋다</li>
</ul>
</li>
</ul>
<h2 id="1️⃣-문제-이해-및-설계-범위-확정">1️⃣ 문제 이해 및 설계 범위 확정</h2>
<h3 id="🤔-이런-요소들을-고민해보기">🤔 이런 요소들을 고민해보기</h3>
<ul>
<li><strong>읽기, 쓰기, 메모리 사용량 사이에서 균형 찾기</strong></li>
<li><strong>데이터 일관성과 가용성 사이에서 타협적 결정 내리기</strong></li>
<li>Ex. 이런 특성을 고민해보자~~<ul>
<li>키-값 쌍의 크기</li>
<li>큰 데이터 저장 가능</li>
<li>높은 가용성 :: 장애 있어도 빠른 응답 가능</li>
<li>높은 규모 확장성 :: 트래픽 양에 따라 자동으로 서버 증설/삭제 가능</li>
<li>데이터 일관성 수준 조정 가능</li>
<li>응답 지연 시간</li>
</ul>
</li>
</ul>
<h3 id="✨-단일-서버-키-값-저장소">✨ 단일 서버 키-값 저장소</h3>
<ul>
<li>키-값 쌍 모두를 <code>메모리에 해시 테이블</code>로 저장한다<ul>
<li>가장 직관적이고 빠르지만 모든 데이터를 메모리 안에 두는 것이 불가능 할 수 있다.</li>
<li>이를 해결하기 위해 <strong>데이터 압축, 자주 쓰이는 데이터만 메모리에 저장</strong>하는 등의 방식을 사용한다.</li>
</ul>
</li>
<li>하지만 한 대 서버로 부족하게 된다면? <code>분산 저장소</code>를 만들어야 한다.</li>
</ul>
<br>

<hr>
<h2 id="2️⃣-분산-키-값-저장소-설계">2️⃣ 분산 키-값 저장소 설계</h2>
<blockquote>
<p>📢  <code>= 분산 해시 테이블</code> : 키-값 쌍을 여러 서버에 분산시킨다.</p>
</blockquote>
<h3 id="📍-cap-정리">📍 CAP 정리</h3>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/4db99b5e-e771-46b1-b46a-fcf4d1a80ad6/image.png" alt=""></p>
<ul>
<li><strong>데이터 일관성, 가용성, 파티션 감내의 세 요구사항</strong>을 동시에 만족하는 분산 시스템을 설계하는 것은 <strong>불가능</strong>하다.<ul>
<li><code>데이터 일관성 (consistency)</code>: 분산 시스템에 접속하는 모든 클라이언트는 <strong>어떤 노드에 접속했느냐와 관계 없이 언제나 같은 데이터</strong>를 보게 되어야 한다.</li>
<li><code>가용성 (availability)</code> : 분산 시스템에 접속하는 클라이언트는 <strong>일부 노드에 장애가 발생해도 항상 응답을 받을 수 있어</strong>야 한다.</li>
<li><code>파티션 감내 (partition tolerance)</code> : 파티션은 두 노드 사이에 통신 장애가 발생했음을 알린다. 따라서 파티션 감내는 <strong>파티션이 생겨도 시스템은 계속 동작해야 한다</strong>는 의미!</li>
</ul>
</li>
</ul>
<ul>
<li>키-값 저장소의 분류<ul>
<li><code>CP 시스템</code> : 일관성과 파티션 감내를 지원. <strong>가용성 희생</strong><ul>
<li>서버 A, B, C에서 C가 죽은 순간부터 서버에 읽기/쓰기 불가능<ul>
<li>일관성 = 언제, 무슨 일이 있어도 각 서버에 있는 모든 데이터가 동기화 됨’을 보장하겠다</li>
<li>따라서 C가 죽은 순간, 데이터 일관성을 유지할 수 없으니 서버는 에러를 보낸다.</li>
</ul>
</li>
</ul>
</li>
<li><code>AP 시스템</code> : 가용성과 파티션 감내 지원, <strong>데이터 일관성 희생</strong><ul>
<li>서버 A, B, C에서 C가 죽은 순간 서버에 연산은 가능<ul>
<li>서버가 요청에 대해 ‘사용 가능’한 상태</li>
<li>그러나 일관성을 희생했으므로, 서버에 저장된 데이터는 조금씩 다르다</li>
<li>서버는 변경사항을 임시로 들고, 파티션 문제가 해결되면 데이터 동기화를 진행한다 = 어느 특정 시점에서 데이터 불일치 가능</li>
</ul>
</li>
</ul>
</li>
<li><code>CA 시스템</code> : 일관성과 가용성 지원, <strong>파티션 감내 희생</strong><ul>
<li>단, 통상 네트워크 장애는 피할 수 없는 문제! 분산 시스템은 반드시 파티션 이슈를 감내할 수 있어야 한다.</li>
<li><strong>따라서 개념상으로만 존재하지, 실세계에는 존재 X</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="📍-분산-시스템-데이터-보관-예시">📍 분산 시스템 데이터 보관 예시</h3>
<ul>
<li>세 대의 복제 (replica) 노드 n1, n2, n3에 데이터를 복제해서 보관하는 케이스를 가정<ul>
<li><strong>이상적 상태</strong><ul>
<li>네트워크 파티션? 응 절대 안일어나~</li>
<li>n1에 있는 데이터는 n2, n3에 자동으로 복제 → 일관성, 가용성 만족</li>
</ul>
</li>
<li><strong>실세계의 분산 시스템</strong><ul>
<li>네트워크 파티션? 응 절대 일어나~<ul>
<li>파티션 발생 시, 일관성과 가용성 둘 중 하나를 선택해야 한다.</li>
</ul>
</li>
<li>Ex. n3가 죽어서 n1, n2와 통신할 수 없는 상황<ul>
<li><code>CP 시스템</code> : 데이터 일관성을 위해 n1, n2에 대해 쓰기 연산 중단!</li>
<li><code>AP 시스템</code> : 시스템 가용성을 위해 n1, n2에 쓰기 연산 허용, 파티션 문제 해결 후 새 데이터를 n3에 전송!</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="📍-시스템-컴포넌트">📍 시스템 컴포넌트</h3>
<blockquote>
<p>📢 키-값 저장소 구현에 사용될 핵심 컴포넌트들</p>
</blockquote>
<h4 id="데이터-파티션">데이터 파티션</h4>
<ul>
<li>대규모 서비스의 경우, <strong>데이터를 작은 파티션으로 분할한 다음 여러 대의 서버에 저장</strong>한다.</li>
<li>데이터를 파티션 단위로 나눌 때는 아래를 고려한다<ul>
<li>데이터를 여러 서버에 <em>고르게 분산할 수 있는가</em>?</li>
<li>노드가 추가되거나 삭제될 때 _데이터 이동을 최소화_할 수 있는가?</li>
</ul>
</li>
<li><code>안정 해시</code>를 이용해 데이터를 파티션하면 이런 점이 좋다<ul>
<li><strong>규모 확장 자동화 (automatic scaling)</strong> : 시스템 부하에 따라 서버가 자동으로 추가되거나 삭제</li>
<li><strong>다양성 (heterogeneity)</strong> : 각 서버 용량에 맞게 가상 노드 수를 조정할 수 있다 (= 고성능 서버는 더 많은 가상 노드)</li>
</ul>
</li>
</ul>
<h4 id="데이터-다중화-replication">데이터 다중화 (replication)</h4>
<ul>
<li>목적 : <strong>높은 가용성과 안정성을 확보</strong>하기 위함</li>
<li>방법 : 데이터를 N개 (튜닝 가능한 값) 서버에 비동기적으로 다중화<ul>
<li>N개 서버 선정 과정<ul>
<li>어떤 키를 해시 링 위에 배치 → 거기서부터 시계방향으로 링 순회하다 만나는 첫 N개 서버에 데이터 사본 보관</li>
</ul>
</li>
</ul>
</li>
<li>문제 : 가상 노드 사용 시, <em>선택한 N개 노드가 대응될 실제 물리 서버 개수가 N보다 작아질 수 있다</em>.</li>
<li>해결 : 노드 선택 시 <strong>같은 물리 서버 중복 선택하지 않게 처리</strong>한다.</li>
<li>기타 문제 : 데이터 센터는 물리적 (정전, 자연재해 등) 문제를 겪을 수 있으니, 안정성을 위해 _데이터 사본을 다른 데이터 센터 서버에 보관_하고, _센터들은 고속 네트워크로 연결_한다.</li>
</ul>
<h4 id="일관성-consistency">일관성 (consistency)</h4>
<ul>
<li>여러 노드에 다중화된 데이터는 적절한 동기화가 필요하다.</li>
<li><code>정족수 합의 프로토콜 (Quorum Consensus)</code><ul>
<li>읽기/쓰기 연산 모두에 일관성 보장 가능</li>
<li>관계 정의<ul>
<li>N = 사본 개수</li>
<li>W = 쓰기 연산에 대한 정족수 
  :: 쓰기 연산 성공 = 중재자는 W개의 서버로부터 쓰기 연산 성공 응답 받아야 함</li>
<li>R = 읽기 연산에 대한 정족수
  :: 읽기 연산 성공 = 중재자는 R개의 서버로부터 응답 받아야 함</li>
</ul>
</li>
<li>면접에서 N, W, R 정하기<ul>
<li><code>W = 1, R = N</code> : 빠른 쓰기 연산에 최적화<ul>
<li>중재자는 클라이언트와 노드 사이에서 프록시 역할 수행</li>
</ul>
</li>
<li><code>W = N, R = 1</code> : 빠른 읽기 연산에 최적화<ul>
<li>중재자는 한 대 서버의 응답만 받으면 됨 :: 응답속도 굿</li>
</ul>
</li>
<li>W 또는 R이 1보다 큰 경우<ul>
<li>시스템의 데이터 일관성은 향상</li>
<li>중재자 응답 속도는 느려짐 :: 가장 느린 서버로부터의 응답 기다려야 함</li>
</ul>
</li>
<li><code>W + R &gt; N</code> : 강한 일관성 보장<ul>
<li>보통 N = 3, W = R = 2</li>
</ul>
</li>
<li><code>W + R ≤ N</code> : 강한 일관성 보장 X</li>
</ul>
</li>
</ul>
</li>
<li><code>일관성 모델 (consistency model)</code><ul>
<li>데이터 일관성의 수준을 결정한다.</li>
<li>종류<ul>
<li><code>강한 일관성 (strong consistency)</code><ul>
<li>모든 읽기 연산은 <strong>가장 최근에 갱신된 결과</strong>를 반환</li>
<li>모든 사본에 현재 쓰기 연산의 결과가 반영될 때까지 해당 데이터에 대한 읽기/쓰기 금지해서 달성</li>
<li>새로운 요청의 처리가 중단되기 때문에, 고가용성 시스템에 적합 X</li>
</ul>
</li>
<li><code>약한 일관성 (weak consistency)</code><ul>
<li>읽기 연산은 가장 <strong>최근에 갱신된 결과를 반환하지 못할 수 있다</strong>.</li>
</ul>
</li>
<li><code>최종 일관성 (eventual consistency)</code><ul>
<li>약한 일관성의 한 형태</li>
<li>갱신 결과가 <strong>결국에는 모든 사본에 반영</strong> (=동기화) 된다.</li>
<li>쓰기 연산이 병렬적으로 발생할 경우, 시스템에 저장된 값의 일관성이 깨질 수 있다.<ul>
<li>클라이언트가 직접 데이터 버전 정보를 이용해, 일관성이 깨진 데이터를 읽기 않게 처리한다. 
   ⇒ 비일관성 해소 기법 사용</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="일관성-불일치-해소-inconsistency-resolution">일관성 불일치 해소 (inconsistency resolution)</h4>
<ul>
<li>데이터 다중화 :: 가용성은 높아져도, 사본 간 일관성 깨질 가능성 높아짐</li>
<li>해결<ul>
<li><code>버저닝 (versioning)</code> : 데이터 변경할 때마다 새로운 버전 생성<ul>
<li>각 버전의 데이터는 변경 불가능 (immutable)</li>
</ul>
</li>
<li><code>벡터 시계 (vector clock)</code> : 버저닝에서 두 버전 사이의 충돌 해소<ul>
<li>동작 방식<ul>
<li><strong>[ 서버, 버전 ]의 순서쌍을 데이터에 맵핑</strong><ul>
<li>어떤 버전이 선행인지, 후행인지, 다른 버전과 충돌이 있는지 판별하는데 사용!</li>
</ul>
</li>
<li>데이터 D를 서버 Si에 기록한다고 할 때, 시스템은 아래 작업 수행<ul>
<li>[ Si, vi ] 가 있으면 버전 카운터 vi를 증가시킨다.</li>
<li>그렇지 않으면 새 항목 [ Si, 1 ] 을 만든다.</li>
</ul>
</li>
</ul>
</li>
<li>장점<ul>
<li>어떤 버전 X가 버전 Y의 이전 버전인지 (따라서 충돌이 없는지) 쉽게 판단 가능!</li>
<li>버전 Y에 포함된 모든 구성 요소 값이 X에 포함된 모든 구성 요소 값보다 같거나 큰지만 체크하면 된다.</li>
</ul>
</li>
<li>단점<ul>
<li>충돌 감지 및 해소 로직이 클라이언트단에 추가 :: <strong>클라이언트 구현 복잡</strong></li>
<li>[서버:버전] 순서쌍 개수가 빠르게 증가<ul>
<li>길이에 임계치 정하고, _임계치 초과할 때 오래된 순서쌍을 벡터 시계에서 제거_해야 한다.</li>
<li>단, 이 경우 버전 간 선후 관계가 정확하게 결정될 수 없어 <strong>충돌 해소 효율성이 낮아진다.</strong><ul>
<li>Amazon dynamoDB말로는 이런 적이 없다고 하니, <strong>사실상 안전한 솔루션</strong>이긴 하다. 참고만..!</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="장애-처리">장애 처리</h4>
<ul>
<li><code>장애 감지 (failure detectinon)</code><ul>
<li>분산 시스템에서는 보통 두 대 이상의 서버가 똑같이 특정 서버의 장애를 보고해야, 해당 서버에 실제로 장애가 발생했다고 간주한다.</li>
<li><strong>서버 적을 때</strong><ul>
<li>모든 노드 사이에 멀티캐스팅 (multicasting) 채널을 구축해 서버 장애 감지</li>
</ul>
</li>
<li><strong>서버 많을 때</strong><ul>
<li>분산형 장애 감지 (decentrallized failure detectino) 솔루션 사용<ul>
<li>Ex. 가십 프로토콜 (gossip protocol)<ul>
<li>각 노드는 멤버십 목록을 유지 :: 멤버십 목록 = 각 멤버 ID와 박동 카운터 (heartbeat counter) 쌍 목록<ul>
<li>각 노드는 주기적으로 자신의 박동 카운터를 증가</li>
<li>각 노드는 무작위로 선정된 노드들에게 주기적으로 자기 박동 카운터 목록을 보낸다</li>
<li>박동 카운터 목록을 받은 노드는 멤버십 목록을 최신 값으로 갱신</li>
<li>어떤 멤버의 박동 카운터 값이 지정된 시간 동안 갱신되지 않는다면? → 해당 멤버는 장애 상태인 것으로 간주</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code>장애 해소 (failure resolution)</code><ul>
<li>일시적 장애 처리<ul>
<li>가십 프로토콜로 장애 감지 → 가용성 보장을 위해 조치<ul>
<li><code>엄격한 정족수 (strict quorum) 접근</code> : 읽기, 쓰기 연산 금지</li>
<li><code>느슨한 정족수 (sloppy quorum) 접근</code> : 임시 위탁 (hinted handoff)<pre><code>  1. 쓰기 연산을 수행할 W개의 건강한 서버와 읽기 연산을 수행할 R개의 건강한 서버를 해시 링에서 고른다. (장애 서버 무시)
  2. 장애 서버로 가는 요청은 위에서 고른 서버들이 맡아 처리한다. 
  3. 변경사항들은 장애 서버가 복구되었을 때 일괄 반영해 데이터 일관성을 보존한다. 이를 위해 임시 쓰기 연산 처리 서버는 단서 (hint)를 남긴다. </code></pre></li>
</ul>
</li>
</ul>
</li>
<li>영구적 장애 처리<ul>
<li><code>반-엔트로피 (anti-entropy) 프로토콜</code>을 이용해 사본 동기화<ul>
<li>사본들을 비교해 최신 버전으로 갱신</li>
<li>사본 간의 일관성이 망가진 상태 탐지 → 전송 데이터 양을 줄이기 위해 머클 트리 사용 (Merkle)<ul>
<li><code>머클 트리 = 해시 트리</code><ul>
<li>각 노드에 그 자식 노드들에 보관된 값의 해시 또는 자식 노드들의 레이블로부터 계산된 해시 값을 레이블로 붙여두는 트리</li>
<li>대규모 자료 구조의 내용을 효과적이고 안전하게 검증 가능</li>
<li>동기화해야 하는 데이터의 양은 존재하는 차이 크기에 비례하지, 두 서버에 보관된 데이터 총량과는 무관하다.</li>
<li>단, 실제 시스템의 경우 버킷 크기가 크다는 것 참고</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>데이터 센터 장애 처리<ul>
<li>데이터를 여러 데이터 센터에 다중화하자!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="시스템-아키텍처-다이어그램">시스템 아키텍처 다이어그램</h4>
<ul>
<li>아키텍처의 주된 기능 예시<ul>
<li>클라이언트는 키-값 저장소가 제공하는 두 가지 단순한 API, get과 put와 통신한다.</li>
<li>중재자 (coordinator)는 _클라이언트에게 키-값 저장소에 대한 프록시 역할을 하는 노드_다.</li>
<li>노드는 _안정 해시의 해시 링 위에 분포_한다.</li>
<li>노드를 자동으로 추가 또는 삭제할 수 있게 시스템은 완전히 분산된다.</li>
<li>데이터는 여러 노드에 다중화된다.</li>
<li>모든 노드가 같은 책임을 지므로, SPOP (Single Point of Failure)는 존재하지 않는다.</li>
</ul>
</li>
</ul>
<h4 id="쓰기-경로-write-path">쓰기 경로 (write path)</h4>
<ul>
<li>쓰기 요청이 특정 노드에게 전달되었을 때 벌어지는 일<ul>
<li>카산드라 구조 참고<ul>
<li>쓰기 요청이 커밋 로그 파일에 기록</li>
<li>데이터가 메모리 캐시에 기록</li>
<li>메모리 캐시가 가득차거나 사전에 정의된 어떤 임계치에 도달하면 데이터는 디스크에 있는 SSTable에 기록된다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="읽기-경로-read-path">읽기 경로 (read path)</h4>
<ul>
<li>읽기 요청을 받은 노드는 데이터가 <strong>메모리 캐시에 있는지</strong>부터 살핀다.<ul>
<li>있으면 해당 데이터를 클라에게 반환</li>
<li>없으면 디스크에서 가져온다<ul>
<li>어느 SSTable에 있는지 알아내기 위해 블룸 필터 (Bloom filter) 등을 사용한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<hr>
<h2 id="3️⃣-요약">3️⃣ 요약</h2>
<table>
<thead>
<tr>
<th>목표/문제</th>
<th>기술</th>
</tr>
</thead>
<tbody><tr>
<td>대규모 데이터 저장</td>
<td>안정 해시를 사용해 서버들에 부하 분산</td>
</tr>
<tr>
<td>읽기 연산에 대한 높은 가용성 보장</td>
<td>데이터를 여러 데이터 센터에 다중화</td>
</tr>
<tr>
<td>쓰기 연산에 대한 높은 가용성 보장</td>
<td>버저닝 및 벡터 시계를 이용해 충돌 해소</td>
</tr>
<tr>
<td>데이터 파티션</td>
<td>안정 해시</td>
</tr>
<tr>
<td>점진적 규모 확장성</td>
<td>안정 해시</td>
</tr>
<tr>
<td>다양성</td>
<td>안정 해시</td>
</tr>
<tr>
<td>조절 가능한 데이터 일관성</td>
<td>정족수 합의</td>
</tr>
<tr>
<td>일시적 장애 처리</td>
<td>느슨한 정족수 프로토콜과 단서 후 임시 위탁</td>
</tr>
<tr>
<td>영구적 장애 처리</td>
<td>머클 트리</td>
</tr>
<tr>
<td>데이터 센터 장애 대응</td>
<td>여러 데이터 센터에 걸쳐 데이터 다중화</td>
</tr>
</tbody></table>
<p>[230204] 노션 -&gt; 벨로그 이동 :: 가독성 개선 예정 😇</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CH5] 안정 해시 설계]]></title>
            <link>https://velog.io/@ej_shin/CH5-%EC%95%88%EC%A0%95-%ED%95%B4%EC%8B%9C-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@ej_shin/CH5-%EC%95%88%EC%A0%95-%ED%95%B4%EC%8B%9C-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Sat, 04 Feb 2023 05:41:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/c35f7cb2-5fd0-4010-9024-d846c45dd1c8/image.jpeg" alt="">
<a href="http://www.yes24.com/Product/Goods/102819435">가상 면접 사례로 배우는 대규모 시스템 설계 기초 (알렉스 쉬 저)</a>를 읽고 정리합니다. </p>
</blockquote>
<blockquote>
<p>📢 <code>안정 해시</code> : 수평적 규모 확장성을 위해 요청 또는 데이터를 서버에 균등하게 나누는 기술</p>
</blockquote>
<h2 id="안정-해시">안정 해시</h2>
<h3 id="0️⃣-해시-키-재배치-rehash">0️⃣ 해시 키 재배치 (rehash)</h3>
<ul>
<li><p>N개의 캐시 서버에 부하를 균등하게 나누는 해시 함수</p>
<pre><code class="language-java">  serverIndex = hash(key) % N</code></pre>
<ul>
<li>특정 키가 보관된 서버를 찾기 위한 나머지 연산을 수행</li>
<li>추천 환경<ul>
<li>서버 풀 (server pool) 크기 고정된 환경</li>
<li>데이터 분포가 균등한 환경</li>
</ul>
</li>
<li>비추천 환경<ul>
<li>서버가 추가되거나, 기존 서버가 삭제될 가능성이 있는 환경</li>
<li>즉, 키 분포가 변화 될 가능성이 있을 경우 대규모 캐시 미스 (cache miss) 발생 가능성 존재</li>
</ul>
</li>
</ul>
</li>
<li><p>위 문제를 해결하기 위해 안정 해시 사용</p>
<ul>
<li>전통적 해시 테이블은 보통 슬롯 수가 바뀌면 전체 키를 재배치한다</li>
</ul>
</li>
</ul>
<br>

<h3 id="1️⃣-안정-해시-consistent-hash">1️⃣ 안정 해시 (consistent hash)</h3>
<ul>
<li>테이블 크기가 조정될 때 평균적으로 <code>k/n개의 키만 재배치</code>하는 해시 기술<ul>
<li>k = 키의 개수</li>
<li>n = 슬롯 개수</li>
</ul>
</li>
<li>동작 원리<ul>
<li>기본 용어<ul>
<li><code>해시 공간</code> : 해시 함수의 출력 값 범위 공간</li>
<li><code>해시 링</code> : 해시 공간의 양쪽을 구부려 접은 것</li>
<li><code>해시 서버</code> : 해시 함수 f를 사용해 서버 IP 또는 이름을 해시 링 위에 배치</li>
<li><code>해시 키</code> : 해시 함수를 이용, 캐시할 키를 해시 링 위에 배치</li>
</ul>
</li>
<li><code>서버와 키 배치</code> : <strong>균등 분포 해시 함수</strong>를 이용해, 해시 링에 서버와 키 배치</li>
<li><code>서버 조회</code> : 해당 키의 위치로부터 <strong>시계 방향으로 링을 탐색하는 중 만나는 첫 번째 서버에 키가 저장</strong>된다.</li>
<li><code>서버가 추가/제거</code>될 경우, <strong>키 가운데 일부를 재배치</strong>한다.</li>
</ul>
</li>
<li>문제<ul>
<li>서버 추가/제거 상황에서 파티션 크기를 균등하게 유지할 수 없다.<ul>
<li>파티션 : 인접한 서버 사이의 해시 공간</li>
</ul>
</li>
<li>키의 균등 분포 (uniform distribution)을 달성하기 어렵다.</li>
<li>위 문제를 해결하기 위해 가상 노드 (= 복제) 기술 사용</li>
</ul>
</li>
</ul>
<br>

<h3 id="2️⃣-가상-노드-virtual-node--복제-replica">2️⃣ 가상 노드 (virtual node) = 복제 (replica)</h3>
<ul>
<li><code>가상 노드</code> : 실제 노드 또는 서버를 가리키는 노드<ul>
<li>서버를 링에 배치할 때 여러 개의 가상 노드를 이용, 여러 개의 파티션을 관리한다.</li>
</ul>
</li>
<li><code>서버 조회</code> : 해당 키의 위치로부터 <strong>시계 방향으로 링을 탐색하는 중 만나는 첫 번째 가상 노드가 나타내는 서버에 키가 저장</strong>된다.<ul>
<li>가상 노드 개수가 많을 수록 표준 편차가 작아져, 키의 분포가 균등해진다.<ul>
<li>표준 편차 : 데이터가 어떻게 퍼져 나갔는지 보이는 척도</li>
<li>단, 가상 노드를 저장할 공간이 증가하니 trade-off 필요</li>
</ul>
</li>
</ul>
</li>
<li><code>재배치할 키 결정</code><ul>
<li>서버 추가 / 삭제 : 새로 추가 / 삭제된 노드부터 반시계 방향에 있는 첫 번째 서버 까지에 있는 키들을 재배치</li>
</ul>
</li>
</ul>
<br>

<h3 id="3️⃣-안정-해시의-이점">3️⃣ 안정 해시의 이점</h3>
<ul>
<li>서버 추가 / 삭제될 때 재배치되는 키의 수를 최소화할 수 있다.</li>
<li>데이터가 보다 균등하게 분포되어 수평적 규모 확장성을 달성하기 쉽고, 따라서 핫스팟 키 문제를 줄일 수 있다.<ul>
<li>핫스팟 : 특정 샤드에 대한 접근이 지나치게 빈번할 때 서버 과부하 문제가 생길 수 있다.</li>
</ul>
</li>
<li>예시<ul>
<li>아마존 dynamoDB의 파티셔닝 관련 컴포넌트</li>
<li>아파치 카산드라 클러스터에서의 데이터 파티셔닝</li>
<li>디스코드 채팅 어플리케이션 등…..</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CH4] 처리율 제한 장치의 설계]]></title>
            <link>https://velog.io/@ej_shin/CH4-%EC%B2%98%EB%A6%AC%EC%9C%A8-%EC%A0%9C%ED%95%9C-%EC%9E%A5%EC%B9%98%EC%9D%98-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@ej_shin/CH4-%EC%B2%98%EB%A6%AC%EC%9C%A8-%EC%A0%9C%ED%95%9C-%EC%9E%A5%EC%B9%98%EC%9D%98-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Fri, 03 Feb 2023 15:35:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/c35f7cb2-5fd0-4010-9024-d846c45dd1c8/image.jpeg" alt="">
<a href="http://www.yes24.com/Product/Goods/102819435">가상 면접 사례로 배우는 대규모 시스템 설계 기초 (알렉스 쉬 저)</a>를 읽고 정리합니다. </p>
</blockquote>
<h2 id="처리율-제한-장치-rate-limiter">처리율 제한 장치 (rate limiter)</h2>
<blockquote>
<p>💡 클라이언트 또는 서비스가 보내는 트래픽 처리율을 제어하기 위한 장치</p>
</blockquote>
<h3 id="0️⃣-왜-필요한가">0️⃣ 왜 필요한가?</h3>
<ul>
<li>Dos 공격에 의한 자원 고갈 (resource starvation)을 방지할 수 있다.</li>
<li>처리를 제한함으로써 서버를 많지 않게 두거나, 우선 순위가 높은 API에 더 높은 자원을 할당하는 식으로 비용을 절감할 수 있다.</li>
<li>잘못된 이용 패턴으로 유발된 트래픽을 막는 등 서버 과부하를 방지할 수 있다.</li>
<li>예시<ul>
<li>HTTP : 특정 기간 내 전송되는 클라이언트 요청 횟수를 제한<ul>
<li>요청 횟수가 threshold를 넘어서면 추가로 요청된 호출은 모두 block</li>
</ul>
</li>
<li>사용자는 초당 2회 이상 새 글을 올릴 수 없다</li>
<li>같은 IP 주소로는 하루에 10개 이상의 계정을 생성할 수 없다</li>
</ul>
</li>
</ul>
<br>

<h3 id="1️⃣-문제-이해-및-설계-범위-확정--필요한-알고리즘-특정">1️⃣ 문제 이해 및 설계 범위 확정 :: 필요한 알고리즘 특정</h3>
<ul>
<li>요구에 따라 알고리즘의 장단점을 따지며 뭘 쓸지 고민해보자<ul>
<li>이런 질문을 해보자<ul>
<li>어떤 종류의 처리율 제한 장치? 서버? 클라이언트?</li>
<li>어떤 기준? IP 주소? 사용자 ID?</li>
<li>시스템 규모? 스타트업? 대규모 요청?</li>
<li>환경? 분산 환경? 모놀리틱?</li>
<li>독립적? 코드에 종속?</li>
<li>사용자 요청이 걸러진 경우 그 사실을 알려야 하는가?</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="2️⃣-개략적인-설계안-제시-및-동의-구하기">2️⃣ 개략적인 설계안 제시 및 동의 구하기</h3>
<ul>
<li><strong>처리율 제한 장치를 어디에 둘건가?</strong><ul>
<li>클라이언트측 : 위변조가 쉽고, 모든 구현을 통제하기 어렵기에 일반적으로 안정적이지 못하다.</li>
<li>미들웨어측 : 처리율 제한 미들웨어를 만들어 거기서 <strong>API 서버로의 요청을 통제</strong>하게 만들자.<ul>
<li>API gateway가 이를 지원해준다.</li>
<li>만약 마이크로서비스이고, API 게이트웨이를 이미 설계에 포함했거나, 또는 처리율 제한 장치 구현 인력이 부족하다면 <strong>API 게이트웨이를 이용</strong>하는게 바람직하다.</li>
</ul>
</li>
<li>서버측 : 프로그래밍 언어 등이 서버측 구현을 지원할만큼 효율이 높은지 검토해보자. 만약 서버에서 직접 구현할 경우 알고리즘은 자유롭게 선택 가능</li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>처리율 제한 알고리즘</strong></p>
<ul>
<li><code>토큰 버킷</code> : 간단하고, 세간의 이해도도 높아 폭넓게 사용되는 중<ul>
<li>버킷이 꽉 찼다면 추가로 공급된 토큰은 추가되지 않고 버려진다 (overflow)</li>
<li>요청은 처리할 때마다 토큰을 사용한다. 토큰이 있으면 꺼내고 요청을 전달하고, 없으면 요청을 버리는 방식</li>
<li>장점<ul>
<li>구현쉽고, 메모리 효율적이고, 짧은 시간에 집중되는 (burst of traffic)도 커버 가능하고, 버킷에 토큰 남아 있으면 요청은 무조건 시스템에 전달된다.</li>
</ul>
</li>
<li>단점<ul>
<li><strong>버킷 크기와 토큰 공급률</strong>을 정하기가 까다롭다.<ul>
<li>보통 API endpoint마다 버킷을 둔다.</li>
<li>만약 IP주소마다 처리율을 제한해야 한다면 IP주소마다 버킷을 둬야 하고, 초당 요청으로 제한하고 싶다면 모든 요청이 하나의 버킷을 쓰게 해야 한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code>누출 버킷</code> : 요청 처리율 고정되어 있는 토큰 버킷 알고리즘, FIFO 큐로 구현<ul>
<li>동작 원리<ul>
<li>큐에 빈 자리가 있는지 확인</li>
<li>가득 찼으면 새 요청을 버리고, 빈 자리가 있으면 큐에 요청 추가</li>
<li>지정된 시간마다 큐에서 요청 꺼내서 처리</li>
</ul>
</li>
<li>장점<ul>
<li>큐 크기 제한적이라 메모리 효율적, 고정된 처리율이라 안정적 출력(stable outflow rate)이 필요할 때 적합</li>
</ul>
</li>
<li>단점<ul>
<li>단시간에 많은 트래픽 몰리면 큐에 오래된 요청이 쌓여지고, 그 요청 제때 처리 못하면 새 요청이 버려짐</li>
<li>버킷 크기 (= 큐 사이즈), 처리율(= 정해진 시간에 몇 개 처리?) 인자를 관리하기 힘듬</li>
</ul>
</li>
</ul>
</li>
<li><code>고정 윈도 카운터</code><ul>
<li>동작 원리<ul>
<li>타임라인을 고정 간격의 윈도로 나누고, 윈도마다 카운터 붙인다</li>
<li>요청 접수된 카운터 += 1</li>
<li>카운터 값이 사전 정의된 임계에 도달하면 새 요청은 새 윈도 열릴 때까지 버려진다</li>
</ul>
</li>
<li>장점<ul>
<li>메모리 효율 좋고, 이해하기 쉽고, 특정 트래픽 패턴 처리에 적합</li>
</ul>
</li>
<li>단점<ul>
<li>윈도 경계 부근에서 일시적인 트래픽이 몰려들 때, 기대했던 처리 한도보다 많은 양의 요청을 처리하게 됨</li>
</ul>
</li>
</ul>
</li>
<li><code>이동 윈도 로깅</code> : 고정 윈도의 윈도 부근 트래픽 문제를 해결<ul>
<li>동작 방식<ul>
<li>요청 타임스탬프 추적 (보통 레디스 등 캐시에 보관)</li>
<li>새 요청이 오면 만료된 타임스탬프(= 윈도 시작 시점보다 오래된 것) 제거</li>
<li>새 요청 타임스탬프를 로그에 추가</li>
<li>로그 크기가 허용치보다 같거나 작으면 요청 전달, 아니면 처리 거부</li>
</ul>
</li>
<li>장점<ul>
<li>어느 순간의 윈도도 허용 요청 개수는 시스템 처리 한도를 넘지 않음</li>
</ul>
</li>
<li>단점<ul>
<li>거부된 요청의 타임스탬프도 보관 ⇒ 메모리 많이 잡아먹음</li>
</ul>
</li>
</ul>
</li>
<li><code>이동 윈도 카운터</code> : 고정 윈도 카운터 + 이동 윈도 로깅<ul>
<li>장점<ul>
<li>이전 시간대 평균 처리율에 따라 윈도 상태 계산 → 짧은 시간 트래픽 대응 O</li>
<li>메모리 효율 굿</li>
</ul>
</li>
<li>단점<ul>
<li>직전 시간대 요청이 균등히 분포되어 있다고 가정 → 느슨<ul>
<li>실험 결과 그렇게 심각하지 않음</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>개략적인 아키텍처</strong></p>
<ul>
<li>얼마나 많은 요청이 접수되었는지 추적할 수 있는 카운터를 추적 대상별로 생성 → 카운터 값이 한도를 넘어서면 요청 거부<ul>
<li>카운터를 어디에 보관할건지?<ul>
<li>DB는 디스크 접근이라 느리니까 안됨</li>
<li>캐시가 적절 : 빠르고 시간에 기반한 만료 정책 쓰기 때문<ul>
<li>redis : 클라이언트가 처리율 제한 미들웨어에게 요청을 보내면, 미들웨어는 레디스 지정 버킷에서 카운터를 가져와 한도 도달 여부를 확인한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="3️⃣-상세-설계">3️⃣ 상세 설계</h3>
<ul>
<li><code>처리율 제한 규칙</code>은 어떻게 만들어지고 어디에 저장되나?<ul>
<li>보통 오픈소스 등을 활용할 수 있고, 설정 파일 형태로 디스크에 저장된다.</li>
</ul>
</li>
<li><code>처리가 제한된 요청</code>들은 어떻게 처리하나? (= 처리율 한도 초과 트래픽 처리 = throttle)<ul>
<li>HTTP 429 응답만 보내거나<ul>
<li>HTTP 응답 헤더로 확인할 수도 있다.<ul>
<li>X-Ratelimit-Remaining</li>
<li>X-Ratelimit-Limit</li>
<li>X-Ratelimit-Retry0After</li>
</ul>
</li>
</ul>
</li>
<li>또는 나중에 처리하기 위해 큐에 보관한다.</li>
</ul>
</li>
<li><code>분산 환경</code>에서의 처리율 제한 장치의 구현<ul>
<li>여러대의 서버와 병렬 스레드 지원하도록 시스템 확장하기.</li>
<li>경쟁 조건 (race condition)<ul>
<li>요청을 처리하는 스레드가 병렬로 레디스에서 값을 읽었다면, 그리고 둘 중 어느쪽도 아직 변경 값을 저장하지 않았다면 카운터가 이상하게 작동한다.</li>
<li>락을 걸어 해결할 수 있지만, 성능 이슈가 있다.<ul>
<li>이 경우 루아 스크립트 또는 정렬 집합 자료구조를 이용해 해결할 수 있다.</li>
</ul>
</li>
</ul>
</li>
<li>동기화 (synchronization)<ul>
<li>처리율 제한 장치 서버가 여러 대인 경우 동기화가 필요하다.</li>
<li>고정 세션 (sticky session)을 활용해 같은 클라이언트 요청을 같은 장치로 보내게 만들 수 있다.<ul>
<li>확장이 불가능하고 유연하지 않아 비추천</li>
</ul>
</li>
<li>레디스같은 중앙 집중형 데이터 저장소를 쓸 수 있다.</li>
</ul>
</li>
</ul>
</li>
<li><code>성능 최적화</code><ul>
<li>데이터 센터 지원<ul>
<li>에지 서버를 이용해 트래픽 전달 지연 시간을 줄인다.</li>
</ul>
</li>
<li>제한 장치 간 데이터 동기화에 최종 일관성 모델 사용 (eventual consistency model)</li>
</ul>
</li>
<li><code>모니터링</code><ul>
<li>아래를 모니터링을 통해 확인한다<ul>
<li>채택된 처리율 제한 알고리즘이 효과적인지</li>
<li>정의한 처리율 제한 규칙이 효과적인지</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="4️⃣-마무리--설계에-대한-후속-질문-및-추가-논의">4️⃣ 마무리 :: 설계에 대한 후속 질문 및 추가 논의</h3>
<ul>
<li>경성 (hard) 또는 연성 (soft) 처리율 제한<ul>
<li>경성 처리율 제한 : 요청 개수는 임계치를 절대 못 넘는다</li>
<li>연성 처리율 제한 : 요청 개수는 잠시 동안은 임계치를 넘을 수 있다</li>
</ul>
</li>
<li>다양한 계층에서의 처리율 제한<ul>
<li>OSI 계층 어디에서나 처리율 제한이 가능하다</li>
</ul>
</li>
<li>처리율 제한을 회피하는 방법<ul>
<li>클라이언트를 어떻게 설계할 것인가?<ul>
<li>캐시 사용으로 API 호출 횟수 줄이기</li>
<li>짧은 시간에 너무 많은 메시지 보내지 말기</li>
<li>예외나 에러 처리 코드를 도입해 클라이언트가 예외 상황에서 복구될 수 있게 하기</li>
<li>재시도 로직 구현에는 충분한 백오프 시간을 두기</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CH3] 시스템 설계 면접 공략법]]></title>
            <link>https://velog.io/@ej_shin/CH3-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EB%A9%B4%EC%A0%91-%EA%B3%B5%EB%9E%B5%EB%B2%95</link>
            <guid>https://velog.io/@ej_shin/CH3-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EB%A9%B4%EC%A0%91-%EA%B3%B5%EB%9E%B5%EB%B2%95</guid>
            <pubDate>Fri, 03 Feb 2023 00:12:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/c35f7cb2-5fd0-4010-9024-d846c45dd1c8/image.jpeg" alt="">
<a href="http://www.yes24.com/Product/Goods/102819435">가상 면접 사례로 배우는 대규모 시스템 설계 기초 (알렉스 쉬 저)</a>를 읽고 정리합니다. </p>
</blockquote>
<blockquote>
<p>💡 만약에 내가 구글 면접을 가. 근데 시스템 설계 면접을 볼거래. 
내가…. 구글 시스템 설계를?....</p>
</blockquote>
<p>👉 정답 기대하는게 아님!</p>
<h2 id="1️⃣-팁">1️⃣ 팁</h2>
<blockquote>
<p>💡 시스템 설계는 정해진 정답이 없는 질문이다.</p>
</blockquote>
<ul>
<li><p><strong>보려는 것</strong></p>
<ul>
<li><p>설계 기술 시연</p>
</li>
<li><p>설계 과정에서 내린 결정에 대한 방어 능력</p>
<p>  → 왜 그렇게 설계했는지에 대한 질문을 받아 칠 수 있는지? </p>
</li>
<li><p>면접관의 피드백을 건설적으로 처리할 자질</p>
<p>  → 피드백을 받아 내 설계에 반영할 수 있는지? </p>
</li>
</ul>
</li>
<li><p><strong>보려는 이유</strong></p>
<ul>
<li>협력에 적합한 사람인지?</li>
<li>압박이 심한 상황에서 잘 판단 내릴 수 있는지?</li>
<li>모호한 문제를 건설적으로 해결할 수 있는지?</li>
<li>좋은 질문을 할 수 있는지?</li>
<li>완고하지 않고, 편협하지 않고…</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2️⃣-효과적-면접을-위한-4단계-접근법">2️⃣ 효과적 면접을 위한 4단계 접근법</h2>
<blockquote>
<p>💡 시스템 설계 면접의 공통 분모</p>
</blockquote>
<h3 id="1-문제-이해-및-설계-범위-확정--모호함-없애기">1. 문제 이해 및 설계 범위 확정 :: 모호함 없애기</h3>
<ul>
<li>빠른 답변 필요 X<ul>
<li>요구사항과 가정을 분명히 하기 위해 신중하게 생각하자</li>
<li><code>올바른 질문, 적절한 가정, 시스템 구축에 필요한 정보</code>를 모으는게 중요. 답변에 시간 좀 들어도 된다</li>
</ul>
</li>
<li>이런 질문을 해보자<ul>
<li>구체적으로 어떤 기능들이 필요? 가장 중요한 기능은?</li>
<li>제품 사용자 수는?</li>
<li>회사 규모의 확대 속도와 기대 수명은?</li>
<li>회사 사용 기술 스택은? 설계 단순화를 위한 기존 서비스는?</li>
</ul>
</li>
</ul>
<h3 id="2-개략적인-설계안-제시-및-동의-구하기--면접관과-협력하기">2. 개략적인 설계안 제시 및 동의 구하기 :: 면접관과 협력하기</h3>
<ul>
<li>이런걸 해보자<ul>
<li>설계 최초 청사진 제시 후 의견 구하기<ul>
<li>면접관이 팀원인 것처럼!</li>
</ul>
</li>
<li>핵심 컴포넌트를 포함하는 다이어그램 그리기<ul>
<li>클라, API, 웹 서버, DB, 캐시, CDN, 메세지 큐 등…</li>
</ul>
</li>
<li>최초 설계안이 시스템 규모 제약을 만족하는지 계산해보기<ul>
<li>개략적인 추정이 필요한지 미리 확인 받고!</li>
</ul>
</li>
<li>시스템 구체적 사용 사례 확인하기<ul>
<li>엣지 케이스가 있는지?</li>
</ul>
</li>
<li>질문에 따라 API 엔드포인트, DB 스키마도 고려<ul>
<li>전체 시스템이 아닌 백엔드 시스템을 설계하라는 질문이면 필요할 것!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="3-상세-설계--설계-컴포넌트-우선순위-정하기">3. 상세 설계 :: 설계 컴포넌트 우선순위 정하기</h3>
<ul>
<li>면접관이 강조한 설계 영역<ul>
<li>보통 시니어라면?<ul>
<li>시스템 성능 특성 → 병목 구간, 자원 요구량 추정치</li>
<li>컴포넌트 세부사항에 초점 맞췄을 것</li>
</ul>
</li>
</ul>
</li>
<li>시간 관리하기<ul>
<li>사소한건 버리고, 면접관이 관심 있어하는 요소에 집중하자</li>
</ul>
</li>
</ul>
<h3 id="4-마무리--설계에-대한-후속-질문-및-추가-논의">4. 마무리 :: 설계에 대한 후속 질문 및 추가 논의</h3>
<ul>
<li>시스템 개선 방법에 대한 질문<ul>
<li>비판적 사고 → 일단 그 자리에서 내가 완벽퍼펙트어메이징한 설계를 하진 않았을 것ㅋㅋ 뭘 놓쳤을까 한번 찾아보자</li>
</ul>
</li>
<li>설계 요악</li>
<li>오류 발생했을 때 어떤 일이 생기는지?</li>
<li>운영 이슈 (메트릭 수집, 모니터링, 로그, 배포…)</li>
<li>규모 확장</li>
<li>등등</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CH2] 개략적인 규모 추정]]></title>
            <link>https://velog.io/@ej_shin/CH2-%EA%B0%9C%EB%9E%B5%EC%A0%81%EC%9D%B8-%EA%B7%9C%EB%AA%A8-%EC%B6%94%EC%A0%95</link>
            <guid>https://velog.io/@ej_shin/CH2-%EA%B0%9C%EB%9E%B5%EC%A0%81%EC%9D%B8-%EA%B7%9C%EB%AA%A8-%EC%B6%94%EC%A0%95</guid>
            <pubDate>Fri, 03 Feb 2023 00:09:20 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/c35f7cb2-5fd0-4010-9024-d846c45dd1c8/image.jpeg" alt="">
<a href="http://www.yes24.com/Product/Goods/102819435">가상 면접 사례로 배우는 대규모 시스템 설계 기초 (알렉스 쉬 저)</a>를 읽고 정리합니다. </p>
</blockquote>
<blockquote>
<p>💡 만약에 내가 면접에 가. 근데 면접자가 시스템 용량이나 성능 요구사항을 추정해보래. 그럼 뭐라고 대답해야 하게요?</p>
</blockquote>
<p>👉 보편적인 성능 수치 측정 도구를 이용해 추정치를 계산해보자!</p>
<h2 id="1️⃣-2의-제곱-수">1️⃣ 2의 제곱 수</h2>
<ul>
<li>데이터 볼륨 단위를 2의 제곱수로 표현하자.<ul>
<li>최소 단위 : 1바이트 (= ASCII 문자 하나가 차지하는 메모리 크기)</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th>2의 x 제곱</th>
<th>이름</th>
<th>축약형</th>
</tr>
</thead>
<tbody><tr>
<td>10</td>
<td>1 킬로바이트</td>
<td>1KB</td>
</tr>
<tr>
<td>20</td>
<td>1 메가바이트</td>
<td>1MB</td>
</tr>
<tr>
<td>30</td>
<td>1 기가바이트</td>
<td>1GB</td>
</tr>
<tr>
<td>40</td>
<td>1 테라바이트</td>
<td>1TB</td>
</tr>
<tr>
<td>50</td>
<td>1 페타바이트</td>
<td>1PB</td>
</tr>
</tbody></table>
<hr>
<h2 id="2️⃣-응답-지연-latency-값">2️⃣ 응답 지연 (latency) 값</h2>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/981f169f-b5bb-4a64-8618-47b4a831fd71/image.png" alt=""></p>
<ul>
<li>대충 이런걸 알 수 있다<ul>
<li>메모리는 빠르지만 디스크는 느리니까 가능한 디스크 탐색은 피해라</li>
<li>단순 압축 알고리즘은 빠르니까, 데이터를 전송하기 전에 가능한 압축해라</li>
<li>데이터센터는 분산되어 있고, 센터간 데이터 주고받는데는 시간이 걸린다</li>
</ul>
</li>
</ul>
<hr>
<h2 id="3️⃣-가용성-관련-수치">3️⃣ 가용성 관련 수치</h2>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/d85507e1-45fb-4bf9-98c9-0ceb50801746/image.png" alt=""></p>
<ul>
<li>시스템이 중단 없이 지속적으로 운영될 수 있는지 측정하는 방법</li>
<li>100%면 한번도 중단 된 적 없다는 의미</li>
</ul>
<hr>
<h2 id="4️⃣-팁">4️⃣ 팁</h2>
<blockquote>
<p>💡 결과보다 과정이 중요! 문제 해결 능력에 초점</p>
</blockquote>
<ul>
<li>근사치 활용해서 계산하기</li>
<li>가정를 확인하기 위해 적어두기</li>
<li>단위 붙여서 정확하게</li>
<li>주로 <code>QPS, 최대 QPS, 저장소 요구량, 캐시 요구량, 서버 수 추정</code>을 많이 물어본다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CH1] 사용자 수에 따른 규모 확장성]]></title>
            <link>https://velog.io/@ej_shin/CH1-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5%EC%84%B1</link>
            <guid>https://velog.io/@ej_shin/CH1-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5%EC%84%B1</guid>
            <pubDate>Fri, 03 Feb 2023 00:06:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/c35f7cb2-5fd0-4010-9024-d846c45dd1c8/image.jpeg" alt="">
<a href="http://www.yes24.com/Product/Goods/102819435">가상 면접 사례로 배우는 대규모 시스템 설계 기초 (알렉스 쉬 저)</a>를 읽고 정리합니다. </p>
</blockquote>
<p>새해가 되면서 스터디를 진행했는데, 노션에만 열심히 적는게 아까워서 벨로그에도 올려봅니다👀 
아니 근데 마크다운 양식인데도 복사 붙여넣기 했을 때 깨지는게 많아서 일일이 수정해야 하네요..? 처음부터 블로그에 쓸 걸 😇</p>
<h2 id="🖥-단일-서버-시스템-구성">🖥 단일 서버 시스템 구성</h2>
<blockquote>
<p>💡 웹, DB, 캐시 등이 모두 서버 한 대에서 실행된다면?</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/90d4fbee-51c1-4f82-91ba-5c658d300df4/image.png" alt=""></p>
<p>도메인 이름을 IP 주소로 변환하는 과정을 거쳐 사용자가 웹 사이트에 접속하고, 해당 IP 주소에서 HTTP 요청과 응답이 오고 간다. </p>
<hr>
<h2 id="🤔-사용자가-늘어나도-해당-구조를-그대로-유지하나">🤔 사용자가 늘어나도 해당 구조를 그대로 유지하나?</h2>
<blockquote>
<p>💡 당근빠따 안되기 때문에 아래 행위 고려하기</p>
</blockquote>
<h3 id="데이터베이스-서버-분리"><code>데이터베이스 서버 분리</code></h3>
<ol>
<li>웹/모바일 트래픽 처리용 서버와 데이터베이스 서버를 <strong>분리해서 독립적으로 확장</strong>한다. </li>
<li>데이터베이스는 시스템에 따라 RDB 또는 NoSQL을 선택할 수 있다. </li>
</ol>
<h3 id="수직적-규모-확장--스케일-업"><code>수직적 규모 확장 (= 스케일 업)</code></h3>
<ul>
<li>서버에 <strong>고사양 자원</strong> (더 좋은 CPU, 더 많은 RAM, …)을 추가하는 행위</li>
<li>장점 : 트래픽 양이 적을 때 단순하니 좋은 선택</li>
<li>단점 <pre><code>  1. 한계 존재 : 하나의 서버에 무한대의 자원을 추가할 수 없다 (당연함)
  2. 장애 대응 : 장애 자동 복구 (failover)이나 다중화 방안 X (고가용성 X)</code></pre></li>
</ul>
<h3 id="수평적-규모-확장--스케일-아웃"><code>수평적 규모 확장 (= 스케일 아웃)</code></h3>
<ul>
<li><strong>더 많은 서버</strong>를 추가해 성능을 개선하는 행위</li>
<li>대규모 애플리케이션에 적합</li>
</ul>
<h3 id="로드-밸런서"><code>로드 밸런서</code></h3>
<ol>
<li><p><strong>등장 배경</strong></p>
<ul>
<li>사용자가 웹 서버에 바로 연결되면 <strong>서버 상태에 서비스가 직접적인 영향</strong>을 받는다</li>
<li>사용자가 몰리면 <strong>웹 서버 응답 속도가 느려지거나, 접속이 어려워</strong>진다 </li>
</ul>
<p>👉 부하 분산기, 또는 로드 밸런서를 이용해 위 문제를 해결하고자 함</p>
</li>
<li><p><strong>역할</strong></p>
<ul>
<li>부하 분산 집합에 속한 웹 서버들에게 <strong>트래픽 부하를 고르게 분산</strong>한다. </li>
</ul>
</li>
<li><p><strong>동작 방식</strong></p>
<ul>
<li>사용자는 웹 서버가 아닌 로드 밸런서의 공개 IP로 접속한다. </li>
<li>로드 밸런서는 요청을 사설 IP 주소를 이용해 웹 서버와 통신한다. </li>
<li>로드 밸런서는 부하 분산 집합에 존재하는 웹 서버들에게 요청을 분산하여 나눠준다.</li>
</ul>
</li>
<li><p><strong>웹 가용성 향상</strong></p>
<ul>
<li><p>서버 1이 다운되면 로드 밸런서는 서버 2에게 트래픽을 전송한다. 
  👉 웹 사이트 <strong>전체가 다운되는 일 방지</strong> 가능</p>
</li>
<li><p>유입 트래픽이 증가하면 웹 서버 계층에 더 많은 서버를 추가해서, 로드밸런서가 적절하게 서버로 트래픽을 나눠주게 만든다. 
  👉 <strong>많은 트래픽을 간단하게 관리</strong>할 수 있음 </p>
</li>
</ul>
</li>
</ol>
<h3 id="데이터베이스-다중화"><code>데이터베이스 다중화</code></h3>
<ol>
<li><strong>master-slave 관계 설정</strong><ul>
<li>master (주서버)<ol>
<li><strong>쓰기 연산</strong> 지원 (insert, update, delete). 데이터 원본 저장</li>
</ol>
</li>
<li>slave (부서버)<ol>
<li><strong>읽기 연산만</strong> 지원 (only read). 마스터의 데이터 사본을 전달받는다 </li>
<li>보통 서비스는 읽기를 많이 하니까, 슬레이브 데이터베이스 수가 더 많은게 보편적</li>
</ol>
</li>
</ul>
</li>
<li><strong>장점</strong><ul>
<li><strong>성능 개선</strong> : 데이터 변경 연산은 마스터로, 읽기 연산은 슬레이브로 분산되므로 병렬 처리 가능한 쿼리 수가 늘어난다. </li>
<li><strong>안정성</strong> : 데이터베이스 서버 일부에 장애가 발생하거나 파괴되어도 사본이 남아있으므로 데이터는 보존된다. </li>
<li><strong>가용성</strong> : 데이터베이스 서버 일부에 장애(이하생략) 다른 서버에 있는 데이터를 가져오면 되므로 서비스는 중단되지 않는다. </li>
</ul>
</li>
<li><strong>예시</strong><ol>
<li><strong>부서버가 한대인데 다운 발생</strong><ul>
<li>읽기 연산이 일시적으로 주서버에 전달됨</li>
<li>새로운 부서버가 올라와 장애 서버 대체</li>
</ul>
</li>
<li><strong>부서버가 여러대인데 그 중 하나에 다운 발생</strong><ul>
<li>나머지 부서버들로 읽기 연산 분산</li>
<li>새로운 부서버가 올라와 장애 서버 대체</li>
</ul>
</li>
<li><strong>주서버가 다운</strong><ul>
<li>부서버 한대면 해당 서버가 주 서버를 대체하며, 읽기/쓰기 연산이 일시적으로 주 서버에서 처리</li>
<li>부서버 데이터가 최신 데이터가 아닌 경우 복구 스크립트를 활용</li>
<li>다중 마스터 구조, 원형 다중화 구조를 도입해 해결할 수도 있음</li>
</ul>
</li>
</ol>
</li>
</ol>
<h3 id="개선된-구조"><code>개선된 구조</code></h3>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/7f1acc02-c361-4e65-9ba1-8db324a57557/image.png" alt=""></p>
<hr>
<h2 id="⚡️-난-응답-시간도-개선하고-싶어요">⚡️ 난 응답 시간도 개선하고 싶어요!</h2>
<blockquote>
<p>💡 캐시와 CDN을 이용해 응답 시간 개선하기</p>
</blockquote>
<h3 id="캐시--db-부하를-줄여요"><code>캐시</code> : DB 부하를 줄여요~</h3>
<ol>
<li><strong>의미</strong><ul>
<li>처리가 오래 걸리는 연산 결과 또는 자주 참조되는 <strong>데이터를 메모리에 두고, 이후 요청을 보다 빠르게 처리</strong>해주는 저장소</li>
<li>어플리케이션 성능은 데이터베이스 호출 수에 크게 좌우된다 (디스크까지 다이빙하기 힘드니까)</li>
</ul>
</li>
<li><strong>캐시 계층</strong><ul>
<li>데이터가 잠시 보관되는 곳으로 DB보다 훨씬 빠르다.</li>
<li>캐시 계층을 이용해 DB 부하를 줄이고, 캐시 계층 규모를 독립적으로 확장시킬 수 있다 <ul>
<li><strong>캐시 주도 전략</strong> : 캐시에 없으면 DB 조회하는 방식!</li>
</ul>
</li>
</ul>
</li>
<li><strong>주의할 점</strong><ol>
<li>캐시는 <strong>데이터 갱신은 적지만, 참조는 자주 일어나는 데이터</strong>에 적절하다</li>
<li><strong>휘발성 메모리를 사용</strong>하므로, 중요한 데이터는 지속적 저장소에 두어야 한다. </li>
<li>데이터가 계속 캐시에 남는 걸 막기 위해, <strong>적절한 만료 기한</strong>을 두어야 한다. </li>
<li>단일 트랜잭션을 이용, 지속적 저장소의 데이터와 캐시에 있는 <strong>데이터의 일관성을 관리</strong>해야 한다.</li>
<li>단일 장애 지점(SPOF)이 되는 걸 막기 위해 <strong>캐시 서버를 분산</strong>시켜야 한다.</li>
<li><strong>캐시 메모리를 과할당</strong>하여 데이터 방출(eviction) 현상을 방지한다.</li>
<li>캐시가 꽉 차는 경우를 대비하여 <strong>데이터 방출(eviction) 정책</strong>을 마련한다.<ol>
<li>LRU : 마지막으로 사용된 시점이 가장 오래된 친구부터 방출</li>
<li>LFU : 사용된 빈도가 가장 낮은 친구부터 방출</li>
<li>FIFO : 가장 먼저 캐시에 들어온 친구부터 방출 </li>
</ol>
</li>
</ol>
</li>
</ol>
<h3 id="콘텐츠-전송-네트워크-cdn--정적-컨텐츠는-웹-서버를-거치지-않아요"><code>콘텐츠 전송 네트워크 (CDN)</code> : 정적 컨텐츠는 웹 서버를 거치지 않아요~</h3>
<ol>
<li><strong>의미</strong><ul>
<li>정적 콘텐츠 전송에 쓰이는 지리적으로 분산된 서버의 네트워크. 정적 파일을 캐싱한다. </li>
<li>요청 경로, 쿼리 스트링, 쿠키, 헤더 등을 이용해 HTML 페이지 캐시하는 방식</li>
</ul>
</li>
<li><strong>동작 방식</strong><ul>
<li>사용자가 웹 사이트에 방문하면, 해당 사용자에게 물리적으로 제일 가까운 CDN 서버가 정적 컨텐츠를 전달해준다. </li>
<li>만약 컨텐츠가 CDN에 없다면 웹 서버에서 가져온다. 캐시를 활용하는거 맞아요.</li>
</ul>
</li>
<li><strong>고려할 점</strong><ol>
<li>서드파티 제공자가 운영하므로 <strong>비용에 주의</strong>한다.</li>
<li>시간에 민감한 <strong>콘텐츠의 만료 시한에 주의</strong>한다.</li>
<li><strong>CDN이 죽었을 경우</strong> 어떻게 처리할건지 주의한다. </li>
<li>아직 만료되지 않은 콘텐츠에 문제가 생긴 경우, <strong>콘텐츠 무효화 처리</strong>가 필요하다. </li>
</ol>
</li>
</ol>
<hr>
<h2 id="🤖-웹-계층의-수평적-확장">🤖 웹 계층의 수평적 확장</h2>
<blockquote>
<p>💡 사용자 세션 정보와 같은 상태 정보를 웹 계층에서 제거해 웹 수평적 확장을 구현한다.</p>
</blockquote>
<h3 id="무상태-웹-계층--상태-정보를-db에-저장해요"><code>무상태 웹 계층</code> : 상태 정보를 DB에 저장해요~</h3>
<ol>
<li><strong>상태 정보 의존적인 아키텍처</strong><ul>
<li>상태를 서버에 저장하기 때문에, 동일 클라이언트의 요청은 항상 동일 서버로 전송되어야 한다. </li>
<li>로드밸런서가 고정 세션 기능을 제공해주지만, 부담이 크다.</li>
<li>로드밸런서 뒷단에 서버의 추가와 제거가 어려워지고, 장애 대응도 빡세다. </li>
</ul>
</li>
<li><strong>무상태 아키텍처</strong><ul>
<li>클라 요청은 어떤 웹 서버로든 전송될 수 있다. </li>
<li>웹 서버는 상태 정보가 필요하면, 웹 서버와 물리적으로 분리된 상태 저장소에서 정보를 가져온다. </li>
<li>따라서 구조가 단순하고, 안정적이고, 확장에 유연해진다. </li>
</ul>
</li>
</ol>
<hr>
<h2 id="💽-가용성을-높이자">💽 가용성을 높이자</h2>
<blockquote>
<p>💡 높은 가용성을 위해 데이터 센터를 여러개 지원하는게 필수적!</p>
</blockquote>
<h3 id="데이터-센터"><code>데이터 센터</code></h3>
<ol>
<li><strong>지리적 라우팅</strong><ul>
<li>장애가 없는 상황에서 <strong>사용자가 가장 가까운 데이터 센터로 안내</strong>되는 것<ul>
<li>geoDNS : 사용자 위치에 따라 도메인 이름을 어떤 IP 주소로 변환할지 결정해주는 DNS 서비스</li>
</ul>
</li>
<li>데이터 센터 중 하나에 장애 발생하면, 가까운 데이터 센터로 트래픽이 전송된다. </li>
</ul>
</li>
<li><strong>다중 데이터 센터 아키텍처 구현을 위한 기술 난제</strong> <ul>
<li><strong>트래픽 우회</strong> : 올바른 데이터 센터로 트래픽 보내는 방법</li>
<li><strong>데이터 동기화</strong> : 데이터센터마다 다른 데이터베이스를 쓴다면, 페일오버 전략이 필요하다. <ul>
<li>보통 데이터를 여러 데이터 센터에 걸쳐 다중화한다. </li>
</ul>
</li>
<li><strong>테스트와 배포</strong> : 웹 사이트와 어플리케이션을 여러 위치에서 테스트해봐야 한다. 배포 자동화를 통해 모든 데이터 센터에 동일한 서비스를 설치하는 걸 도울 수 있다. </li>
</ul>
</li>
</ol>
<hr>
<h2 id="⛑-더-큰-시스템을-위하여">⛑ 더 큰 시스템을 위하여</h2>
<blockquote>
<p>💡 시스템 컴포넌트를 분리해 각각 독립적으로 확장될 수 있는 구조를 만들어야 한다.</p>
</blockquote>
<h3 id="메시지-큐"><code>메시지 큐</code></h3>
<ol>
<li><strong>의미</strong><ul>
<li><strong>데이터 무손실을 보장하는 비동기 통신을 지원하는 컴포넌트.</strong> 보통 메시지 버퍼 역할</li>
<li>뭔 🐕소리야? : 한번 메시지 큐에 저장되면 데이터를 꺼내 갈 때까지 안전히 보관된다는 소리</li>
</ul>
</li>
<li><strong>메시지 큐 기본 아키텍처</strong><ul>
<li><strong>입력 서비스 ( = 생산자 또는 발행자)</strong>가 메시지를 만들어 <strong>메시지 큐에 발행</strong>한다. </li>
<li>큐에 연결된 <strong>서비스 혹은 서버 ( = 소비자 도는 구독자)</strong>가 <strong>메시지를 받아, 역할을 수행</strong>한다. </li>
</ul>
</li>
<li><strong>장점</strong><ul>
<li>서비스, 또는 서버 간 결합이 느슨해짐 → 규모 확장성이 보장되어 안정적<ul>
<li>생산자는 소비자가 죽어도 메시지를 발행할 수 있고, 소비자는 생산자가 죽어도 메시지를 수신할 수 있다. </li>
<li>큐가 크면 소비자를 추가하고, 큐가 비어있으면 소비자를 줄이는 방식! </li>
</ul>
</li>
</ul>
</li>
</ol>
<h3 id="로그-메트릭-자동화"><code>로그, 메트릭, 자동화</code></h3>
<ol>
<li><strong>로그</strong><ol>
<li><strong>시스템 오류와 문제를 찾기 위한</strong> 에러 로그 모니터링</li>
<li>로그를 단일 서비스로 모아주는 서비스를 이용하면 다중 서버 환경에서 편리하다</li>
</ol>
</li>
<li><strong>메트릭</strong><ol>
<li><strong>사업 현황에 대한 정보와 시스템 현재 상태에 대한 정보</strong>를 얻을 수 있다. </li>
<li>유용한 메트릭 친구들<ol>
<li>호스트 단위 메트릭 : CPU, memory, disk I/O에 관한 친구들</li>
<li>종합 메트릭 : DB 계층 성능, 캐시 계층 성능에 관한 친구들</li>
<li>핵심 비즈니스 메트릭 : 일별 능동 사용자, 수익, 재방문에 관한 친구들</li>
</ol>
</li>
</ol>
</li>
<li><strong>자동화</strong><ol>
<li>시스템이 크고 복잡해지면 <strong>생산성을 높이기 위한 자동화 시스템</strong>을 도입하자.</li>
<li>CI, 빌드, 테스트, 배포 등… </li>
</ol>
</li>
</ol>
<h3 id="개선된-구조-2"><code>개선된 구조 2</code></h3>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/c66cfea4-2487-4814-8e87-146382e82f0c/image.png" alt=""></p>
<hr>
<h2 id="📈-db-규모를-확장하자">📈 DB 규모를 확장하자</h2>
<blockquote>
<p>💡 저장할 데이터가 많아졌어요 뭘 해야 돼요 DB를 확장해야 돼요</p>
</blockquote>
<h3 id="수직적-확장"><code>수직적 확장</code></h3>
<ol>
<li><strong>의미</strong><ul>
<li>기존 서버에 더 많은 고성능 자원 증설해주기</li>
<li>성능을 아주~~ 좋게 만들어서 많은 데이터를 처리할 수 있게 하는 방법</li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>증설 한계가 있어용</li>
<li>SPOF 위험성</li>
<li>서버 성능이 좋아질 수록 비용이 따블로 든다</li>
</ul>
</li>
</ol>
<h3 id="수평적-확장"><code>수평적 확장</code></h3>
<ol>
<li><strong>의미</strong><ul>
<li>더 많은 서버를 추가하기</li>
<li>샤딩 : 데이터베이스를 샤드라는 작은 단위로 분할하는 것. 모든 샤드는 같은 스키마를 공유하지만, 보관하는 데이터에는 중복이 없다. </li>
</ul>
</li>
<li><strong>고려할 점</strong><ul>
<li><strong>샤딩 키(=파티션 키)</strong>를 정해야 해요 : 데이터가 어떻게 분산될지 정하는 하나 이상의 컬럼<ol>
<li>샤딩 키를 이용해 올바른 DB에 질의를 보낼 수 있다.</li>
<li>효율적인 처리를 위해 데이터를 고르게 분할 할 수 있는 샤딩 키를 지정한다. </li>
</ol>
</li>
<li>샤딩을 도입하면 <strong>시스템이 이렇게 복잡</strong>해져요<ol>
<li><code>데이터의 재샤딩</code><ul>
<li>데이터가 너무 많아졌을 때, 샤드간 데이터 분포가 균등하지 않아 샤드 소진이 발생할 때 재 샤딩이 필요하다.</li>
<li>안정 해시 기법으로 해결 가능!</li>
</ul>
</li>
<li><code>유명인사 (= 핫스팟 키)</code><ul>
<li>특정 샤드에 질의가 집중되어 서버 과부하 걸리는 문제</li>
<li>자주 쓰이는 데이터 (유명인사)가 모두 한 샤드에 저장될 경우, 해당 유명인사 각각에 샤드를 할당해야 한다.</li>
</ul>
</li>
<li><code>조인과 비정규화</code><ul>
<li>하나의 데이터베이스를 여러 샤드 서버로 나누었으니, 여러 샤드에 걸친 데이터를 조인하기 어려워진다.</li>
<li>해결하기 위해 데이터베이스를 비정규화하여 하나의 테이블에서 질의를 수행한다.</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ch.5] 책임 할당하기]]></title>
            <link>https://velog.io/@ej_shin/Ch.5-%EC%B1%85%EC%9E%84-%ED%95%A0%EB%8B%B9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@ej_shin/Ch.5-%EC%B1%85%EC%9E%84-%ED%95%A0%EB%8B%B9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 28 Nov 2022 15:08:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/1f7bf951-3c0a-40b1-9c4e-d85e47945ded/image.gif" alt=""></p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/7e49ee8c-b4d7-4065-a4e9-93e1c14e3e01/image.jpeg" alt="">
<a href="https://wikibook.co.kr/object/">오브젝트 : 코드로 이해하는 객체지향 설계 (조영호 저) </a>를 읽고 정리합니다. </p>
</blockquote>
<br>

<h2 id="grasp-패턴으로-알아보는-책임-할당-기준">GRASP 패턴으로 알아보는 책임 할당 기준</h2>
<blockquote>
<p>어떤 객체에게 어떤 책임을 할당해야할까? 🧐</p>
</blockquote>
<h3 id="1️⃣-책임-주도-설계-리마인드">1️⃣ 책임 주도 설계 리마인드</h3>
<ul>
<li>객체의 책임과 협력에 초점 맞추기<ol>
<li><strong>데이터보다 행동을 먼저 결정할 것</strong>
: 협력에 참여하기 위해, 외부에 제공하는 행동(= 협력 속에서 수행할 책임)을 먼저 결정해야 한다. </li>
<li><strong>협력이라는 문맥 안에서 책임을 결정할 것</strong>
: 책임은 객체가 아닌 객체가 참여하는 협력에 적합해야 한다. 
: 메시지를 전송하는 클라이언트의 의도에 적합하도록, 메시지를 결정한 후 객체를 선택해야 한다. </li>
</ol>
</li>
</ul>
<ul>
<li>책임 주도 설계의 흐름<ul>
<li>책임을 결정한 후, 책임을 수행할 객체를 결정하기<ol>
<li>시스템이 사용자에게 제공해야 하는 기능, 전체 시스템 책임 파악</li>
<li>시스템 책임을 더 작은 책임으로 분할</li>
<li>분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임 할당</li>
<li>객체가 책임을 수행하는 중 다른 객체 도움이 필요한 경우, 해당 도움을 수행할 적절한 객체 또는 역할 찾기</li>
<li>찾은 객체 또는 역할에게 책임 할당 = 두 객체의 협력 관계 구축 완료</li>
</ol>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="2️⃣-grasp-패턴">2️⃣ GRASP 패턴?</h3>
<blockquote>
<p>일반적인 객체 책임 할당을 위한 소프트웨어 패턴. </p>
</blockquote>
<h4 id="👣-도메인-개념에서-출발하기">👣 도메인 개념에서 출발하기</h4>
<ul>
<li>설계를 시작할 때, 도메인에 대한 개략적인 모습을 그려보면 좋다.<ul>
<li>도메인에 존재하는 개념들을 책임 할당의 대상으로 사용하자. </li>
</ul>
</li>
<li>이때, 도메인을 완벽하게 정리하는 것에 집착하지 말자! <ul>
<li>단순히 출발점의 목적으로 활용할 것</li>
<li>책임을 <strong>할당받을 객체들의 종류와 관계에 대한 정보를 수집</strong>하는 선으로 족하다.</li>
</ul>
</li>
<li>올바른 도메인 모델?<ul>
<li>그런건 없다..! 단지 구현에 도움이 되는 실용적인 모델이 필요할 뿐이다. </li>
</ul>
</li>
</ul>
<br>

<h4 id="🦾-정보-전문가에게-책임-할당하기">🦾 정보 전문가에게 책임 할당하기</h4>
<ul>
<li>책임 수행에 필요한 메시지를 결정하자. <ul>
<li>메시지를 전송할 객체의 의도를 반영하여 아래 질문을 생각하며 메시지를 결정해야 한다. <ul>
<li>메시지를 전송할 객체는 뭘 원할까? </li>
<li>메시지를 수신할 객체는 누가 적당할까? </li>
</ul>
</li>
</ul>
</li>
<li>메시지 수신 객체는 <strong>캡슐화의 단위에서 결정</strong>해야 한다. <ul>
<li>INFORMATION EXPERT (정보 전문가) 패턴</li>
<li>책임을 수행할 정보를 알고 있는 객체에게 책임을 할당하자. </li>
</ul>
</li>
</ul>
<blockquote>
<p>정보 전문가 패턴 </p>
</blockquote>
<ul>
<li>객체가 자신이 소유한 정보와 관련된 작업을 수행하는 직관<ul>
<li>이때, 정보 전문가는 <strong>데이터를 반드시 저장할 필요가 없다</strong>는 점에 주의<ul>
<li>정보를 제공할 수 있는 다른 객체를 알거나, 필요한 정보를 직접 계산해서 제공할 수 있기 때문</li>
</ul>
</li>
</ul>
</li>
<li>따라서 <strong>상태와 행동을 함께 가지는 단위(=객체)를 책임 할당 관점에서 표현</strong>한다. </li>
</ul>
<br>

<h4 id="⛑-높은-응집도-낮은-결합도">⛑ 높은 응집도, 낮은 결합도</h4>
<ul>
<li>위 과정대로 했는데, 동일한 기능을 구현할 수 있는 여러 설계 선택지가 있다면?<ul>
<li>응집도를 높이고 결합도를 낮게 만들 수 있는 설계를 선택하자.</li>
</ul>
</li>
<li>높은 응집도 패턴 (HIGH COHESION)<ul>
<li>어떤 협력 관계를 만들어야 각 객체의 변경 책임을 분리할 수 있을까? </li>
</ul>
</li>
<li>낮은 결합도 패턴 (LOW COUPLING)<ul>
<li>어떤 협력 관계를 만들어야 설계 전체적으로 결합도를 추가하지 않을 수 있을까? </li>
</ul>
</li>
</ul>
<br>

<h4 id="⚒-창조자에게-객체-생성-책임-할당하기">⚒ 창조자에게 객체 생성 책임 할당하기</h4>
<ul>
<li>창조자 패턴 (CREATOR)<ul>
<li>객체 A를 생성할 때, 아래 조건을 최대한 많이 만족하는 B에게 책임을 할당하라<ul>
<li>B가 A 객체를 포함하거나 참조한다</li>
<li>B가 A 객체를 기록한다</li>
<li>B가 A 객체를 긴밀하게 사용한다</li>
<li>B가 A 객체를 초기화하는 데 필요한 데이터를 가지고 있다 (= B는 A에 대한 정보 전문가 )</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h2 id="구현을-통해-설계-검증하기">구현을 통해 설계 검증하기</h2>
<blockquote>
<p>설계가 협력과 책임을 잘 반영했는지 검증하기 위해서 코드를 작성하고 실행해봐야 한다. </p>
</blockquote>
<h3 id="1️⃣-클래스-응집도-판단하기">1️⃣ 클래스 응집도 판단하기</h3>
<blockquote>
<p>메세지가 객체를 선택해야 한다! </p>
</blockquote>
<ul>
<li>객체가 협력하는 객체의 내부 구현에 대해 지식이 없을 때, 각 객체의 깔끔한 캡슐화가 가능하다.</li>
<li>객체간 연결고리가 메시지 뿐이기 때문에, 메시지가 변경되지 않는 한 내부 구현은 영향을 받지 않기 때문</li>
</ul>
<ul>
<li>변경에 취약한 클래스가 존재하는가?<ul>
<li>변경에 취약한 클래스 : 코드를 수정해야 하는 이유가 하나 이상인 클래스 </li>
<li><strong>높은 응집도를 위해 변경의 이유에 따라 클래스를 분리</strong>해야 한다. <ul>
<li>따라서 설계 개선 작업은 변경의 이유가 여러개인 클래스가 있는지 확인하면서 시작하면 좋다. </li>
</ul>
</li>
<li>변경의 이유를 파악하는 방법<ul>
<li><strong>(1) 인스턴스 변수 초기화 시점 파악</strong>
: 클래스 속성이 서로 다른 시점에 초기화되거나 Or 일부만 초기화된다 = 응집도가 낮다
: 함께 초기화되는 속성을 기준으로 코드를 분리하자! </li>
<li><strong>(2) 메서드들이 인스턴스 변수를 사용하는 방식 파악</strong>
: 메서드가 객체의 모든 속성을 사용할 때 클래스 응집도가 높다.
: 속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리하자22! </li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="2️⃣-설계-개선하기">2️⃣ 설계 개선하기</h3>
<h4 id="⛑-타입-분리하기">⛑ 타입 분리하기</h4>
<ul>
<li><strong>독립적인 타입은 서로 다른 클래스로 분리</strong>하자<ul>
<li>자신의 인스턴스 변수를 모두 함께 초기화 가능</li>
<li>클래스에 있는 모든 메서드는 동일한 인스턴스 변수 그룹을 사용</li>
</ul>
</li>
</ul>
<blockquote>
<p>타입 분리로 인해 발생할 수 있는 문제들 🔨</p>
</blockquote>
<ul>
<li>특정 클래스 응집도는 높아져도, 변경과 캡슐화 관점에서 보면 전체 설계 품질은 나빠지는 케이스<ul>
<li>전체 설계의 결합도가 높아질 수 있다</li>
<li>새로운 기능을 추가하기 위해 여러 클래스에 접근해야 한다</li>
</ul>
</li>
</ul>
<br>

<h4 id="⛑-다형성을-통해-분리하기">⛑ 다형성을 통해 분리하기</h4>
<ul>
<li><strong>역할을 이용해 객체 타입을 추상화</strong>하자<ul>
<li>동일한 책임을 수행한다 = 동일한 역할을 수행한다. 따라서 역할에 대해 결합하게 의존성을 제한하자</li>
<li>역할을 대체할 클래스들 사이의 관계에 따라 추상화 방법을 골라보자 (Java)<ul>
<li>(1) 클래스들 사이서 구현을 공유해야 함 = 추상 클래스</li>
<li>(2) 구현 공유할 필요 없이 책임만 정의하면 됨 = 인터페이스</li>
</ul>
</li>
</ul>
</li>
<li>POLYMORPHISM (다형성) 패턴<ul>
<li>객체 타입에 따라 변하는 행동이 있다면? 타입을 분리하고, 변하는 행동을 각 타입의 책임으로 할당하자 </li>
<li>if-else가 아니라, 객체의 타입에 따라 변화를 컨트롤하는 방식</li>
</ul>
</li>
</ul>
<br>

<h4 id="⛑-변경으로부터-보호하기">⛑ 변경으로부터 보호하기</h4>
<ul>
<li>PROTECTED VARIATIONS (변경 보호) 패턴<ul>
<li>변경을 캡슐화하도록 책임을 할당하는 방식<ul>
<li>변화가 예상되는 불안정한 지점들을 클래스로 인터페이스를 이용해 변경을 캡슐화한다</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<p>캡슐화를 이용해 설계 결합도와 응집도 향상하기
(1) 하나의 클래스가 여러 타입의 행동을 구현하는 것 같다 = 다형성 패턴으로 책임 분산
(2) 예측 가능한 변경으로 인해 클래스들이 불안정해진다 = 변경 보호 패턴으로 인터페이스를 이용한 캡슐화</p>
</blockquote>
<br>

<h3 id="3️⃣-변경과-유연성">3️⃣ 변경과 유연성</h3>
<blockquote>
<p>클래스가 작고 한 가지 일만 수행할 때, 책임이 적절하게 분배되어 있을 때, 다른 클래스와 느슨하게 결합되어 있을 때 변경에 유연한 코드를 만들 수 있다.</p>
</blockquote>
<ul>
<li>개발자가 변경에 대비하는 두 가지 방법<ul>
<li>(1) 코드를 수정하기 쉽도록 최대한 단순하게 설계하기</li>
<li>(2) 코드를 수정하지 않고도 변경을 수용할 수 있게 유연하게 설계하기</li>
</ul>
</li>
<li><strong>유연성은 요소들 사이의 의존성에 의해 결정</strong>된다</li>
</ul>
<br>

<h2 id="책임-주도-설계의-대안">책임 주도 설계의 대안</h2>
<blockquote>
<p>적절한 책임을 찾는게 어렵다면, 일단 코드부터 짜고 이후 <strong>리팩토링</strong>을 통해 구조를 개선해보자. </p>
</blockquote>
<ul>
<li>리팩토링 (Refactoring)<ul>
<li>겉으로 보이는 동작은 바꾸지 않은 채 내부 구조를 개선(변경)하는 것</li>
</ul>
</li>
</ul>
<br>

<h3 id="1️⃣-메서드-응집도">1️⃣ 메서드 응집도</h3>
<ul>
<li>긴 메서드 = 몬스터 메서드 (monster method)<ul>
<li>문제<ul>
<li>코드의 전체적인 컨텍스트 파악이 오래 걸린다</li>
<li>코드 변경할 때 수정해야 할 부분 찾기가 어렵다</li>
<li>메서드 내부 일부 로직을 수정했을 때, 다른 로직이 영향을 받을 가능성이 크다</li>
<li>로직의 일부를 재사용하는 것이 불가능하다</li>
<li>코드 재사용을 위해 복붙하게 되어, 코드 중복이 발생하기 쉽다</li>
</ul>
</li>
<li>요약<ul>
<li>응집도가 낮아서 보기도 어렵고 쓰기도 어렵고 재활용도 어렵고 어쨌든 모든게 어려워진다! </li>
</ul>
</li>
<li>주석이 주렁주렁 필요하다? 높은 확률로 메서드 응집도가 낮은 경우! <ul>
<li>메서드를 작게 분해해서 응집도를 높이자. 작은 메서드는 일종의 주석과 같다</li>
</ul>
</li>
</ul>
</li>
<li>메서드를 작고 응집도 높은 메서드로 분리할 때, 각 메서드를 적절한 클래스로 이동 (책임 할당)하기가 더 쉬워진다.</li>
</ul>
<br>

<h4 id="2️⃣-자율적인-객체-만들기">2️⃣ 자율적인 객체 만들기</h4>
<ul>
<li>메서드가 사용하는 데이터를 가지고 있는 클래스로 메서드를 이동시키자<ul>
<li>자율적인 객체 = 데이터를 처리하기 위해 타 객체에게 의존할 필요가 없는 객체 = 내가 가진 데이터를 스스로 처리하는 객체 </li>
<li>메서드를 이동할 때는 캡슐화, 응집도, 결합도의 측면에서 해당 이동이 적절한지 판단한다. </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ch.4] 설계 품질과 트레이드 오프]]></title>
            <link>https://velog.io/@ej_shin/Ch.4-%EC%84%A4%EA%B3%84-%ED%92%88%EC%A7%88%EA%B3%BC-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%93%9C-%EC%98%A4%ED%94%84</link>
            <guid>https://velog.io/@ej_shin/Ch.4-%EC%84%A4%EA%B3%84-%ED%92%88%EC%A7%88%EA%B3%BC-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%93%9C-%EC%98%A4%ED%94%84</guid>
            <pubDate>Sat, 26 Nov 2022 18:17:42 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/683ba5d7-73c3-411a-bc09-7496b577684f/image.gif" alt=""></p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/7e49ee8c-b4d7-4065-a4e9-93e1c14e3e01/image.jpeg" alt="">
<a href="https://wikibook.co.kr/object/">오브젝트 : 코드로 이해하는 객체지향 설계 (조영호 저) </a>를 읽고 정리합니다. </p>
</blockquote>
<br>

<h2 id="품질-척도">품질 척도</h2>
<blockquote>
<p>유지보수성! 오늘의 기능을 수행하면서 내일의 변경을 수용할 수 있는지?</p>
</blockquote>
<h3 id="🌱-캡슐화">🌱 캡슐화</h3>
<ul>
<li>목적은 변경의 영향 통제! </li>
<li>객체 내부 구현을 외부로부터 감춰, 특정 변경의 파급 효과를 조절하게 하는 것.<ul>
<li>구현 : 변경될 가능성이 높은 부분</li>
<li>인터페이스 : 상대적으로 안정적인 부분 </li>
</ul>
</li>
<li>불안정한 구현이 아닌, <strong>안정적인 인터페이스에 의존</strong>하게 관계를 조절할 때 객체지향이 구현될 수 있다. </li>
</ul>
<h3 id="🔨-응집도와-결합도">🔨 응집도와 결합도</h3>
<ul>
<li>응집도 (⬆️)<ul>
<li>내부 요소들이 연관되어 있는 정도</li>
<li>객체 또는 클래스에 얼마나 관련 높은 책임을 할당했는가? 를 확인하는 척도</li>
</ul>
</li>
<li>결합도 (⬇️)<ul>
<li>다른 모듈에 대해 얼마나 많은 지식을 가지고 있는가. 즉 의존성의 정도</li>
<li>객체 또는 클래스가 협력에 필요한 적절한 수준의 관계만을 유지하고 있는가? 를 확인하는 척도 </li>
</ul>
</li>
<li>내 설계를 쉽게 변경할 수 없다면, <strong>구성 요소들의 응집도가 너무 낮지 않은지, 결합도가 너무 높지 않은지</strong>를 점검해 볼 타이밍..! 😇</li>
</ul>
<blockquote>
<p><strong>변경의 관점</strong>에서 응집도와 결합도를 살펴보자. </p>
</blockquote>
<ul>
<li>응집도 : 변경이 발생할 때 <strong>모듈 내부에서 얼마나 많이 변경</strong>해야 하니?</li>
<li>결합도 : 한 모듈이 변경되기 위해 <strong>다른 모듈을 얼마나 많이 변경</strong>해야 하니?</li>
</ul>
<br>

<h2 id="훌륭한-설계">훌륭한 설계?</h2>
<blockquote>
<p>훌륭한 설계란 합리적인 비용 안에서 변경을 수용할 수 있는 구조를 만드는 것! </p>
</blockquote>
<h3 id="😊-책임-중심-설계">😊 책임 중심 설계</h3>
<blockquote>
<p>객체는 협력하는 공동체의 일원이라는 관점으로, 객체 행동에 초점을 맞춘다. </p>
</blockquote>
<ul>
<li>설계는 변경에 유연하게 대응할 수 있는 시스템을 만든다는 목적을 가지고 진행된다. <ul>
<li>이 목적 아래서 모든 객체들은 서로에게 덜 영향받도록 최대한 자율적이어야 하며, 따라서 추상화되고 캡슐화된다. </li>
</ul>
</li>
</ul>
<br>

<h3 id="😡-상태-중심-설계">😡 상태 중심 설계</h3>
<blockquote>
<p>상태 = 데이터. 객체를 독립된 데이터 덩어리로 바라보는 관점 </p>
</blockquote>
<blockquote>
<p>캡슐화를 위반하고, 결합도가 높아지며, 응집도가 낮아져서 문제에요~ </p>
</blockquote>
<ul>
<li>객체에게 필요한 데이터가 무엇인가? 에 초점이 맞춰진다. </li>
<li>구현에 관한 세부사항이 객체 인터페이스에 강하게 결합된다. <ul>
<li>따라서 변경에 유연하게 대응할 수 없다. <ul>
<li>상태를 변경하면 인터페이스가 변해야하고, 인터페이스에 의존하는 모든 객체들을 바꿔야하니까! </li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="🤔-데이터-중심-설계가-갖는-문제점을-구체적으로-확인해보자">🤔 데이터 중심 설계가 갖는 문제점을 구체적으로 확인해보자!</h4>
<ul>
<li>1️⃣ 캡슐화 위반<ul>
<li>문맥이 아닌, 내부 데이터에 초점을 맞췄기 때문에 언제 상태값이 사용될 지 알 수 없다. <ul>
<li>따라서 발생 가능한 상황들을 커버하기 위해 모든 상태에 대해 접근자 메서드와 수정자 메서드를 두어야 한다. (= 추측에 의한 설계 전략)</li>
</ul>
</li>
<li>객체 내부에 접근하기 위해 사용하는 접근자와 수정자가, 오히려 <strong>객체 내부에 어떤 값이 있다는 사실을 퍼블릭 인터페이스에 드러내게</strong> 된다! <ul>
<li>따라서 내부 구현을 퍼블릭 인터페이스에 노출하면서 자연스럽게 캡슐화를 위반하고, 변경에 약해진다. </li>
</ul>
</li>
</ul>
</li>
<li>2️⃣ 높은 결합도<ul>
<li>전체 시스템을 <strong>하나의 거대한 의존성 덩어리</strong>로 만드는 지름길! <ul>
<li>캡슐화 위반 -&gt; 내부 구현의 노출 -&gt; 클라이언트가 구현에 강결합 -&gt; 뭐 하나 바꾸려면 전부 바꿔야함</li>
<li>여러 데이터 객체를 쓰는 제어 로직이 특정 객체에게 집중 -&gt; 특정 객체에게 전체 로직이 강결합 -&gt; 뭐 하나 바꾸려면 전부 바꿔야함22</li>
</ul>
</li>
</ul>
</li>
<li>3️⃣ 낮은 응집도<ul>
<li>단일 책임 원칙(SRP)의 위반 : 클래스는 단 하나의 변경 이유만 가져야 한다.<ul>
<li>변경의 이유가 다른 코드들을 하나의 모듈에 뭉쳐둠 -&gt; 변경과 아무 상관 없는 코드들이 변경에 영향을 받음</li>
<li>다른 모듈에 위치해야 할 책임의 일부가 엉뚱한 곳에 위치 -&gt; 하나의 요구사항 변경을 위해 여러 모듈 수정 필요 </li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="🥺-어떻게-고쳐쓸-순-없을까요">🥺 어떻게 고쳐쓸 순 없을까요?</h4>
<ul>
<li>1️⃣ 캡슐화를 지키자! <ul>
<li>특정 객체의 변경 주체를 외부에서 내부로 이동시키자!<ul>
<li>객체가 스스로를 책임지게 만들기<ul>
<li>객체가 어떤 데이터를 포함해야 하는지 &amp; 이 데이터에 대해 수행해야 하는 행동은 무엇인지 고려</li>
<li>= 객체 내부 상태를 저장하는 방식 &amp; 저장된 상태에 대해 호출할 수 있는 오퍼레이션의 집합</li>
</ul>
</li>
</ul>
</li>
<li>객체 구현도 내부로 이동시키자!<ul>
<li>내부 변경이 외부로 퍼져나가지 않게 만들기<ul>
<li>변할 수 있는 것은 어떤 것(속성이든, 뭐든 전부!)이라도 전부 감춰야 한다</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>2️⃣ 결합도를 낮추자! <ul>
<li>변경의 여파가 퍼지지 않도록, 위의 캡슐화 원칙을 통해 결합도를 낮추기</li>
</ul>
</li>
<li>3️⃣ 응집도를 높이자! <ul>
<li>바꿀 때 하나만 바꿔도 되게, 위의 캡슐화 원칙을 통해 응집도를 높이기 </li>
</ul>
</li>
</ul>
<h4 id="😮-그거-완전-책임-중심-설계-아니에요">😮 그거 완전.. 책임 중심 설계 아니에요?</h4>
<blockquote>
<p>맞아용! </p>
</blockquote>
<ul>
<li>데이터 중심 설계가 유연하지 못한 이유는 캡슐화를 위반했기 때문! <ul>
<li>객체의 상태는 결국 구현의 일부<ul>
<li>잘 변하는 불안정한 요소에 초점을 맞추고 설계했으니 당연히 변경에 취약해진 것</li>
</ul>
</li>
<li>상태 처리 오퍼레이션을 나중에 추가하느라 내부 지식이 인터페이스에 노출된다<ul>
<li>협력 문맥을 놓쳤기에 객체가 고립될 수 밖에 없다</li>
<li>협력이 구현에 종속되게 만들면 당연히 객체들은 변경에 휩쓸리게 된다.</li>
</ul>
</li>
</ul>
</li>
<li>객체지향 설계는 항상 <strong>객체 외부</strong>를 위주로 이루어져야 한다. <ul>
<li>객체가 어떻게 다른 객체와 협력하는지가 중요하지, 내부 상태를 관리하는 방법은 전체 설계에서 그닥 중요한 일이 아니다! </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ch.3] 역할, 책임, 협력]]></title>
            <link>https://velog.io/@ej_shin/Ch.3-%EC%97%AD%ED%95%A0-%EC%B1%85%EC%9E%84-%ED%98%91%EB%A0%A5</link>
            <guid>https://velog.io/@ej_shin/Ch.3-%EC%97%AD%ED%95%A0-%EC%B1%85%EC%9E%84-%ED%98%91%EB%A0%A5</guid>
            <pubDate>Sat, 26 Nov 2022 17:05:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/bd24ac18-40ae-4c7c-a58b-6014763a5ad1/image.gif" alt=""></p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/7e49ee8c-b4d7-4065-a4e9-93e1c14e3e01/image.jpeg" alt="">
<a href="https://wikibook.co.kr/object/">오브젝트 : 코드로 이해하는 객체지향 설계 (조영호 저) </a>를 읽고 정리합니다. </p>
</blockquote>
<br>

<h2 id="역할-책임-협력">역할, 책임, 협력</h2>
<blockquote>
<p>객체 = 협력이라는 연극 안에서 역할이라는 배역을 연기하는 배우 </p>
</blockquote>
<ul>
<li>객체지향의 본질은 협력하는 객체들의 공동체를 창조하는 것! <ul>
<li>기능 구현에 필요한 협력을 먼저 설계하고, 이후 클래스와 상속 등의 구현 메커니즘을 이용해 완성할 때 유연한 코드를 짤 수 있다. </li>
</ul>
</li>
<li>동일한 객체(배우)라도, 참여하는 협력(연극)에 따라 역할(배역)이 계속 달라진다. <ul>
<li>따라서 특정 협력(연극) 안에서, 필요한 객체의 특정 역할(배역) 이외의 부분은 감춰진다. </li>
</ul>
</li>
</ul>
<br>

<h3 id="1️⃣-협력">1️⃣ 협력</h3>
<blockquote>
<p>객체들이 어플리케이션의 기능을 구현하기 위해 수행하는 상호작용</p>
</blockquote>
<ul>
<li>객체지향 시스템은 자율적인 객체들의 공동체! 자기 일은 스스로 해야 한다. <ul>
<li>객체는 자율적이기 위해 필요한 정보와 정보에 기반한 행동만으로 구성되어야 한다 (캡슐화)</li>
<li>따라서 객체는 자신이 할 수 없는 일을 다른 객체에게 요청하며, 이를 협력이라고 한다. <ul>
<li>Ex. A객체는 자신이 할 수 없는 일을 B객체에게 요청(메시지 전송)하고, B객체는 필요한 메서드를 선택하여 실행해 요청에 응답한다. </li>
</ul>
</li>
</ul>
</li>
<li>협력은 객체 설계(= 객체에게 할당할 책임을 결정)에 필요한 문맥을 제공한다. <ul>
<li><strong>협력은 객체의 행동을 결정하고, 행동은 객체의 상태를 결정</strong>한다. <ul>
<li>객체는 협력에 필요한 행동을 보유할 때만 어플리케이션에 필요하기 때문!</li>
</ul>
</li>
</ul>
</li>
</ul>
<br> 

<h3 id="2️⃣-책임">2️⃣ 책임</h3>
<blockquote>
<p>객체가 협력에 참여하기 위해 수행하는 로직</p>
</blockquote>
<ul>
<li>책임은 객체가 유지해야 하는 정보와 수행할 수 이는 행동에 대해 개략적으로 서술한다. <ul>
<li>&#39;하는 것&#39; 책임 <ul>
<li>객체가 &#39;하는&#39; 책임을 수행하기 위해선 &#39;아는&#39; 책임이 필요하다. </li>
<li>Ex. 객체를 생성하거나, 계산을 수행하거나, 다른 객체에게 도움을 요청하거나... </li>
</ul>
</li>
<li>&#39;아는 것&#39; 책임<ul>
<li>객체는 자신이 맡은 책임을 수행하는데 필요한 정보를 알 책임이 있다. </li>
<li>객체는 자신이 할 수 없는 일을 도와줄 객체를 알고 있을 책임이 있다.</li>
<li>Ex. 사적인 정보에 대해 알거나, 관련된 객체에 대해 알거나, 내가 할 수 있는 걸 알거나...</li>
</ul>
</li>
</ul>
</li>
<li>객체가 할 수 있는 행동들을 종합적으로 서술한다는 점에서 메시지보다 추상적이고 큰 개념! <ul>
<li>따라서 <strong>객체에게 얼마나 적절한 책임을 할당</strong>하는지가 설계에서 굉장히 중요! </li>
</ul>
</li>
<li>보통 <strong>정보 전문가 패턴</strong>을 이용해 책임을 할당한다. <ul>
<li>정보 전문가 패턴 : 책임 수행에 필요한 정보를 가장 잘 알고있는 전문가에게 책임을 할당하는 것<ul>
<li>보통 일 시킬 때 잘 하는, 잘 아는 사람한테 시킨다. 똑같은 이유~</li>
<li>물론 응집도와 결합도의 관점에서, 다른 방식이 더 적절할 수도 있다. 고냥 보통은 이렇게 한다고~</li>
</ul>
</li>
<li><strong>협력이라는 문맥 안에서 더 작은 책임을 찾고, 그 책임의 적임자를 찾아 할당하는 과정을 반복하며 설계가 이루어진다</strong>. <ul>
<li>이 과정(책임 주도 설계)을 통해 퍼블릭 인터페이스를 구성할 수도 있다! </li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h4 id="📌-책임에-집중하는-책임-주도-설계">📌 책임에 집중하는 책임 주도 설계</h4>
<ul>
<li>책임이 협력에 참여할 객체를 결정하도록, 객체의 구현보다는 책임에 집중할 때 유연한 시스템이 된다. </li>
<li>단, 책임을 할당할 때는 아래를 고려한다.<ul>
<li>메세지가 객체를 결정한다.<ul>
<li>(1) 식별된 메세지를 기반으로 인터페이스를 구성하기 때문에, <strong>객체가 최소한의 인터페이스를 가질 수 있게 된다</strong>. </li>
<li>(2) 외부 객체가 요청하는 케이스를 고려할 수 있으므로, 메세지를 먼저 식별하면 무엇을 수행할지에 초점을 맞추는 <strong>추상적인 인터페이스</strong>를 가질 수 있게 된다. </li>
</ul>
</li>
<li>행동이 상태를 결정한다.<ul>
<li>다른 객체로부터 <strong>&#39;무엇을 얻어야 하는지&#39;</strong> 고민할 때, 즉 협력에 초점을 맞출 때 높은 응집도와 낮은 결합도를 구현할 수 있다. </li>
<li>협력에 적합한지를 판단하는 기준은 행동! 상태를 기준으로 설계할 때 내부 구현과 퍼블릭 인터페이스의 의존도가 높아진다. </li>
</ul>
</li>
</ul>
</li>
</ul>
<br>


<h3 id="3️⃣-역할">3️⃣ 역할</h3>
<blockquote>
<p>협력 안에서 특정 객체가 수행하는 책임들이 모인 것</p>
</blockquote>
<ul>
<li>책임의 할당은 (1) 적절한 역할이 뭔지 찾고 (2) 그 역할을 수행할 객체를 선택하는 과정으로 이루어진다. <ul>
<li>동일한 책임을 수행하는 역할을 기반으로 다수의 협력을 하나로 통합할 수 있다. <ul>
<li>즉, 역할을 이용해 <strong>불필요한 코드의 중복을 방지</strong>하고, <strong>유연한 협력을 구현</strong>할 수 있다.</li>
</ul>
</li>
</ul>
</li>
<li>설계 초반에는 책임과 협력을 탐색하며 큰 그림을 그리는 것에 집중하자. <ul>
<li>일단 구체적인 객체들로 시작하고, 이후 유사한 협력들을 합치다보면 역할을 나눌 수 있을 것! <ul>
<li>역할의 기준<ul>
<li>협력에 적합한 책임을 수행하는 대상이 한 종류 -&gt; 객체</li>
<li>반대로 대상이 여러 종류가 될 수 있다면? -&gt; 역할</li>
</ul>
</li>
</ul>
</li>
<li>역할을 중심으로 역할 모델링을 시도할 때, <strong>설계의 구성 요소를 추상화</strong>할 수 있다.<ul>
<li>역할은 모양이나 구조가 아닌, 시스템 문맥 안에서 무엇을 하는지에 의해 정의한다. </li>
</ul>
</li>
</ul>
</li>
<li>역할을 슬롯이라는 관점으로 이해하자! <ul>
<li>동일한 책임을 수행하는 객체는 동일한 역할을 수행하기에 서로 대체할 수 있다. </li>
<li>따라서 역할이라는 추상화를 이용할 때, 새로운 기능을 구현할 때 유연하게 처리할 수 있다. </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ch.2] 객체지향 프로그래밍]]></title>
            <link>https://velog.io/@ej_shin/Ch.2-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@ej_shin/Ch.2-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Sat, 19 Nov 2022 11:08:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/30ffc133-718d-4368-b115-4c4618eb4bf9/image.gif" alt=""></p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/7e49ee8c-b4d7-4065-a4e9-93e1c14e3e01/image.jpeg" alt="">
<a href="https://wikibook.co.kr/object/">오브젝트 : 코드로 이해하는 객체지향 설계 (조영호 저) </a>를 읽고 정리합니다. </p>
</blockquote>
<br>

<h2 id="객체지향-프로그래밍">객체지향 프로그래밍</h2>
<blockquote>
<p>객체지향의 핵심은 애플리케이션 기능 구현을 위해, 객체들이 협력하며 상호작용하는 것! </p>
</blockquote>
<ul>
<li>프로그래밍 관점에 치우쳐서 객체지향을 바라보지말자. <ul>
<li>프로그래밍 관점에서 생각해보면, 우리는 기능을 만들기 위해 클래스를 짜고, 클래스 안에 메소드와 변수들을 채워넣어야한다. </li>
<li>하지만? 우리는 클래스들이 서로 상호작용한다고 생각하는게 아니라, <strong>객체들이 서로 상호작용</strong>한다고 생각해야한다. </li>
</ul>
</li>
</ul>
<h4 id="🤷-그럼-뭘-생각하며-객체지향을-구현해요">🤷 그럼 뭘 생각하며 객체지향을 구현해요?</h4>
<ol>
<li><p>어떤 객체가 필요한가?
: 기능 구현을 위해 어떤 클래스가 필요한지를 고민하는게 아니라, 어떤 객체가 필요한지 고민해야 한다. </p>
</li>
<li><p>객체는 기능을 구현하기 위한 협력체의 일원! 
: 기능 구현을 위해 다른 객체에게 도움을 주거나 의존하고 있는지 생각해야 한다. </p>
</li>
</ol>
<br>


<h4 id="🤷♀️-예">🤷‍♀️ 예?</h4>
<blockquote>
<p>무슨 소린지 모르겠다면, 도메인을 활용해 이해해보자.</p>
</blockquote>
<ul>
<li><p>도메인 (domain) : 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야</p>
<ul>
<li>우리가 구현하려는 기능 = 문제를 해결하려는 수단 = 도메인<ul>
<li>Ex. 문제 : 영화를 보고 싶은데, 보려면 티켓이 필요해! </li>
<li>Ex. 수단(도메인) : 티켓 예매 </li>
</ul>
</li>
</ul>
</li>
<li><p>요구사항부터 구현까지 전부 객체의 관점으로 바라보자</p>
<ul>
<li>도메인을 구성하는 개념들을 나열하고, 그 개념들을 객체와 클래스로 재구성하자.<ul>
<li>Ex. 티켓 예매 도메인을 구현하려면 어떤 상태와 행동이 필요할까?
: 영화, 예매, 상영, 영화별 적용되는 할인 정책, 할인 정책1, 정책 2...</li>
<li>Ex. 이 상태와 행동들 중 유사한 친구들을 묶어서 하나의 개념으로 만들자. 
: 할인정책1과 할인정책2는 &gt;할인 정책&lt; 이라는 점에서 공통점을 가지니까, 하나로 묶자.</li>
<li>Ex. 이렇게 만든 개념을 클래스로 구현하자. 
: 할인정책은 할인정책1이라는 객체와 할인정책2라는 객체를 가질 수 있는 클래스로 정리된다. </li>
</ul>
</li>
<li>즉, 클래스의 구조가 도메인의 구조와 유사할 때 프로그램의 구조를 이해하고 예상하기 쉽다. </li>
</ul>
<br>

</li>
</ul>
<h2 id="오케-그럼-클래스는-어떻게-구현해야-하나요">오케. 그럼 클래스는 어떻게 구현해야 하나요?</h2>
<blockquote>
<p>좋은 코드는? 변경하기 쉬운 코드. 변경하기 쉬운 코드는? 이해하기 쉬운 코드!</p>
</blockquote>
<h4 id="🧚-우리는-객체가-자율적인-존재라고-약속했어요">🧚 우리는 객체가 자율적인 존재라고 약속했어요!</h4>
<ul>
<li><p>객체가 자율적이고, 외부에 덜 의존적이게 되려면 <strong>외부 간섭을 최소화</strong>해야 한다. </p>
<ul>
<li>나는 떡볶이 먹고 싶을 때 떡볶이 먹을거고, 만두 먹고 싶으면 만두 먹을건데 이상한 애가 찾아와서 아냐 넌 만두를 먹으려면 단무지가 필요한 애야~~ 하면 성질나니까..!</li>
</ul>
</li>
<li><p>이를 위해 객체는 <strong>상태와 행동을 객체 내부로 묶고 (= 캡슐화)</strong>, <strong>외부에서의 접근은 허가된 친구만</strong> 가능하도록 제어한다 (= 접근 제어).</p>
<ul>
<li>인터페이스와 구현의 분리 원칙<ul>
<li>퍼블릭 인터페이스 (public interface) : 외부에서 접근 가능</li>
<li>구현 (implementation) : 외부에서 접근 불가능, 내부에서만 접근 가능 </li>
</ul>
</li>
</ul>
<br>

</li>
</ul>
<h4 id="🤨-객체-접근-제어가-자율성에-어떻게-영향을-미치는지-잘-모르겠어요">🤨 객체 접근 제어가 자율성에 어떻게 영향을 미치는지 잘 모르겠어요.</h4>
<ul>
<li>중요한건 변경을 관리하는 것! <ul>
<li>프로그래머를 두 종류로 구분해보자. <ul>
<li>클래스 작성자 : 새로운 데이터 타입을 프로그램에 추가한다</li>
<li>클라이언트 프로그래머 : 클래스 작성자가 만든 데이터 타입을 사용한다</li>
<li>클래스 작성자 A씨는 내가 신나게 짠 코드가 클라이언트 B씨에게 어떤 영향을 줄 지 걱정하고, 클라이언트 B씨는 빠르게 기능을 만들고 싶은데 A씨가 코드를 갑자기 바꿀까봐 걱정한다. </li>
</ul>
</li>
<li>하지만 A씨의 코드 핵심은 안에 숨겨두고, 이 코드에 접근하는 간접적인 방법만 B씨에게 알려준다면?<ul>
<li>A씨는 내부 로직만 변경하면 되고, B씨는 내부 로직에 무관하게 접근 통로만 신경쓰면 된다! </li>
</ul>
</li>
</ul>
</li>
<li>우린 이걸 <strong>구현 은닉 (implementation hiding)</strong> 이라고 부르기로 했어요<ul>
<li>정리 : 객체로의 접근을 제어해 사용자 영향 고려 없이 내부 구현을 변경 가능하게 만들 수 있다.</li>
</ul>
</li>
</ul>
 <br>

<h4 id="😯-근데-객체는-협력해야-한다고-하지-않으셨나요">😯 근데.. 객체는 협력해야 한다고 하지 않으셨나요?</h4>
<blockquote>
<p>B씨가 A씨의 코드에 접근하는 간접적인 방식 = 객체 상호 작용 = 메시지를 주고 받는 행위</p>
</blockquote>
<ul>
<li><p>객체는 다른 객체와 협력할 때, 협력을 요청하는 메시지를 보낸다.</p>
<ul>
<li>주의할 점은, <strong>메시지 != 문제의 해결</strong> 이라는 점이다.<ul>
<li>문제의 해결은 메시지를 받은 객체가 메서드를 통해 스스로 해결한다. </li>
<li>메시지를 보낸 객체는 메시지를 받은 객체가 문제를 어떻게 해결하는지 전혀 알 수 없다. </li>
</ul>
</li>
</ul>
<br>

</li>
</ul>
<h2 id="근데-우리-아까-비슷한-친구들을-묶어놓지-않았나요">근데 우리 아까 비슷한 친구들을 묶어놓지 않았나요?</h2>
<blockquote>
<p>메시지를 받은 객체가 어떤 친구로 응답해야 하는지 어떻게 알 수 있을까? </p>
</blockquote>
<h4 id="🐳-상속은-몬가요">🐳 상속은 몬가요..?</h4>
<ul>
<li>기존 클래스를 기반으로 새로운 클래스를 만드는 행위 <ul>
<li>서로 공통점이 있지만, 차이점도 있는 클래스를 쉽게 추가할 수 있다. (= 차이에 의한 프로그래밍)</li>
</ul>
</li>
<li><strong>그렇다고 목적이 코드 재사용은 아니에요~</strong> <ul>
<li>부모의 인터페이스를 자식이 물려받을 수 있게 만드는게 중요!<ul>
<li>즉 구현이 아닌 인터페이스 상속이 객체지향적으로 가치있는 상속</li>
</ul>
</li>
<li>외부 객체가 봤을 때, <strong>자식과 부모를 동일한 타입이라고 생각하게 만드는게</strong> 주 목적이다.</li>
</ul>
</li>
<li>템플릿 메서드 (Template Method) 패턴<ul>
<li>부모는 기본적인 알고리즘 흐름을 구현하고, 중간에 필요한 처리는 자식에게 위임하는 패턴</li>
</ul>
</li>
<li><strong>코드 (클래스) 의존성과 실행 시점 (객체) 의존성이 다를 때 확장 가능하다.</strong><ul>
<li>객체는 내가 어떤 클래스의 인스턴스와 협력하는지가 아니라, 내가 협력하는 친구가 내 메시지를 수신할 수 있는지 아닌지가 중요하다. </li>
<li>따라서 객체가 소통할 수 있는 창구를 부모에게 터두고 처리 자체는 담당 자식에게 맡기자. <ul>
<li>상속을 통해 자식에게 부모의 인터페이스가 존재하기에 자식은 부모를 대신(업캐스팅)할 수 있다. </li>
</ul>
</li>
<li>단, 이 경우 기능은 유연해져도 코드 가독성은 떨어질 수 있다 ^.ㅠ </li>
</ul>
</li>
</ul>
<br>

<h4 id="🐳-다형성은요">🐳 다형성은요?</h4>
<ul>
<li><strong>동일한 메시지를 전송했을 때, 메시지 수신 객체 클래스에 따라 실행되는 메서드가 달라지는 것</strong><ul>
<li>이를 위해 협력에 참여하는 객체들은 모두 같은 메시지를 이해할 수 있어야 한다.</li>
<li>Ex. 부모가 받을 수 있는 동일한 메시지를 전송했을 때, 각기 다른 자식이 메시지를 처리한 경우</li>
</ul>
</li>
<li>메시지에 응답할 (= 실행될) 메서드를 컴파일 시점이 아닌 실행 시점에 정한다.<ul>
<li>메시지와 메서드를 실행 시점에 바인딩한다 = 지연 바인딩 = 동적 바인딩</li>
</ul>
</li>
<li>자식 클래스들이 순수하게 인터페이스만 공유하는 경우는?<ul>
<li>마찬가지로 동일한 인터페이스를 공유하면서, 부모를 대신해 사용될 수 있기에 업캐스팅이 적용되며 다형적!</li>
</ul>
</li>
</ul>
<blockquote>
<p>즉 상속과 다형성에는 추상화가 깔려있다. </p>
</blockquote>
<br>


<h2 id="추상화">추상화</h2>
<blockquote>
<p>최대한 인터페이스에 초점을 맞추고, 자식 클래스에게 결정권을 일임하는 것</p>
</blockquote>
<br>

<h4 id="📌-추상화를-통해-요구사항-정책을-높은-수준에서-서술할-수-있다">📌 추상화를 통해 요구사항 정책을 높은 수준에서 서술할 수 있다.</h4>
<blockquote>
<p>세부적인 내용을 무시한 채 상위 정책을 쉽고 간단하게 표현할 수 있다는 의미</p>
</blockquote>
<ul>
<li>자식 하나하나를 언급하는게 아니라, 인터페이스 수준에서 설명할 수 있으니까! <ul>
<li>Ex. 할인 정책 1, 할인 정책 2로 구분하지 않고 전체 할인 정책으로 구분</li>
</ul>
</li>
<li>추상화를 통해 애플리케이션의 기본 협력 흐름을 기술할 수 있다 (= 상위 정책 기술)<ul>
<li>디자인 패턴, 프레임워크 등 재사용 가능한 설계들은 모두 위와 같은 메커니즘을 활용한다. </li>
</ul>
</li>
</ul>
<br>

<h4 id="📌-추상화를-통해-유연하게-설계할-수-있다">📌 추상화를 통해 유연하게 설계할 수 있다.</h4>
<blockquote>
<p>책임의 위치를 정하기 위해 조건문을 사용하기보다는, 예외 케이스를 최소화하고 일관성을 유지해라. </p>
</blockquote>
<ul>
<li>설계는 포괄적으로 하고, 세부 사항은 자식이 처리하게 결정 위임 <ul>
<li>기존 구조를 수정하지 않으면서 새로운 기능을 쉽게 추가 &amp; 확장할 수 있다. </li>
</ul>
</li>
<li>컨텍스트 독립성 (context independency)<ul>
<li>설계가 구체적인 상황에 결합되지 않아, 어떤 클래스와도 협력이 가능하다. </li>
</ul>
</li>
</ul>
<br>


<h4 id="📌-트레이드-오프">📌 트레이드 오프</h4>
<ul>
<li>구현과 관련된 모든 것들이 트레이드 오프의 대상이 될 수 있다. <ul>
<li>Ex. 이상적으로는 인터페이스를 활용해 책임을 명확하게 하는게 좋지만, 또 현실적으로 생각했을 때 아주 작고 단순한 기능 하나만을 위해 인터페이스를 추가하는건 과할 수 있다. </li>
</ul>
</li>
<li>따라서 모든 코드를 합당한 이유를 기반으로 설계하고 작성해야 한다. </li>
</ul>
<br>


<h2 id="코드-재사용">코드 재사용</h2>
<blockquote>
<p>코드를 재사용할 땐 상속 말고 합성을 쓰기로 약속해요~ </p>
</blockquote>
<ul>
<li>합성 : 다른 객체의 인스턴스를 자신의 인스턴스 변수로 포함해서 재사용하는 방법</li>
</ul>
<br>

<h4 id="🔨-상속을-이용한-코드-재사용의-문제점">🔨 상속을 이용한 코드 재사용의 문제점</h4>
<blockquote>
<p>클래스를 통한 강결합 </p>
</blockquote>
<ul>
<li>캡슐화 위반<ul>
<li>부모 클래스의 구현이 자식 클래스에게 노출됨</li>
<li>즉, 자식이 부모에게 강하게 결합되고, 부모의 변경에 따라야 할 가능성이 생겨 코드 변경하기 어려워진다</li>
</ul>
</li>
<li>설계를 유연하지 않게 만듬<ul>
<li>부모 클래스와 자식 클래스의 관계를 컴파일 시점에 결정한다</li>
<li>따라서 실행 시점에 객체 종류를 변경할 수 없다</li>
</ul>
</li>
</ul>
<br>

<h4 id="🔨-합성을-이용한-코드-재사용의-장점">🔨 합성을 이용한 코드 재사용의 장점</h4>
<blockquote>
<p>인터페이스를 통한 약결합</p>
</blockquote>
<ul>
<li>인터페이스에 정의된 메시지를 통해서만 코드를 재사용할 수 있다<ul>
<li>메서드를 외부에 제공한다는 것만 알고, 내부 구현에 대해서는 전혀 모르는 상태!</li>
<li>즉, 상속과 달리 인터페이스를 통해 약하게 결합된다. </li>
</ul>
</li>
<li>따라서 구현을 효과적으로 캡슐화할 수 있고, 변경시 의존하는 인터페이스만 교체하면 되어 변경이 쉽다. </li>
</ul>
<br>

<h4 id="⚒-그렇다고-상속을-쓰지-말라는건-아니고">⚒ 그렇다고 상속을 쓰지 말라는건 아니고..!</h4>
<ul>
<li>코드를 재사용할 때는 : 합성</li>
<li>다형성을 위해 인터페이스를 재사용할 때는 : 상속과 합성을 함께 조합하기</li>
</ul>
<blockquote>
<p>객체지향의 핵심은 객체들의 협력과 상호작용이 적절하게 이루어져야 한다는 것! </p>
</blockquote>
<ul>
<li>적절한 협력을 식별하고, 적절한 객체에게 협력에 필요한 역할을 할당하자</li>
<li>프로그래밍 관점에 치우치는게 아니라, 객체를 지향하는 관점에서 설계해야한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ch.1] 객체, 설계 ]]></title>
            <link>https://velog.io/@ej_shin/Ch.1-%EA%B0%9D%EC%B2%B4-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@ej_shin/Ch.1-%EA%B0%9D%EC%B2%B4-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Mon, 14 Nov 2022 14:44:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/eeafa900-27e3-4e9f-aca3-5c8c3d253101/image.gif" alt=""></p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/7e49ee8c-b4d7-4065-a4e9-93e1c14e3e01/image.jpeg" alt="">
<a href="https://wikibook.co.kr/object/">오브젝트 : 코드로 이해하는 객체지향 설계 (조영호 저) </a>를 읽고 정리합니다. </p>
</blockquote>
<br>

<h1 id="객체와-설계">객체와 설계</h1>
<blockquote>
<p>소프트웨어 설계와 유지보수에 중점을 두기 위해서, 이론이 아닌 실무(실제 코드)에 집중해야 한다. </p>
</blockquote>
<h3 id="소프트웨어-모듈이-가져야-하는-세-가지-기능">소프트웨어 모듈이 가져야 하는 세 가지 기능</h3>
<ol>
<li>실행 중 제대로 동작할 것</li>
<li>변경을 위해 존재할 것 (변경 용이성)</li>
<li>코드를 읽는 사람과 의사소통할 수 있을 것 (이해 용이)</li>
</ol>
<br>

<h3 id="단순히-동작하는-것에-만족하면-안되는-이유">단순히 동작하는 것에 만족하면 안되는 이유</h3>
<blockquote>
<p>프로그램은 오늘 기능을 완성하는 동시에, 내일 쉽게 변경되어야 한다. </p>
</blockquote>
<h4 id="왜-변경이-쉬워야할까">왜 변경이 쉬워야할까?</h4>
<ul>
<li>요구사항이 항상 변경되기 때문<ul>
<li>유저의 의견으로, 기획자의 요청으로, 리소스의 한계로... 소프트웨어는 타의적으로든 자의적으로든 <em>언제든지 변경될 수 있다</em>. </li>
</ul>
</li>
<li>코드 변경과 버그 변경 가능성은 떼어놓을 수 없다<ul>
<li>요구사항의 변경은 코드의 변경을, 코드의 변경은 <em>버그 발생 가능성을 높인다</em>. </li>
<li>변경이 어려울 수록 기구현된 코드를 많이 고쳐야하고, 당연히 그만큼 기존 모듈 또는 신규 모듈에서 버그가 발생할 가능성이 높아진다. </li>
</ul>
</li>
</ul>
<br>

<h3 id="변경이-쉬운-코드란">변경이 쉬운 코드란?</h3>
<ul>
<li>변경이 쉬운 코드 = 변경에 유연하게 대응할 수 있는 코드 = 이해하기 쉬운 코드 <ul>
<li>이해하기 쉬워야 보고 고치기 쉬우니까! </li>
<li>이해하기 쉬운 코드 = 우리 예상에서 크게 벗어나지 않는 코드 <ul>
<li>척보고 무슨 기능인지 딱 연상이 되어야 이해하기 쉽지, 가나다라 하다가 갑자기 efg 나오면 갑자기? 싶어진다. 그런 맥락이다. </li>
</ul>
</li>
</ul>
</li>
<li>근데 코드 (객체)들이 하나만 있나? <ul>
<li>기능을 구현하기 위해 여러 객체들이 필요하고, 기능을 수정하기 위해 여러 객체들을 관리해야 한다. </li>
</ul>
</li>
<li>결국 <strong>이 객체 하나만 보고 이 객체의 역할이 뭔지 알 수 있어야</strong> 예상을 벗어나지 않는 이해하기 쉬운 코드인거고, 변경이 쉬운 코드인 것! </li>
</ul>
<blockquote>
<p>어? 이거 어디서 많이 들어본 소리 아니냐? </p>
</blockquote>
<h4 id="-✨-우린-이걸-응집도가-높다고-표현하기로-했어요-✨-">~ ✨ 우린 이걸 응집도가 높다고 표현하기로 했어요 ✨ ~</h4>
<ul>
<li>응집도 (cohesion) : 객체 내부 로직이 하나의 목표를 바라보고 있는가? </li>
<li>결합도 (coupling) : 객체가 다른 객체와 얼마나 연관성이 있는가? <ul>
<li><del>대학생때.. 교수님이 커플 보면 보기 싫으니까 커플링을 낮춰야 한다고 설명해주신 적이 있다. 너무 충격적인 드립이라 평생 못 잊을 듯</del></li>
</ul>
</li>
</ul>
<blockquote>
<h4 id="응집도를-높이고-결합도를-낮춰야-하는-이유">응집도를 높이고 결합도를 낮춰야 하는 이유</h4>
</blockquote>
<ul>
<li>응집도가 낮고 결합도가 높으면 객체 역할을 알아내기 어렵다. <ul>
<li>객체 역할을 알기 위해 여러 객체를 참조해야하니까!</li>
</ul>
</li>
<li>또, 객체를 변경하는 것도 어려워진다.<ul>
<li>한 객체를 변경하기 위해 관련있는 여러 객체들을 전부 변경해야하니까</li>
</ul>
</li>
</ul>
<h4 id="-✨-우린-이걸-의존성이-낮다고-표현하기로-했어요-✨-">~ ✨ 우린 이걸 의존성이 낮다고 표현하기로 했어요 ✨ ~</h4>
<ul>
<li>즉, 객체 사이의 의존성을 낮추고, 변경이 용이한 설계를 만드는게 최종 목표! </li>
</ul>
<br>

<h3 id="의존성은-어떻게-낮추죠">의존성은 어떻게 낮추죠?</h3>
<ul>
<li>객체 의존성이 높아지는 이유<ul>
<li>내가 할 일을 쟤가 하고 있으니까, 내가 뭘 할 때마다 쟤의 협조가 필요해지는 것. </li>
</ul>
</li>
<li>Ex. 밥을 먹는데 반찬을 스스로 집을 수 없고 누군가 떠먹여줘야 하는 상황을 생각해보자. <ul>
<li>밥을 국에도 말아먹고 싶고, 반찬 올려서 먹고도 싶고, 이것저것 넣고 볶아도 먹고 싶은데 그때마다 남한테 허락을 받아야 한다면? </li>
<li>오늘은 콩밥말고 쌀밥먹고 싶은데 내게는 선택권이 없다면? </li>
</ul>
</li>
</ul>
<blockquote>
<p>아! 객체가 스스로 처리할 수 있게 자율적인 친구로 만들어주면 되겠구나! </p>
</blockquote>
<ul>
<li>뭔가를 수행할 때, 다른 객체와 엮여서 처리하는게 아니라 객체가 스스로 직접 셀프로 움직이게 해주면 된다. <ul>
<li><strong>객체 내부의 세부적인 사항을 스스로 수행</strong>하게 만들기 위해, 외부에서 접근할 수 없게 강제하자! </li>
</ul>
</li>
</ul>
<h4 id="-✨-우린-이걸-캡슐화라고-표현하기로-했어요-✨-">~ ✨ 우린 이걸 캡슐화라고 표현하기로 했어요 ✨ ~</h4>
<ul>
<li>개념적, 물리적으로 객체 내부의 세부적인 사항을 감추고 객체 내부로의 접근을 제한하는 행위 </li>
<li>외부 객체는 객체의 인터페이스에만 의존하고, 실제 기능의 구현은 해당 객체만 가능하도록 만들자. <ul>
<li>즉, 객체를 인터페이스와 구현으로 나누고 인터페이스만을 공개하는게 중요! <ul>
<li>결국 객체는 나와 연관있는 작업만 수행하고, 연관 없는 작업은 다른 객체에게 위임하게 된다. </li>
</ul>
</li>
</ul>
</li>
<li>객체를 모두 사람이라고 생각하자. (= 의인화)<ul>
<li>객체가 쌀밥이어도, 떡볶이여도, 얘네는 다 살아있고 스스로의 의지로 움직이는 애들이라고 생각하고 책임을 넘기자. </li>
</ul>
</li>
</ul>
<h4 id="이제-각-객체들이-스스로를-책임지게-되었습니다-😮">이제 각 객체들이 스스로를 책임지게 되었습니다! 😮</h4>
<ul>
<li>어느 한 대장 객체가 다른 객체들을 모두 책임지는게 아니라, <strong>모든 객체들에게 책임이 적절히 분배</strong>되었고, <strong>각 객체들은 자율적으로 스스로의 책임을 수행</strong>한다. </li>
<li>데이터와 데이터를 사용하는 프로세스들이 동일한 객체 안에 위치해서 객체 응집도가 높아진 상태!</li>
</ul>
<br>

<h3 id="그럼-이제-객체들이-완전히-독립적인-건가요">그럼 이제 객체들이 완전히 독립적인 건가요?</h3>
<blockquote>
<p>아니용! </p>
</blockquote>
<h4 id="트레이드-오프-trade-off">트레이드 오프 (trade-off)</h4>
<ul>
<li>어떤 기능을 설계하는 방법은 한 가지 이상일 수 있다. </li>
<li>객체 자율성을 지키기 위해 전체 설계 관점에서 타 객체와의 결합도가 높아질 수도 있다. </li>
</ul>
<h4 id="왜-이런-일이-발생한거죠">왜 이런 일이 발생한거죠?</h4>
<ul>
<li>한 어플리케이션을 만들기 위해 여러 객체들이 서로 협력해야 하고, 이를 위해 객체 사이에 메시지를 주고받게 된다. <ul>
<li>이 메시지를 주고 받기 위해 필요한 최소한의 지식이 의존성을 만들게 된다. <ul>
<li>아무리 인터페이스에만 접근하게 설계하게 만들어 의존성을 낮춰도, 결국 타 객체의 인터페이스 &lt; 에는 접근하게 되는 것. </li>
</ul>
</li>
</ul>
</li>
<li>좋은 설계의 목적은 <strong>의존성을 없애는게 아니라, 의존성을 낮추는 것</strong>이다. <ul>
<li>최대한 한 번에 하나의 클래스만 변경할 수 있도록, 최대한 데이터와 프로세스가 동일한 모듈 내부에 위치할 수 있도록! </li>
</ul>
</li>
</ul>
<br>

<h3 id="그럼-객체-지향-설계의-최종-목표는-뭔가요">그럼 객체 지향 설계의 최종 목표는 뭔가요?</h3>
<blockquote>
<p>협력하는 객체 사이의 의존성을 적절하게 관리하는 설계를 구현하자! </p>
</blockquote>
<ul>
<li>불필요한 의존 관계 없이, 객체들이 적절하게 상호작용하고 적당한 수준의 변경을 구현 가능하게 할 때 유연하게 설계되었다고 평가할 수 있을 것이다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring/DB] @Transactional의 전파 속성과 고립 레벨]]></title>
            <link>https://velog.io/@ej_shin/SpringDB-Transactional%EC%9D%98-%EC%A0%84%ED%8C%8C-%EC%86%8D%EC%84%B1%EA%B3%BC-%EA%B3%A0%EB%A6%BD-%EB%A0%88%EB%B2%A8</link>
            <guid>https://velog.io/@ej_shin/SpringDB-Transactional%EC%9D%98-%EC%A0%84%ED%8C%8C-%EC%86%8D%EC%84%B1%EA%B3%BC-%EA%B3%A0%EB%A6%BD-%EB%A0%88%EB%B2%A8</guid>
            <pubDate>Mon, 31 Oct 2022 11:14:05 GMT</pubDate>
            <description><![CDATA[<h1 id="시작-전에-읽어두기">시작 전에 읽어두기</h1>
<ul>
<li>트랜잭션 <ul>
<li>DB 상태를 변화시키기 위해 수행하는 작업 단위 </li>
<li>즉, 유저(서비스)가 정의한 쿼리 묶음 <ul>
<li>Ex. 로그인 : 중복 ID 확인 (select)와 신규 계정 생성 (insert)</li>
</ul>
</li>
</ul>
</li>
<li>트랜잭션의 ACID <ul>
<li>원자성 (Atomicity) : 트랜잭션은 완전히 성공하거나 완전히 실패하거나 둘 중 하나의 상태를 가져야 한다. </li>
<li>일관성 (Consistency) : 트랜잭션의 작업 처리 결과는 항상 일관적이어야 한다. <ul>
<li>Ex. 트랜잭션 진행 중 DB가 변경되어도, 처음 참조한 DB로 트랜잭션을 진행해야 한다. </li>
</ul>
</li>
<li>격리성 (Isolation) : 둘 이상의 트랜잭션이 실행될 때, 그 어떤 트랜잭션도 다른 트랜잭션의 연산을 침범할 수 없다. </li>
<li>지속성 (Durability) : 트랜잭션이 성공적으로 완료되었을 때, 해당 트랜잭션의 결과는 DB에 영구적으로 반영되어야 한다. </li>
</ul>
</li>
</ul>
<br>

<h1 id="전파-속성-propagation">전파 속성 (Propagation)</h1>
<blockquote>
<p>spring boot @Transactional에서는 Propagation.REQUIRED가 default로 세팅되어 있다</p>
</blockquote>
<h3 id="전파-속성이란">전파 속성이란?</h3>
<ul>
<li>SQL단에서 제공하는게 아니라 spring 단에서 제공하는 속성 <ul>
<li>Spring은 @Transactional (선언적 트랜잭션)을 이용해 여러 트랜잭션을 묶어 하나의 큰 트랜잭션 경계를 만들 수 있다. </li>
<li>따라서 기존 트랜잭션 작업 중, 추가 트랜잭션 작업을 진행해야 할 때 <strong>해당 작업을 어떻게 진행할지 흐름을 결정</strong>해야 한다. <ul>
<li>Ex. 추가 트랜잭션을 기존 트랜잭션의 일부로 볼 것인지, 아니면 별도로 볼 것인지, 아님 에러를 발생시키던지..! </li>
</ul>
</li>
<li>즉, 이 추가 트랜잭션의 흐름을 결정하는 옵션이 전파 속성! </li>
</ul>
</li>
</ul>
<ul>
<li><p>요런 느낌  </p>
<pre><code>  @Transactional
  public void parent() {
    deviceRepository.save(new Device());
    child();
  }

  @Transactional
  public void child() {
    deviceRepository.save(new Device());
  }</code></pre></li>
</ul>
<h3 id="spring-boot-transactional-옵션-정리">Spring Boot @Transactional 옵션 정리</h3>
<blockquote>
<ul>
<li>부모 트랜잭션 : 호출한 메소드의 트랜잭션</li>
<li>자식 트랜잭션 : 호출당한 메소드의 트랜잭션 </li>
</ul>
</blockquote>
<ul>
<li>Propagation.REQUIRED<ul>
<li>부모 트랜잭션이 존재한다면, 자식 트랜잭션은 부모에게 포함된다. <ul>
<li>즉, 자식 트랜잭션이 실패하면 부모도 롤백된다 </li>
</ul>
</li>
<li>부모 트랜잭션이 존재하지 않다면, 새로운 트랜잭션을 생성한다 (즉 자식이 부모가 된다)</li>
</ul>
</li>
<li>Propagation.REQUIRES_NEW<ul>
<li>부모 트랜잭션의 존재 유무와 관계없이, 새로운 트랜잭션을 생성한다. </li>
</ul>
</li>
<li>Propagation.NESTED<ul>
<li>부모 트랜잭션이 존재할 경우, 부모 트랜잭션 안에 새로운 트랜잭션을 생성한다. </li>
<li>단, 자식 트랜잭션은 부모 트랜잭션의 영향(커밋, 롤백)을 받지만 부모는 자식의 영향을 받지 않는다. <ul>
<li>부모를 롤백하면 자식도 롤백되지만, 자식을 롤백해도 부모는 영향 X </li>
</ul>
</li>
</ul>
</li>
<li>Propagation.MANDATORY<ul>
<li>부모가 존재하면 부모에 포함</li>
<li>부모가 존재하지 않는다면 예외 발생</li>
</ul>
</li>
<li>Propagation.SUPPORTS<ul>
<li>부모가 존재하면 부모에 포함</li>
<li>부모가 존재하지 않는다면 트랜잭션 없이 동작</li>
</ul>
</li>
<li>Propagation.NOT_SUPPORTED<ul>
<li>부모가 존재하면 부모 트랜잭션을 보류하고, 트랜잭션 없이 동작</li>
<li>부모가 존재하지 않는다면 트랜잭션 없이 동작</li>
</ul>
</li>
<li>Propagation.NEVER<ul>
<li>트랜잭션을 사용하지 않도록 강제한다. </li>
<li>부모가 존재할 경우, 예외 발생</li>
</ul>
</li>
</ul>
<br>

<h1 id="고립-레벨-isolation">고립 레벨 (Isolation)</h1>
<blockquote>
<p>spring boot @Transactional에서는 Isolation.DEFAULT, 즉 JDBC isolation level가 default로 세팅되어 있다. 
:: 즉 MySQL InnoDB를 사용한다면 REPEATABLE READ 사용 </p>
</blockquote>
<h3 id="고립-레벨이란">고립 레벨이란?</h3>
<blockquote>
<ul>
<li>Shared Lock (공유 잠금) : 읽기 잠금</li>
<li>Exclusive Lock (배타적 잠금) : Insert, Update, Delete (=쓰기) 잠금</li>
</ul>
</blockquote>
<ul>
<li>SQL의 Isolation level과 동일하게 동작</li>
<li>SQL의 고립 레벨 (= 고립 수준 = 격리 레벨 = 격리수준)이란?<ul>
<li>트랜잭션끼리 일관성 없는 데이터를 허용하는 수준</li>
</ul>
</li>
<li>종류 <ul>
<li>[Level 0] Read Uncommitted : select 수행하는 동안 해당 데이터에 Shared lock이 걸리지 않는다.</li>
<li>[Level 1] Read Committed : select 수행하는 동안 해당 데이터에 Shared lock이 걸린다. </li>
<li>[Level 2] Repeatable Read : 트랜잭션이 완료될 때까지 해당 데이터 수정이 불가능하다. </li>
<li>[Level 3] Serializable : 트랜잭션이 완료될 때까지 해당 데이터 수정, 입력이 불가능하다. </li>
</ul>
</li>
</ul>
<h3 id="고립-레벨별-나타나는-이슈">고립 레벨별 나타나는 이슈</h3>
<ul>
<li>Dirty Read : Uncommitted 또는 더티 버퍼에 있는 데이터를 읽어, 롤백된 데이터를 노출하게 됨</li>
<li>Non Repeatable Read : 동일한 쿼리를 2번(A와 B라고 가정) 실행 할 때, A와 B 사이에 <strong>수정 또는 삭제</strong>가 일어나 A와 B의 결과가 달라짐 (=행 값의 변화)</li>
<li>Phantom Read : 동일한 쿼리를 2번(A와 B라고 가정) 실행할 때, A와 B 사이에 <strong>삽입</strong>이 일어나 A와 B의 결과가 달라짐 (=행 수의 변화)</li>
</ul>
<table>
<thead>
<tr>
<th>Isolation Level</th>
<th>Dirty Read</th>
<th>Non Repeatable Read</th>
<th>Phantom Read</th>
</tr>
</thead>
<tbody><tr>
<td>READ UNCOMMITTED</td>
<td>Permitted</td>
<td>Permitted</td>
<td>Permitted</td>
</tr>
<tr>
<td>READ COMMITTED</td>
<td></td>
<td>Permitted</td>
<td>Permitted</td>
</tr>
<tr>
<td>REPEATABLE READ</td>
<td></td>
<td></td>
<td>Permitted</td>
</tr>
<tr>
<td>SERIALIZABLE</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h4 id="db별-default-isolation">DB별 default isolation</h4>
<ul>
<li>MySQL InnoDB : REPEATABLE READ</li>
<li>Oracle : READ COMMITTED</li>
</ul>
<h3 id="spring-boot-transactional-옵션-정리-1">Spring Boot @Transactional 옵션 정리</h3>
<blockquote>
<p>SQL의 고립레벨과 동일!!! </p>
</blockquote>
<blockquote>
<p>Ex. 홍길동씨와 강동원씨가 공유 통장에 대해 아래 작업을 요청할 때</p>
<ol>
<li>통장 잔고 확인 (=select)</li>
<li>통장 잔고 수정 (=update)</li>
</ol>
</blockquote>
<ul>
<li>Isolation.READ_UNCOMMITTED (level 0)<ul>
<li>홍길동씨가 잔고 수정 작업을 끝마치기 전, 강동원씨는 통장 잔고를 확인할 수 있다. </li>
</ul>
</li>
<li>Isolation.READ_COMMITTED (level 1)<ul>
<li>홍길동씨가 잔고 수정을 끝마치기 전까지, 강동원씨는 통장 잔고를 확인할 수 없다. </li>
</ul>
</li>
<li>Isolation.REPEATABLE_READ (level 2)<ul>
<li>홍길동씨가 잔고 확인, 잔고 수정 작업을 끝마칠 때까지 강동원씨는 아무 작업도 시작할 수 없다. </li>
<li>단, 입금의 경우 (=insert) 처리할 수 있다. 하지만 홍길동씨와 강동원씨의 잔고 확인 결과가 달라질 수 있다. </li>
</ul>
</li>
<li>Isolation.SERIALIZABLE (level 3)<ul>
<li>홍길동씨가 잔고 확인, 잔고 수정 작업을 끝마칠 때까지 강동원씨는 아무 작업도 시작할 수 없다 (입금도 불가능). </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[완전 탐색 (0)]]></title>
            <link>https://velog.io/@ej_shin/%EC%99%84%EC%A0%84-%ED%83%90%EC%83%89-0</link>
            <guid>https://velog.io/@ej_shin/%EC%99%84%EC%A0%84-%ED%83%90%EC%83%89-0</guid>
            <pubDate>Fri, 16 Sep 2022 07:34:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/b4f52c53-b57c-43b6-b3f5-70bda5edb2ea/image.gif" alt=""></p>
<blockquote>
<p>완전 탐색을 쌈싸먹기 전, 관련 개념을 알아보자 🧐</p>
</blockquote>
<h2 id="비트-마스크">비트 마스크</h2>
<blockquote>
<p>bit 연산을 이용해 부분 집합을 나타내는 방법
집합을 배열의 인덱스로 표현할 수 있기 때문에 상태 다이나믹을 할 때 자주 사용한다. 
<strong>STL의 bitset</strong>을 이용해서 더 쉽게 사용할 수 있다! </p>
</blockquote>
<h3 id="종류">종류</h3>
<ul>
<li>and (&amp;) : 둘 다 1일 때 1</li>
<li>or (|) : 둘 중 하나라도 1이면 1</li>
<li>not (~) : 0이면 1, 1이면 0</li>
<li>xor (^) : 같으면 0, 다르면 1</li>
</ul>
<h3 id="비트-연산">비트 연산</h3>
<ul>
<li>두 수 A와 B를 비트 연산 하는 경우, 가장 뒤의 자리부터 하나씩 연산을 수행한다. </li>
<li>not 연산의 경우<ul>
<li>자료형에 따라 결과 달라진다. 비트가 자료형에 따라 달라지니까! <ul>
<li>8비트 자료형, 32비트 자료형, unsigned, signed... </li>
</ul>
</li>
</ul>
</li>
<li>shift left (&lt;&lt;), shift right (&gt;&gt;)<ul>
<li>A &lt;&lt; B : A를 왼쪽으로 B비트만큼 민다. A * 2^B와 동일<ul>
<li>1 &lt;&lt; 1 : 10이니까 2 </li>
</ul>
</li>
<li>A &gt;&gt; B : A를 오른쪽으로 B비트만큼 민다. A / 2^B와 동일 <ul>
<li>10 &gt;&gt; 2 : 10이니까 2 </li>
</ul>
</li>
<li>(A+B)/2 = (A+B) &gt;&gt; 1 </li>
</ul>
</li>
</ul>
<h3 id="비트-마스크-1">비트 마스크</h3>
<ul>
<li>*<em>정수로 집합(S)을 나타낼 수 있다. *</em><ul>
<li>{1, 3, 4, 5, 9] = 570 = 2^1 + 2^3 + 2^4 + 2^5 + 2^9 = 1000111010</li>
</ul>
</li>
<li>*<em>어떤 수(i)의 포함 여부를 확인하기 위해 &amp; 연산을 이용한다. *</em><ul>
<li><strong>S &amp; (1 &lt;&lt; i)</strong></li>
<li>0이 포함되어 있는지 검사 <ul>
<li>570 &amp; 2^0 = 570 &amp; (1&lt;&lt;0) = 1000111010 &amp; 1 = 0</li>
</ul>
</li>
<li>1이 포함되어 있는지 검사<ul>
<li>570 &amp; 2^1 = 570 &amp; (1&lt;&lt;1) = 1000111010 &amp; 10 = 10 = 2</li>
</ul>
</li>
<li>2가 포함되어 있는지 검사<ul>
<li>570 &amp; 2^2 = 570 &amp; (1&lt;&lt;2) = 1000111010 &amp; 100 = 000 = 0</li>
</ul>
</li>
</ul>
</li>
<li>*<em>어떤 수(i)를 새로 추가하기 위해 | 연산을 이용한다. *</em><ul>
<li><strong>S | (1 &lt;&lt; i)</strong></li>
<li>2 추가하기 <ul>
<li>570 | 2^2 = 570 | (1&lt;&lt;2) = 1000111010 | 100 = 1000111110 = 574</li>
</ul>
</li>
<li>만약 이미 존재하는 수를 새로 추가한다면? 그대로! <ul>
<li>1 추가하기 </li>
<li>570 | 2^1 = 570 | (1&lt;&lt;1) = 1000111010 | 10 = 1000111010 = 570</li>
</ul>
</li>
</ul>
</li>
<li><strong>어떤 수(i)를 제거하기 위해 ~ 연산을 이용한다.</strong><ul>
<li><strong>S &amp; ~(1 &lt;&lt; i)</strong></li>
<li>1 제거하기 <ul>
<li>570 &amp; ~2^1 = 570 &amp; ~(1&lt;&lt;1) = 570 &amp; ~(10) = 1000111010 &amp; ~(01) = 1000111010 &amp; 1111111101 = 1000111000 = 568 </li>
</ul>
</li>
</ul>
</li>
<li><strong>어떤 수(i)를 토글하기 위해 ^ 연산을 이용한다.</strong><ul>
<li><strong>S ^ (1 &lt;&lt; i)</strong>    </li>
</ul>
</li>
<li>*<em>전체 집합 : (1&lt;&lt;N) - 1 *</em></li>
<li><strong>공집합 : 0</strong></li>
</ul>
<h3 id="문제-예제-1--백준-집합">문제 예제 (1) : 백준 집합</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/11723">https://www.acmicpc.net/problem/11723</a> </p>
</blockquote>
<h4 id="내-풀이">내 풀이</h4>
<ul>
<li>고냥 저항없이 위에서 공부한대로 코드를 짰다. <ul>
<li>그랬더니 시간 초과<del>~</del>😇 </li>
<li>입력은 버퍼로 받았으니 아마 출력하는 부분이 문제겠지 싶어서 StringBuilder를 사용하도록 코드를 수정해줬다. </li>
<li>수정해주니 통과했다. 굿굿 앞으론 스트링빌더를 걍 습관처럼 써줘야지
<img src="https://velog.velcdn.com/images/ej_shin/post/1ab6e44a-f91f-4d5d-99d1-49afc4612423/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<h2 id="순열">순열</h2>
<blockquote>
<p>1~N까지로 이루어진 수열. 
크기는 항상 N이며, 겹치는 숫자가 존재하지 않아야 한다. </p>
</blockquote>
<h3 id="다음-순열">다음 순열</h3>
<blockquote>
<p>순열을 사전순으로 나열했을 때, 사전순으로 다음에 오는 순열과 이전에 오는 순열을 찾는 방법 </p>
</blockquote>
<h4 id="다음-순열-찾는-법-on">다음 순열 찾는 법 (O(n))</h4>
<blockquote>
<p>예시 순열 : 7 2 3 6 5 4 1</p>
</blockquote>
<ul>
<li>A[i-1] &lt; A[i] 를 만족하는 가장 큰 i를 찾는다. <ul>
<li>가장 큰 i니까 오른쪽에서 부터 체크! <ul>
<li>즉 순열의 마지막 수에서 끝나는 가장 긴 감소 수열 찾기. <ul>
<li>A[i-1] = 3, A[i] = 6. i = 3</li>
<li>감소수열은 6 5 4 1</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>j &gt;= i 면서 A[j] &gt; A[i-1]을 만족하는 가장 큰 j를 찾는다. <ul>
<li>가장 큰 j니까 오른쪽에서 찾아야! </li>
<li>A[j] = 4. j = 5</li>
</ul>
</li>
<li>A[i-1]과 A[j]를 swap한다<ul>
<li>7 2 4 6 5 3 1 </li>
</ul>
</li>
<li>A[i]부터 순열을 뒤집는다. <ul>
<li>7 2 4 1 3 5 6</li>
</ul>
</li>
</ul>
<p>다음 순열에서 제일 첫 번째 순열은 오름차순, 제일 마지막 순열은 내림차순이다. 
따라서 다음 순열을 구할 때, 특정 인덱스에서 사전 순 다음 값의 조건은</p>
<ul>
<li>현재 값보다 크면서</li>
<li>현재 값 다음 인덱스의 수들 중 제일 작은 값이어야 한다</li>
</ul>
<p>을 충족해야 한다. </p>
<h3 id="문제-예제-2--백준-다음-순열">문제 예제 (2) : 백준 다음 순열</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/10972">https://www.acmicpc.net/problem/10972</a></p>
</blockquote>
<h4 id="내-풀이-1">내 풀이</h4>
<ul>
<li>이번에도 저항없이... 위에서 공부한 다음 순열 구하는 방식을 그대로 코드로 구현했다. </li>
<li>System.out을 이용한 경우 
<img src="https://velog.velcdn.com/images/ej_shin/post/075ea921-3871-45ff-9e5f-92f7ab544f5d/image.png" alt=""></li>
<li>StringBuilder를 이용한 경우
<img src="https://velog.velcdn.com/images/ej_shin/post/a0dd3d3a-c40f-4d14-b7c1-63ee5734babe/image.png" alt=""></li>
</ul>
<h3 id="문제-예제-3--백준-이전-순열">문제 예제 (3) : 백준 이전 순열</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/10973">https://www.acmicpc.net/problem/10973</a></p>
</blockquote>
<h4 id="내-풀이-2">내 풀이</h4>
<ul>
<li>다음 순열 구했던거랑 반대로 구하면 된다. </li>
<li>맞았당ㅎ.ㅎ
<img src="https://velog.velcdn.com/images/ej_shin/post/a233ee73-0d54-4c81-995b-b633f4687a75/image.png" alt=""></li>
</ul>
<h3 id="문제-예제-4--백준-모든-순열">문제 예제 (4) : 백준 모든 순열</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/10974">https://www.acmicpc.net/problem/10974</a></p>
</blockquote>
<h4 id="내-풀이-3">내 풀이</h4>
<ul>
<li>다음 순열 구했던걸 응용하면 된다. </li>
<li>맞았다<del>~</del> 
<img src="https://velog.velcdn.com/images/ej_shin/post/d3a6517a-bcb4-452f-8562-3673ea5c6bbf/image.png" alt=""></li>
</ul>
<h3 id="문제-예제-5--백준-순열의-순서">문제 예제 (5) : 백준 순열의 순서</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/1722">https://www.acmicpc.net/problem/1722</a></p>
</blockquote>
<h4 id="내-풀이-4">내 풀이</h4>
<ul>
<li>두번째 입력의 첫번째 수가 1이면 k번째 순열을, 2면 뒤이어 입력한 순열이 몇 번째 순열인지 찾으면 된다. </li>
<li>완전 아무 생각 없이 풀면, 위에서 풀었던 다음 순열을 응용하면 된다. <ul>
<li>순서 변수 하나 만들어서, 다음 순열 함수가 호출될 때마다 카운트하는 방식. </li>
<li>근데 이게 좋은 풀이일까? 🤔 </li>
</ul>
</li>
<li>아아아아아주 오래 전 공부했던 확통 지식을 되살려보면, 순열의 개수는 팩토리얼로 구했다. <ul>
<li>ex. 1~N으로 이루어진 순열의 크기는 N! <ul>
<li>요건 각 자리에 올 수 있는 숫자의 개수로 만든 공식이다. </li>
<li>N= 5일 때, 맨 처음에 올 수 있는 수는 1, 2, 3, 4, 5니까 5, 두번째 올 수 있는 수는 앞에 나온 수 제외하고 전부니까 5, ... 하는 식으로. </li>
</ul>
</li>
<li>요걸 잘 응용하면 되지 않을까? <ul>
<li>왜냐하면 문제 조건에서 순열의 순서는 사전순 (오름차순)이라고 했으니까! </li>
<li>예를 들어 N=3일 때 { 1 2 3 } 의 순서를 찾아보자. </li>
<li>각 자리수에 올 수 있는 첫번째 숫자들이 배치되어 있으니, 0 x 0 x 0 = 0. 첫번째 순열을 고려해서 +1해주면 k = 1.</li>
<li>이번엔 { 3 2 1 }<ul>
<li>첫번째 자리수에 3은 세번째로 올 수 있는 수다. 따라서 2! x 2 = 4</li>
<li>두번째 자리수에 2는 두번째로 올 수 있는 수다. 따라서 1 </li>
<li>세번째 자리수에 1은 처음으로 올 수 있는 수다. 따라서 0</li>
</ul>
</li>
<li>4 + 1 + 1(첫번째) = 6</li>
<li>실제로 순열을 전부 나열해보면 아래와 같으므로 6번째 순열이 맞다. <blockquote>
</blockquote>
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1</li>
</ul>
</li>
<li>옹 일단 순서는 구할 수 있겠다! 그럼 이제 순서가 주어졌을 때 순열을 찾는 방법을 고민해보자. </li>
<li>위에서 생각한 순서 찾는 로직을 응용하면 된다. 주어진 숫자가 전체 수 N에서 몇 번째 순서에 위치해있는지를 알면 쏘<del>~</del>이지 </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[그리디 알고리즘 (2)]]></title>
            <link>https://velog.io/@ej_shin/%EA%B7%B8%EB%A6%AC%EB%94%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2</link>
            <guid>https://velog.io/@ej_shin/%EA%B7%B8%EB%A6%AC%EB%94%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2</guid>
            <pubDate>Sat, 10 Sep 2022 14:14:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/d27341d2-efb5-452d-8cae-31193ce0091d/image.gif" alt=""></p>
<blockquote>
<p>그리디 알고리즘을 쌈싸먹어 보자2</p>
</blockquote>
<h3 id="문제-예제-1--백준-잃어버린-괄호">문제 예제 (1) : 백준 잃어버린 괄호</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/1541">https://www.acmicpc.net/problem/1541</a></p>
</blockquote>
<h4 id="내-풀이">내 풀이</h4>
<ul>
<li>세준이는 왜 이런 일을 하는 걸까..? 진짜 이상한 애야..</li>
<li>&#39;+&#39;와 &#39;-&#39; 연산으로만 이루어져 있다는게 포인트인 것 같다. <ul>
<li>&#39;+&#39;만 있는 경우엔 더하기만, &#39;-&#39;만 있는 경우엔 빼기만 하면 된다. 즉 문제는 더하기랑 빼기가 섞여 있는 경우! </li>
</ul>
</li>
<li>상식적으로 생각하면 결과가 작으려면 작은 수에서 큰 수를 빼면 된다. <ul>
<li>그럼 빼기 연산자 바로 뒤에 더하기 연산자가 나온다면, 그 위치에 괄호를 넣으면 된다. </li>
<li>ex. a-b+c가 있다면 a-(b+c) 요런식으로! </li>
</ul>
</li>
<li>습 근디 코드로 어케 구현하지? <ul>
<li><del>근데 문제가 괄호의 위치를 구하는게 아니라, 최소값을 구하는거니까 그냥 + 연산자가 나왔을 때 -계산을 하면 되는거 아닌가..?!</del> <ul>
<li>구론데구로나 + 연산자만 있는 경우엔 + 연산만 해야 한다. 그렇다고 전체 반복을 돈 다음 +만 있는지 -만 있는지 둘다 있는지 케이스를 나눠서 수행하면 쓸데없이 코드 라인만 길어지고 반복도 많이 돌게 된다. </li>
</ul>
</li>
<li>StringTokenizer로 음수 기준으로 자르고, 자른 문자열에 연산자가 끼어있다면 더해서 빼자<ul>
<li>근데 문제가 있다! 맨 초기값을 정해야 하는데, 만약 + 연산자만 있다면 StringTokenizer가 자르지 못하니까 숫자로 형변환를 할 수 없다. <ul>
<li>result를 이상한 값(MAX_VALUE 등)으로 초기화하고 조건문으로 체크해주는 방법도 있겠지만, 마지막에 result에서 초기값을 다시 연산해주는게 싫어서 &amp;&amp; 혹시라도 반복문 중간에 초기값이 또 나올까봐 boolean 플래그를 하나 두었다. </li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>    int result = 0;
    boolean isFirst = true;
    while (st.hasMoreTokens()) {
      String subStr = st.nextToken();
      int subResult = 0;
      StringTokenizer plusSt = new StringTokenizer(subStr, &quot;+&quot;);
      while (plusSt.hasMoreTokens()) {
        subResult += Integer.parseInt(plusSt.nextToken());
      }
      if (isFirst) {
        result += subResult;
        isFirst = false;
      }
      else result -= subResult;
    }</code></pre><ul>
<li>맞았다ㅎㅎ 
<img src="https://velog.velcdn.com/images/ej_shin/post/5786e412-3a1a-4e0f-81aa-ca1afaf6ea0d/image.png" alt=""></li>
</ul>
<h4 id="해설">해설</h4>
<ul>
<li>sign와 minus 변수 두개를 활용한다. </li>
<li>만약 + 연산자가 나오면 sign에 1을, - 연산자가 나오면 sign에 -1을 넣는다. </li>
<li>이후 minus 변수를 이용, sign에서 -1이 한 번이라도 나오면 그 이후로 나오는 모든 수를 전부 뺀다 (+와 -가 섞인 경우니까 전부 빼기로 대체)</li>
</ul>
<p>근데 이게 왜 그리디 유형인지 아직 잘 모르겠다..! 😇 </p>
<br>

<h3 id="문제-예제-2--백준-수-묶기">문제 예제 (2) : 백준 수 묶기</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/1744">https://www.acmicpc.net/problem/1744</a></p>
</blockquote>
<h4 id="내-풀이-1">내 풀이</h4>
<ul>
<li>아까는 더하기, 빼기를 응용해서 최소값을 구했다면 지금은 곱하기를 응용해서 최대값을 구하기! </li>
<li>조건 : 동일 숫자를 묶어서 곱하면 안됨, 모든 수는 단 한번만 묶거나 혹은 묶지 않아야 함, 정답은 항상 2^31보다 작음 </li>
<li>당연히 곱해서 큰 수끼리 묶어야겠지? <ul>
<li>일단 수를 입력받는데, 음수랑 양수를 묶으면 안되니까 따로 입력받자. </li>
<li>입력받은 수들을 내림차순 정렬하자. 이때 음수는 오름차순 정렬해야한다. <ul>
<li>왜냐하면 음수 * 음수는 양수니까, 작은 순서대로 정렬해야 결과적으로 수가 커지기 때문!</li>
</ul>
</li>
<li>0은 음수 배열에 들어가야한다. <ul>
<li>만약 입력받은 음수가 총 짝수개라면, 음수와 곱해서 0으로 만들어 더하는게 좋고, 홀수개라면 오름차순 정렬해서 마지막 남은 1개 (묶이지 않은)로 더해주는게 좋으니까. </li>
</ul>
</li>
</ul>
</li>
<li>이후 각각 배열에서 수를 묶어서 결과값에 더한다. <ul>
<li>배열 사이즈가 홀수라면 제일 마지막 인덱스는 제외하고 묶고, 짝수라면 고냥 다 묶는 방식 </li>
</ul>
</li>
<li>근데 대차게 틀렸다ㅋㅋㅋㅋ <ul>
<li>왜지? 하고 테케를 다시 흝어봤는데, 하나하나 다 돌려보니 [2, 1, 1]에서 틀렸다! </li>
<li>(테케 다 돌려보지도 않고 제출한 내가 돌멩이긴 함ㅠㅠ)</li>
<li>위처럼 1이 짝수개 연속해서 나온다면, 걍 각자 더하는게 더 결과가 커지기 때문! 진짜...멍청하다 나 자신.... </li>
</ul>
</li>
</ul>
<pre><code>  public static int tieNum(List&lt;Integer&gt; numList) {
    int result = 0;
    int size = numList.size();
    int j = 0;
    while (j &lt; size) {
      // DESCRIBE: 전체 크기가 짝수일 때 마지막 수가 아니라면, 그리고 현재랑 다음이 1이 아니라면 곱해서 더하기
      if (j+1 &lt; size &amp;&amp; numList.get(j) != 1 &amp;&amp; numList.get(j+1) != 1) {
        result += numList.get(j++) * numList.get(j++);
      }

      // DESCRIBE: 아니라면 각각 더하기
      else {
        result += numList.get(j++);
      }
    }
    return result;
  }</code></pre><ul>
<li>맞았다~~ 
<img src="https://velog.velcdn.com/images/ej_shin/post/a3a8e69d-a2f4-4d95-a316-19d55fb46e47/image.png" alt=""></li>
</ul>
<h4 id="해설-1">해설</h4>
<ul>
<li>양수는 큰 수끼리<ul>
<li>하지만 1끼리는 묶지 말기! </li>
</ul>
</li>
<li>음수는 작은 수끼리</li>
<li>0은 묶지 않는 것이 최대 <ul>
<li>하지만 묶이지 않은 음수가 있다면 0을 이용하자 </li>
</ul>
</li>
<li>처음 숫자 입력 받을 때부터 1의 개수와 0의 개수를 세고, 양수 배열과 음수 배열을 짝수 사이즈로 만들어서 푸는 방법도 있다. </li>
</ul>
<br>

<h3 id="문제-예제-3--백준-대회-or-인턴">문제 예제 (3) : 백준 대회 or 인턴</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/2875">https://www.acmicpc.net/problem/2875</a></p>
</blockquote>
<h4 id="내-풀이-2">내 풀이</h4>
<ul>
<li>n과 m을 줄이면서 팀 수를 카운트하면 된다. </li>
<li>근데 이제 남은 사람으로 인턴십도 보내고~ 팀도 만들 수 있을 때까지! </li>
</ul>
<pre><code>    int result = 0;
    while (n+m &gt;= k+3 &amp;&amp; n &gt;= 2 &amp;&amp; m &gt;= 1) {
      n -= 2;
      m -= 1;
      result+=1;
    }</code></pre><ul>
<li>맞았돵
<img src="https://velog.velcdn.com/images/ej_shin/post/63d03dde-ce85-4061-8d6a-5dba32f41947/image.png" alt=""></li>
</ul>
<h4 id="해설-2">해설</h4>
<ul>
<li>아래 경우가 한 팀 (동일 접근)<ul>
<li>n+3 &gt;= k+3</li>
<li>m &gt;= 1 </li>
<li>n &gt;= 2</li>
</ul>
</li>
</ul>
<br>

<h3 id="문제-예제-4--백준-30-1053-8분-종료">문제 예제 (4) : 백준 30 (10:53, 8분 종료)</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/10610">https://www.acmicpc.net/problem/10610</a></p>
</blockquote>
<h4 id="내-풀이-3">내 풀이</h4>
<ul>
<li>미르코씨...재밌게 사시는 분이시군요</li>
<li>30의 배수 = 3 * 10의 배수</li>
<li>즉 아래 조건을 충족해야 한다<ul>
<li>하나 이상의 0 포함 (10의 배수가 되려면 마지막 자리가 0이여야 함)</li>
<li>각 자리의 숫자들을 더했을 때 3의 배수여야 함 (3의 배수 조건)</li>
<li>일단 위 조건 충족 못 하면 바로 -1 리턴하게 하자. </li>
</ul>
</li>
<li>충족한다면 이제 최대값을 찾아야함<del>~</del> <ul>
<li>위에서 3의 배수 조건을 이미 충족했으니 제일 마지막 자리가 0이기만 하면 된다. </li>
<li>입력값이 무조건 양수임을 이용, 입력 문자열을 정수 배열로 만들어서 내림차순 정렬하면 된다. </li>
</ul>
</li>
</ul>
<pre><code>코드를 입력하세요</code></pre><ul>
<li>맞았지렁
<img src="https://velog.velcdn.com/images/ej_shin/post/2be00da5-2e1b-4d67-bf31-3ad24b03cb9c/image.png" alt=""></li>
</ul>
<h4 id="해설-3">해설</h4>
<ul>
<li>동일 접근</li>
</ul>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[그리디 알고리즘]]></title>
            <link>https://velog.io/@ej_shin/%EA%B7%B8%EB%A6%AC%EB%94%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@ej_shin/%EA%B7%B8%EB%A6%AC%EB%94%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Thu, 08 Sep 2022 16:32:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/c863df05-a058-434b-951e-4fac0996f59f/image.gif" alt=""></p>
<blockquote>
<p>그리디 알고리즘을 쌈싸먹어 보자! </p>
</blockquote>
<h1 id="그리디-알고리즘">그리디 알고리즘</h1>
<h3 id="특징">특징</h3>
<ul>
<li>결정해야 할 때 그 순간 제일 좋다고 생각되는 걸 선택하면서 답을 찾는 알고리즘<ul>
<li>졸리면 자고~ 배고프면 밥 먹고~ </li>
</ul>
</li>
<li>즉 그 순간엔 최적이라도, 최종적으로는 답이 최적이 아닐 수 있다<h3 id="유형">유형</h3>
</li>
<li>거스름돈 문제 <ul>
<li>동전도 지페도 아주 많을 때, N원을 최소 개수의 지폐, 동전을 이용해서 거슬러주려면 어떻게 해야 하는가? <ul>
<li>가장 큰 액면가를 가진 지폐 / 동전부터 거슬러주자! </li>
</ul>
</li>
</ul>
</li>
<li>즉, 지금 순간에 제일 최적인 걸 골라야 할 때 사용하는 알고리즘...! 🤔 </li>
</ul>
<h3 id="문제-예제-1--백준-동전-0">문제 예제 (1) : 백준 동전 0</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/11047">https://www.acmicpc.net/problem/11047</a></p>
</blockquote>
<h4 id="내-풀이">내 풀이</h4>
<ul>
<li>가치의 합은 최대인데, 사용해야 하는 동전 개수는 최소<ul>
<li>즉 K원을 만들기 위해 숫자를 쪼갰을 때 가장 적게 쪼갤 수 있는 기준(배수)를 찾아야 한다. </li>
</ul>
</li>
<li>주어진 동전의 가치가 오름차순이니까, 뒤에서부터 내려와야 한다.<ul>
<li>뒤에서 내려오면서 만약 현재 동전 가치가 K보다 크다면 넘기고, 작다면 쪼개는 과정을 반복하면 될 듯? </li>
</ul>
</li>
</ul>
<pre><code>    // DESCRIBE: 동전 개수 찾기
    for (int i = N-1; i &gt;= 0; i--) {
      if (array[i] &lt;= K) {
        answer += (K/array[i]);
        K %= array[i];
      }
    }</code></pre><ul>
<li>옹ㅎㅎ 맞았다 
<img src="https://velog.velcdn.com/images/ej_shin/post/35c2b73e-33aa-4a60-abad-ea1bb26d66de/image.png" alt=""></li>
</ul>
<h4 id="해설">해설</h4>
<ul>
<li>(1 ≤ Ai ≤ 1,000,000, A1 = 1, i ≥ 2인 경우에 Ai는 Ai-1의 배수)<ul>
<li>요 조건 덕분에 그리디 알고리즘이 성립하게 된다 </li>
<li>돈을 1원짜리로 전부 바꾼 다음, 그 다음 동전의 가치가 항상 이전 동전의 배수를 만드는지 확인해서 항상 성립하는지 확인하자. </li>
</ul>
</li>
</ul>
<br>

<h3 id="문제-예제-2--백준-회의실-배정">문제 예제 (2) : 백준 회의실 배정</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/1931">https://www.acmicpc.net/problem/1931</a></p>
</blockquote>
<h4 id="내-풀이-1">내 풀이</h4>
<ul>
<li>이 문제가 그리디 유형인걸 어떻게 알 수 있을까? <ul>
<li>N개 중 어떤 것을 선택하느냐에 따라 결과가 달라지는 문제다. </li>
<li>즉 일종의 거스름돈 문제인거지..! <ul>
<li>거스름돈은 최대 액면가를 가진 동전/지폐부터 골랐다면, 이 문제에서는 어떤 회의를 우선적으로 골라야 할까? <ul>
<li>평범하게 생각해보면 회의 시간이 짧거나, 일찍 시작하는 회의를 고르면 될거다. 하지만 시작 시간을 기준으로 잡으면 안된다. 다음 회의랑 겹칠 수 있으니까! <ul>
<li>(1 4, 3 5, 0 6이 있을 때 시작 시간 기준으로 잡으면 1 4, 0 6이지만 종료 시간을 고려하면 겹치는 회의다)</li>
<li>따라서 종료 시간을 기준으로 잡고, 시작 시간이 앞에서 잡은 종료 시간보다 같거나 큰 경우를 우선적으로 고르면 될거다 </li>
</ul>
</li>
<li>위의 흐름에서, 회의를 선택하는 기준은 (1) 회의 종료 시간이 빠르면서 (2) 전체 회의 시간이 짧은 친구를 우선적으로 골라내야 한다. </li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>그럼 어케 풀까? <ul>
<li>일단 회의 종료 시간을 기준으로 입력 회의들을 오름차순 정렬한다. </li>
<li>첫번째 인덱스부터 시작해서, 만약 다음 인덱스의 시작 시간이 이전 인덱스의 종료 시간보다 같거나 큰 경우만 카운트한다.<ul>
<li>이렇게 하면, 만약 시작 시간이 동일하더라도 종료 시간이 더 빠른 친구가 먼저 선택되니까 굳이 회의 전체 시간을 따로 계산해 줄 필요가 없다! <ul>
<li>테케에서 (5,7) (5,9)가 해당. 시작 시간이 동일하다고 가정했을 때, 종료 시간을 기준으로 오름차순 정렬했으니 자연스럽게 전체 회의 시간이 짧은 (5, 7)이 카운트된다. </li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>    // DESCRIBE: 종료 시간 기준으로 오름차순 정렬. 만약 동일하다면 시작 시간 오름차순으로 
    Arrays.sort(array, (o1, o2) -&gt; {
      if(o1[1] == o2[1]) {
        return o1[0] - o2[0];
      }else {
        return o1[1] - o2[1];
      }
    });

    // DESCRIBE: 현재 인덱스의 시작 시간이 이전 인덱스의 종료 시간보다 크거나 같다면 카운트
    int prevEndTime = array[0][1];
    for (int i = 1; i &lt; N; i++) {
      if (array[i][0] &gt;= prevEndTime) {
        result += 1;
        prevEndTime = array[i][1];
      }
    }</code></pre><ul>
<li>맞았다! (2차원 배열 정렬 연습 좀 해야할듯^^^^)
<img src="https://velog.velcdn.com/images/ej_shin/post/694005cd-e607-4a4e-bf96-a389f245232a/image.png" alt=""></li>
</ul>
<h4 id="해설-1">해설</h4>
<ul>
<li>오 회의 시간이 짧은 경우는 아예 기준이 될 수 없구나..! <ul>
<li>기준 세우고 반례 고민하는 연습을 해야 할 것 같다. <ul>
<li>나는 풀 때 어? 정렬해두고 보니 회의 시간 짧은건 따로 계산 안해도 되네? 하고 풀었는데, 사실은 아예 고려하면 안되는 대상이었던 것..! 🤔 </li>
<li>이건 맞은게 아니라 틀렸다고 봐야 할 것 같다. 걍 얻어 걸린거니까... 😇</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="문제-예제-3--백준-atm">문제 예제 (3) : 백준 ATM</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/11399">https://www.acmicpc.net/problem/11399</a></p>
</blockquote>
<h4 id="내-풀이-2">내 풀이</h4>
<ul>
<li>각 사람이 돈을 인출하는데 필요한 시간의 합의 최솟값 = 누적합이 최소<ul>
<li>즉 돈을 뽑는데 걸리는 시간이 작은 사람 순으로 고르면 된다. <ul>
<li>ex. 3 1 4 3 2 -&gt; 3 + (3+1) + (3+1+4) + (3+1+4+3) + (3+1+4+3+2) = 39</li>
<li>ex. 1 2 3 3 4 -&gt; 1 + (1+2) + (1+2+3) + (1+2+3+3) + (1+2+3+3+4) = 32</li>
</ul>
</li>
</ul>
</li>
<li>입력값을 오름차순으로 정렬하고, 이후 누적합을 구하자. </li>
</ul>
<pre><code>    // DESCRIBE: 오름차순 정렬
    Arrays.sort(array);

    // DESCRIBE: 누적합 구하기
    int waitTime = 0;
    int totalWaitTime = 0;
    for (int i = 0; i&lt;N; i++) {
      waitTime += array[i];
      totalWaitTime += waitTime;
    }</code></pre><ul>
<li>맞았다 ^~^ 
<img src="https://velog.velcdn.com/images/ej_shin/post/35154613-2892-46a3-80e6-cc7bb9fed24a/image.png" alt=""></li>
</ul>
<h4 id="해설-2">해설</h4>
<ul>
<li>오름차순 = 비내림차순으로 정렬! 동일한 접근<ul>
<li>(증명) 오름차순으로 정렬하고 결과값(S)을 구했을 때, 인덱스를 바꿔서 다시 계산(S&#39;)해보면 값이 같거나 커지게 된다. </li>
<li>(증명) S-S&#39; = (i-j)(pj-pi)</li>
<li>(i-j)가 &lt;= 0 이므로 항상 음수가 된다. 즉 S&#39;는 언제나 S보다 같거나 크다. <ul>
<li>따라서 오름차순으로 정렬하는게 최적해가 된다. </li>
</ul>
</li>
</ul>
</li>
<li>우억 이렇게 증명할 생각은 안하고 그냥 냅다 응 당연히 오름차순이지~ 하고 풀었는데.. 이렇게 풀면 이제 실전에서 테케에 갈려나가는거겠지?! 🔨 <ul>
<li>구상한 풀이를 먼저 증명한 후 구현하는 연습이 필요하다! 그냥 이럴 것 같아~ 하고 풀지 말자! </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스택 ]]></title>
            <link>https://velog.io/@ej_shin/%EC%8A%A4%ED%83%9D</link>
            <guid>https://velog.io/@ej_shin/%EC%8A%A4%ED%83%9D</guid>
            <pubDate>Tue, 06 Sep 2022 13:19:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/685296b7-b1b7-4565-8c7a-73778eb7eab0/image.gif" alt=""></p>
<blockquote>
<p>스택을 쌈싸먹어 보자! </p>
</blockquote>
<h1 id="스택">스택</h1>
<h3 id="특징">특징</h3>
<ul>
<li>한 쪽으로만 자료를 넣고 뺄 수 있다</li>
<li>위 특징 때문에 제일 먼저 들어간 자료가 제일 나중에 나오는 구조 = 선입후출 (LIFO)</li>
</ul>
<h3 id="스택의-구현">스택의 구현</h3>
<ul>
<li>배열을 이용, 자료를 넣을 땐 size가 늘어나고 뺄 땐 size가 줄어드는 원리 </li>
</ul>
<h3 id="문제-예제-1-백준-9012-괄호">문제 예제 1 (백준 9012 괄호)</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/9012">https://www.acmicpc.net/problem/9012</a> </p>
</blockquote>
<h4 id="내-풀이">내 풀이</h4>
<ul>
<li><p>&#39;(&#39;와 &#39;)&#39;의 쌍이 안 맞는 경우가 있다면 NO를 출력하면 된다. </p>
<ul>
<li>&#39;(&#39;가 &#39;)&#39;보다 먼저 나와야 함 <ul>
<li>즉 현재 위치에서 &#39;)&#39;의 수는 항상 &#39;(&#39;의 수보다 작거나 같아야 함</li>
<li>동시에 문자열 끝까지 갔을 때, &#39;(&#39;의 수와 &#39;)&#39;의 수는 같아야 함</li>
</ul>
</li>
</ul>
</li>
<li><p>문자열을 입력받은 후, 첫 글자부터 마지막 글자까지 넘어가면서 괄호 수를 센다.</p>
<ul>
<li><p>만약 반복문 도는 도중에 &#39;(&#39;의 수가 &#39;)&#39;의 수보다 적어진다면 바로 리턴 </p>
<ul>
<li>(불필요한 반복을 줄이기 위함)</li>
</ul>
</li>
<li><p>끝까지 돌았을 때 &#39;(&#39;의 수와 &#39;)&#39;의 수 비교</p>
<pre><code>public static void count(String str) {
int left = 0;
int right = 0;

for (int i = 0; i &lt; str.length(); i++) {
  if (str.charAt(i) == &#39;(&#39;) left++;
  else right++;

  if (right &gt; left) {
    System.out.println(&quot;NO&quot;);
    return;
  }
}

if (right != left) System.out.println(&quot;NO&quot;);
else System.out.println(&quot;YES&quot;);
}</code></pre></li>
</ul>
</li>
<li><p>맞았당
<img src="https://velog.velcdn.com/images/ej_shin/post/8f570289-9f75-48a9-a3ef-37dec4364d87/image.png" alt=""></p>
</li>
</ul>
<h4 id="해설">해설</h4>
<ul>
<li>닫는 괄호가 나왔을 때, 아직 짝이 없는 여는 괄호와 매칭한다면?<ul>
<li>모든 문자에 대해 검사를 수행해야 하므로 시간 복잡도는 O(N)</li>
</ul>
</li>
<li>(풀이) 스택에 아직 짝이 없는 여는 괄호를 집어 넣고, 닫는 괄호가 나올 때마다 하나씩 스택에서 빼주는 방식으로 구현한다. <ul>
<li>이 경우 최종적으로 스택이 비었는지 아닌지 체크만 하면 되니까 시간 복잡도는 O(1)</li>
<li>근데 이제 이걸 스택에 직접 넣고 빼지 말고, cnt 값을 이용하면 좀 더 쉬워진다! <ul>
<li>cnt가 음수인지 아닌지로 판별하는 방식</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<p>훔 풀이를 어떻게 하느냐보다, 이 문제를 보고 스택이 생각났냐 안났냐로 접근하면 좋을 것 같다..! 사실 풀이를 듣고도 왜 굳이 스택을 응용해야 하는지 모르겠다 
(어차피 문자열의 처음부터 끝까지 반복을 돌아야 하는건 똑같으니까)</p>
<h3 id="문제-예제-2-백준-9012-쇠막대기">문제 예제 2 (백준 9012 쇠막대기)</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/10799">https://www.acmicpc.net/problem/10799</a></p>
</blockquote>
<h4 id="내-풀이-1">내 풀이</h4>
<ul>
<li>이 친구도 결국 방금 풀었던 괄호 쌍 만드는 문제!</li>
<li>스택을 응용해서 풀어보자.<ul>
<li>여는 괄호가 나오면 스택에 넣는다. </li>
<li>닫는 괄호가 나오면 스택에서 뺀다. 이때 result += 1</li>
</ul>
</li>
<li>이렇게 입력 문자열 끝까지 돈 다음, result 수를 출력하면 될 것 같다. <ul>
<li>근데 이제 쇠막대기가 겹쳐있을 수 있다는 점을 고려해야 하는..! </li>
<li>여는 괄호가 연속해서 나온다면 -&gt; 즉, 닫는 괄호가 나와서 스택을 pop했을 때 스택이 비지 않았다면 스택 사이즈만큼 더해주면 된다. </li>
</ul>
</li>
<li>라고 생각했는데 테케에서 틀렸다ㅋㅎ <ul>
<li>)(의 경우와 ())(()의 경우, 즉 가운데가 빈 경우와 아닌 경우를 고려해야 하기 때문! 
<img src="https://velog.velcdn.com/images/ej_shin/post/9bc0f56e-8689-4364-ae84-997b97cc4c47/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li><p>즉 인접한 쇠 막대기가 있냐 없냐로 result를 증가시켜야 한다. </p>
<ul>
<li>그럼 인접한 쇠 막대기가 있다는걸 코드로 어떻게 알 수 있을까? <ul>
<li>위 사진에서 제일 작은 쇠막대기가 +1이 될 때, 그 밑에 있는 친구들은 카운트되면 안된다. 아직 안 잘렸으니까! 이 차이에서 어떻게 알 수 있는지 잡아보자. <ul>
<li>밑까지 전부 잘리는 경우, 즉 인접한 쇠막대가 없는 경우는 여는 괄호와 닫는 괄호가 바로 이어서 나오는 경우다. </li>
<li>밑은 안 잘리는 경우, 즉 인접한 쇠막대가 있는 경우는 여는 괄호와 닫는 괄호의 사이가 떨어져 있는 경우다. </li>
</ul>
</li>
</ul>
</li>
<li>하지만 여는 괄호와 닫는 괄호가 연달아 나오는지 알기 위해선 인덱스가 필요하다. <ul>
<li>어차피 스택에 괄호를 넣어봤자 쓸모가 없으니 (닫는 괄호 나올 때 pop하는 용도로 썼으니 안에 뭐가 들어가도 상관없음) 인덱스를 넣어서 풀어보자. <pre><code>for (int i=0; i&lt;str.length(); i++) {
if (str.charAt(i) == &#39;(&#39;) stack.push(i);
else {
  if (i-1 == stack.pop()) result += stack.size();
  else result += 1;
}
}</code></pre></li>
</ul>
</li>
</ul>
</li>
<li><p>맞았당22
<img src="https://velog.velcdn.com/images/ej_shin/post/bcf189fe-aa23-4870-9f76-9df41f6b9bc4/image.png" alt=""></p>
</li>
</ul>
<h4 id="해설-1">해설</h4>
<ul>
<li>옹 같은 방식으로 인덱스로 접근했다! ^____^ 햅비~ </li>
</ul>
<br>

<h3 id="문제-예제-3-백준-1406-에디터">문제 예제 3 (백준 1406 에디터)</h3>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/9012">https://www.acmicpc.net/problem/9012</a> </p>
</blockquote>
<h4 id="내-풀이-2">내 풀이</h4>
<ul>
<li>요건 예전에 풀었던 문제라 생략! </li>
<li>스택을 이용해 풀었고, 커서 왼쪽과 오른쪽을 구분해서 명령어마다 push, pop 작업을 수행하도록 구현했었다. </li>
</ul>
<h4 id="해설-2">해설</h4>
<ul>
<li><p><strong>문제 N 제한이 제일 중요</strong>하다! </p>
<ul>
<li>조건에 따라, 문자열의 길이는 최대 60만이 될 수 있다.</li>
<li>단순 문자열로 풀면 L은 O(1), D는 O(1), B는 O(N), P는 O(N)으로 O(N^2)가 나와 시간 초과가 뜰 것</li>
</ul>
</li>
<li><p>따라서 좀 더 효율적인 방식으로 -&gt; 커서를 기준으로 왼쪽과 오른쪽으로 나눠서 풀자! </p>
<ul>
<li>왼쪽 오른쪽 번갈아 push, pop을 먹이면 모든 명령이 O(1)이 된다. </li>
<li>즉 O(n)이 되므로 문제 제한 시간에 딱 맞출 수 있게 된다. </li>
</ul>
<br>

</li>
</ul>
<p>옹 접근 자체는 맞았는데, 이렇게 시간 복잡도를 하나하나 따지진 않았던 것 같다. 솔직히 문제 분류에서 스택인거 보고 힌트 얻었던 것 같군 😇 조건과 시간 복잡도 따지는 연습을 해야겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GraphQL의 스키마와 타입]]></title>
            <link>https://velog.io/@ej_shin/GraphQL%EC%9D%98-%EC%8A%A4%ED%82%A4%EB%A7%88%EC%99%80-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@ej_shin/GraphQL%EC%9D%98-%EC%8A%A4%ED%82%A4%EB%A7%88%EC%99%80-%ED%83%80%EC%9E%85</guid>
            <pubDate>Tue, 09 Aug 2022 06:42:03 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/ce6c4dad-75a7-4014-b283-29348a59fc24/image.gif" alt=""></p>
<h2 id="스키마--타입">스키마 &amp; 타입</h2>
<h3 id="객체-타입과-필드">객체 타입과 필드</h3>
<blockquote>
<p>스키마의 대부분은 객체 타입이다! 
서비스에서 가져올 수 있는 객체의 종류와 그 객체의 필드를 표현한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/c775e579-23a6-49c2-b059-9a7a22a297d2/image.png" alt=""></p>
<ul>
<li>GraphQL 스키마 언어 예시 <ul>
<li><code>Character</code> : GraphQL 객체 타입 </li>
<li><code>name</code>, <code>apperasIn</code> : Character의 필드 </li>
<li><code>String</code> : 내장 스칼라 타입 -&gt; 스칼라 객체로 해석</li>
<li><code>!</code> : 해당 필드가 non-null임을 표현</li>
<li><code>[Episode]</code> : Episode 객체의 배열(array)임을 표현 </li>
<li><code>length(unit: LengthUnit = METER)</code> : 인자 <ul>
<li>필수 혹은 옵셔널 </li>
<li>옵셔널일 경우, 디폴트 값 정의 가능</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="쿼리-타입--뮤테이션-타입">쿼리 타입 &amp; 뮤테이션 타입</h3>
<blockquote>
<p>스키마에 대한 <code>진입점 (Entry point)</code> 정의 
위 특이점 제외, 나머지는 객체 타입과 정확히 동일한 방식으로 작동! </p>
</blockquote>
<pre><code>// 쿼리 
query {
  hero {
    name
  }
  droid(id: &quot;2000&quot;) {
    name
  }
}

// GraphQL 서비스
type Query {
  hero(episode: Episode): Character
  droid(id: ID!): Droid
}</code></pre><br>

<h3 id="스칼라-타입">스칼라 타입</h3>
<blockquote>
<p>하위 필드가 없는 쿼리의 끝 </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/6a3d985f-286b-41ff-80bf-d12adb5caabd/image.png" alt=""></p>
<ul>
<li>위 쿼리에서 <code>name</code>, <code>appearIn</code>은 스칼라 타입이다. <ul>
<li><code>Int</code> : 부호가 있는 32비트 정수</li>
<li><code>Float</code> : 부호가 있는 부동소수점 값</li>
<li><code>String</code>: UTF-8 문자열</li>
<li><code>Boolean</code> : true 혹은 false </li>
<li><code>ID</code> : 고유 식별자</li>
</ul>
</li>
</ul>
<br>

<h3 id="열거형-타입-enums">열거형 타입 (Enums)</h3>
<blockquote>
<p>특정 값들로 제한되는 특별한 종류의 스칼라 </p>
</blockquote>
<ul>
<li><p>이런 작업을 할 수 있어요! </p>
<ul>
<li>타입의 인자가 <code>허용된 값 중 하나</code>임을 검증</li>
<li>필드가 <code>항상 값의 열거형 집합 중 하나</code>가 될 것임을 타입 시스템을 이용해 소통</li>
</ul>
</li>
<li><p>이렇게 생겼어요 
<img src="https://velog.velcdn.com/images/ej_shin/post/da5b9a53-e2c1-4545-ad21-c47b0244d605/image.png" alt=""></p>
</li>
</ul>
<br>

<h3 id="리스트와-non-null">리스트와 Non-Null</h3>
<blockquote>
<p>스키마의 다른 타입, 또는 쿼리 변수 선언에서 타입을 활용하면 값의 유효성 검사를 할 수 있는 타입 수정자 (type modifiers)를 적용할 수 있다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/0e19d64b-4171-468e-a89e-497dc19d0756/image.png" alt=""></p>
<ul>
<li><code>!</code>를 이용해 Non-Null로 표시 :: null값이 발생하면 GraphQL 실행 오류 발생 -&gt; 유효성 검사 오류 반환</li>
<li><code>[]</code> 를 이용해 리스트로 표시 :: 해당 타입의 배열을 반환. 배열이 필요한 인자에 대해 유효성 검사 작동 </li>
</ul>
<blockquote>
<h4 id="👾-요따구로-섞어-쓸-수도-있답니다">👾 요따구로 섞어 쓸 수도 있답니다~~</h4>
</blockquote>
<ul>
<li><code>myField: [String!]</code> </li>
<li>리스트 자체는 null일 수 있지만, element는 null일 수 없다. <ul>
<li><code>myField: null // valid</code></li>
<li><code>myField: [] // valid</code></li>
<li><code>myField: [&#39;a&#39;, &#39;b&#39;] // valid</code></li>
<li><code>myField: [&#39;a&#39;, null, &#39;b&#39;] // error</code></li>
</ul>
</li>
</ul>
<br>

<h3 id="인터페이스">인터페이스</h3>
<blockquote>
<p>뭔가를 구현하기 위해 타입이 포함해야 하는 특정 필드들을 포함하는 추상 타입</p>
</blockquote>
<pre><code>// 인터페이스 
interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}

// 인터페이스를 구현한 타입
type Human implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  starships: [Starship]
  totalCredits: Int
}

type Droid implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  primaryFunction: String
}</code></pre><br>

<h3 id="유니온-타입">유니온 타입</h3>
<blockquote>
<p>인터페이스와 유사하지만, 타입 간 공통 필드를 특정하지 않는다. </p>
</blockquote>
<ul>
<li>유니온 타입의 멤버는 <strong>구체적인 객체</strong>여야 한다. </li>
<li>인터페이스 혹은 유니온 타입에서 <strong>다른 유니온 타입을 사용할 수 없다</strong>. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/afd5bf8f-1af3-4e0f-b03f-98d2ae62bbe7/image.png" alt=""></p>
<ul>
<li>위 예시에서 <code>SearchResult</code>를 반환할 때마다 <code>Human</code>, <code>Droid</code>, <code>Startship</code> 를 얻을 수 있다. </li>
<li><code>SearchResult</code>를 반환하는 필드를 쿼리하려면, 어떤 필드라도 쿼리할 수 있도록 <code>조건부 프래그먼트</code>를 활용해야 한다. <pre><code>{
  search(text: &quot;an&quot;) {
    ... on Human {
      name
      height
    }
    ... on Droid {
      name
      primaryFunction
    }
    ... on Starship {
      name
      length
    }
  }
}</code></pre></li>
</ul>
<br>

<h3 id="입력-타입">입력 타입</h3>
<blockquote>
<p>뮤테이션에서 생성될 전체 객체를 전달할 때 사용! 
일반 객체 타입과 동일하지만, type 대신 input을 사용한다. </p>
</blockquote>
<ul>
<li>입력 객체 타입의 입력란은 입력 객체 타입을 참조할 수 있다. </li>
<li>입력 및 출력 타입을 스키마에 혼합할 수는 없다. </li>
<li>필드에 인자를 가질 수 없다. </li>
</ul>
<pre><code>/// input 객체
input ReviewInput {
  stars: Int!
  commentary: String
}

// 뮤테이션
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

// variables
{
  &quot;ep&quot;: &quot;JEDI&quot;,
  &quot;review&quot;: {
    &quot;stars&quot;: 5,
    &quot;commentary&quot;: &quot;This is a great movie!&quot;
  }
}

// 결과 
{
  &quot;data&quot;: {
    &quot;createReview&quot;: {
      &quot;stars&quot;: 5,
      &quot;commentary&quot;: &quot;This is a great movie!&quot;
    }
  }
}</code></pre><br>

<hr>
<h2 id="레퍼런스">레퍼런스</h2>
<ul>
<li><a href="https://graphql-kr.github.io/learn/">공식문서</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[GraphQL의 구조]]></title>
            <link>https://velog.io/@ej_shin/GraphQL-%EC%8C%88%EC%8B%B8%EB%A8%B9%EA%B8%B0-2</link>
            <guid>https://velog.io/@ej_shin/GraphQL-%EC%8C%88%EC%8B%B8%EB%A8%B9%EA%B8%B0-2</guid>
            <pubDate>Tue, 09 Aug 2022 06:05:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/f7d12a05-b043-436f-8653-463f85d6c744/image.gif" alt=""></p>
<h2 id="구조">구조</h2>
<h3 id="query">Query</h3>
<ul>
<li>데이터 조회(fetch) 역할</li>
<li><code>read</code> 수행</li>
</ul>
<pre><code>// 쿼리 
{
  hero {
    name
    friends {
      name
    }
  }
}

// 결과 
{
  &quot;data&quot;: {
    &quot;hero&quot;: {
      &quot;name&quot;: &quot;R2-D2&quot;,
      &quot;friends&quot;: [
        {
          &quot;name&quot;: &quot;Luke Skywalker&quot;
        },
        {
          &quot;name&quot;: &quot;Han Solo&quot;
        },
      ]
    }
  }
}</code></pre><blockquote>
<h4 id="🔨-프래그먼트">🔨 프래그먼트</h4>
</blockquote>
<ul>
<li><strong>복잡한 데이터 요구사항을 작은 단위로 분할</strong>하기 위해 사용하는 재사용 가능한 단위 </li>
<li>프래그먼트를 이용, 필드셋을 구성해 쿼리에 포함시킬 수 있다. </li>
<li>청크가 다른 여러 UI 구성 요소를 하나의 데이터 fetch로 통합할 때 많이 사용! </li>
</ul>
<blockquote>
<h4 id="🛠-지시어">🛠 지시어</h4>
</blockquote>
<ul>
<li>쿼리 구조와 형태를 <strong>동적으로 변경하기 위해</strong> 사용한다. </li>
<li>필드나 프래그먼트 안에 삽입될 수 있다. </li>
<li>종류 <ul>
<li><strong>@include(if: Boolean)</strong> : 인자가 true인 경우에만 결과에 필드 포함</li>
<li><strong>@skip(if: Boolean)</strong> : 인자가 true인 경우에만 이 필드 건너뜀</li>
</ul>
</li>
</ul>
<br> 

<h3 id="mutation">Mutation</h3>
<ul>
<li>데이터 변조 역할</li>
<li><code>create</code>, <code>update</code>, <code>delete</code> 수행 </li>
<li>쿼리도 데이터를 수정할 수 있지만, <strong>변조 작업은 뮤테이션을 통해 전송</strong>되어야 한다는 규칙을 지키는게 좋다! </li>
</ul>
<blockquote>
<h4 id="🎃-다중-필드">🎃 다중 필드</h4>
</blockquote>
<ul>
<li>쿼리 필드는 병렬로 실행되지만, 뮤테이션 필드는 <strong>하나씩 차례로</strong> 실행된다. </li>
</ul>
<pre><code>mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

// variables
{
  &quot;ep&quot;: &quot;JEDI&quot;,
  &quot;review&quot;: {
    &quot;stars&quot;: 5,
    &quot;commentary&quot;: &quot;This is a great movie!&quot;
  }
}

// 결과 
{
  &quot;data&quot;: {
    &quot;createReview&quot;: {
      &quot;stars&quot;: 5,
      &quot;commentary&quot;: &quot;This is a great movie!&quot;
    }
  }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[GraphQL 이란?]]></title>
            <link>https://velog.io/@ej_shin/GraphQL-%EC%8C%88%EC%8B%B8%EB%A8%B9%EA%B8%B0-1</link>
            <guid>https://velog.io/@ej_shin/GraphQL-%EC%8C%88%EC%8B%B8%EB%A8%B9%EA%B8%B0-1</guid>
            <pubDate>Tue, 09 Aug 2022 06:04:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ej_shin/post/bca9a05d-d331-46ef-988d-9a18f7d32067/image.gif" alt=""></p>
<h2 id="graphql">GraphQL</h2>
<blockquote>
<p>웹 클라이언트가 서버로부터 데이터를 효율적으로 가져오는 것을 목적으로 설계된 쿼리 언어. </p>
</blockquote>
<ul>
<li>데이터베이스나 플랫폼에 종속적이지 않음 </li>
<li>네트워크 통신 방식에 종속적이지 않음 <ul>
<li>HTTP POST 메소드와 웹 소켓 프로토콜을 활용 </li>
</ul>
</li>
</ul>
<h3 id="rest-api와-비교">REST API와 비교</h3>
<p><img src="https://velog.velcdn.com/images/ej_shin/post/2c51c79c-f70c-4b03-b0c9-30597d3e2350/image.png" alt=""></p>
<table>
<thead>
<tr>
<th></th>
<th>REST API</th>
<th>GraphQL API</th>
</tr>
</thead>
<tbody><tr>
<td>Method</td>
<td>GET, POST, PUT, PATCH, DELETE 등</td>
<td>POST</td>
</tr>
<tr>
<td>Endpoint</td>
<td>필요에 따라 여러 개 존재</td>
<td>단 하나만 존재</td>
</tr>
<tr>
<td>데이터 형태</td>
<td>서버단에서 ORM, 쿼리 등을 이용해 결정</td>
<td>클라이언트단에서 쿼리 조합하여 결정</td>
</tr>
<tr>
<td>쿼리 조합</td>
<td>endpoint마다 달라짐</td>
<td>스키마 타입에 따라 달라짐</td>
</tr>
<tr>
<td>네트워크 호출</td>
<td>endpoint마다 호출</td>
<td>딱 한번의 호출로 여러 데이터 요청 처리 가능</td>
</tr>
<tr>
<td>요청 처리</td>
<td>endpoint에 정의된 핸들링 함수 호출하여 처리</td>
<td>요청받은 필드에 대한 resolver 호출하여 처리</td>
</tr>
</tbody></table>
<br>

<h3 id="장점">장점</h3>
<ul>
<li><code>OverFetching</code><ul>
<li>필요한 정보들만 들고 올 수 있음 </li>
<li>따라서 통신 리소스 감소 </li>
</ul>
</li>
<li><code>UnderFetching</code> <ul>
<li>필요한 여러 계층의 정보들을 한 번에 가져올 수 있음</li>
<li>요청 횟수 감소</li>
<li>필요 데이터가 쪼꼼 달라졌다고 새로운 REST API 만들지 않아도 됨..! </li>
</ul>
</li>
<li>하나의 <code>POST End-point</code>에서 모든 요청 처리 가능 <ul>
<li>힘들게 API 문서를 관리하지 않아도 됨! </li>
</ul>
</li>
<li>프론트엔드 개발자와 백엔드 개발자의 <code>결합도가 떨어짐</code> <ul>
<li>백엔드가 모델 만들어서 GraphQL로 노출하면, 프론트엔드 개발자가 니즈에 따라 바로 활용 가능</li>
</ul>
</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>맵 / 테이블 / 딕셔너리 지원 X </li>
<li><strong>API 버전 관리에 대한 명확한 방법이 없음</strong></li>
<li>디도스 공격을 방어하기 위해 직접 코드를 짜야 함 <ul>
<li>리턴되는 데이터 양에 레이트 리밋 걸기, 페이지네이션 직접 구현 등 </li>
</ul>
</li>
</ul>
<br>


<hr>
<h2 id="레퍼런스">레퍼런스</h2>
<ul>
<li><a href="https://graphql-kr.github.io/learn/">공식문서</a></li>
<li><a href="https://www.youtube.com/watch?v=9BIXcXHsj0A">얄팍한 GraphQL &amp; Apollo 강좌</a></li>
<li><a href="https://news.hada.io/topic?id=7145&amp;utm_source=slack&amp;utm_medium=bot&amp;utm_campaign=T02BYD5Q9TL">GraphQL은 쫌 짜증나요</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>