<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bahar-j.log</title>
        <link>https://velog.io/</link>
        <description>📝 dev wiki </description>
        <lastBuildDate>Tue, 05 Sep 2023 08:27:02 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bahar-j.log</title>
            <url>https://images.velog.io/images/bahar-j/profile/9d207dfd-273d-4312-987a-d352bedd9ff7/KakaoTalk_Photo_2021-10-28-17-22-04.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bahar-j.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bahar-j" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[GSLB와 SLB]]></title>
            <link>https://velog.io/@bahar-j/GSLB%EC%99%80-SLB</link>
            <guid>https://velog.io/@bahar-j/GSLB%EC%99%80-SLB</guid>
            <pubDate>Tue, 05 Sep 2023 08:27:02 GMT</pubDate>
            <description><![CDATA[<h3 id="slb-server-load-balancing">SLB (Server Load Balancing)</h3>
<p>스위치(허브)가 아닌 서버 레벨에서의 로드 밸런싱(부하 분산). <code>DNAT(Destination Network Address Translation)</code> 방식을 이용해 하나의 VIP에 여러 서버들이 붙어있을 수 있음
예를 들어, nginx g/w의 vip는 하나이지만 실제 서버 및 ip는 수십개인 경우. 해당 VIP로 들어온 요청에 대해 로드밸런서가 하나의 nginx g/w로 요청을 전달해줌</p>
<h3 id="gslb-global-server-load-balancing">GSLB (Global Server Load Balancing)</h3>
<p>같은 역할을 하는 서버들이 같은 네트워크망 / 데이터 센터 / 지역에 배치되어 있지 않은 경우 
예를 들어, VIP 자체가 여러개인 경우(nginx의 리전 별로 VIP가 다르다던지..). GSLB를 통해 어떤 VIP로 요청을 전달해야할지 결정함.</p>
<hr>
cf)

<h4 id="dsr-direct-server-routing">DSR (Direct Server Routing)</h4>
<p>서버로 요청이 들어올 때는 로드 밸런서를 거치지만, 서버에서 요청이 클라이언트로 나갈때는 로드 밸런서를 거치지 않는 방식. 로드 밸런서의 부하를 줄여줌.</p>
<hr>
참고 자료

<p><a href="https://www.stevenjlee.net/2020/06/30/%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EB%B6%80%ED%95%98%EB%B6%84%EC%82%B0-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-load-balancing-%EA%B7%B8/">https://www.stevenjlee.net/2020/06/30/%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EB%B6%80%ED%95%98%EB%B6%84%EC%82%B0-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-load-balancing-%EA%B7%B8/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RBAC과 ABAC]]></title>
            <link>https://velog.io/@bahar-j/RBAC%EA%B3%BC-ABAC</link>
            <guid>https://velog.io/@bahar-j/RBAC%EA%B3%BC-ABAC</guid>
            <pubDate>Tue, 05 Sep 2023 01:41:41 GMT</pubDate>
            <description><![CDATA[<h4 id="rbacrole-based-access-control-역할-기반-접근-제어">RBAC(Role Based Access Control, 역할 기반 접근 제어)</h4>
<p>: 말 그대로 역할에 따라 리소스에 접근할 수 있는지 여부를 결정 (ex. 상품 관리자 / 채널 관리자 / 일반 유저..)</p>
<h4 id="abacattribute-based-access-control-속성-기반-접근-제어">ABAC(Attribute Based Access Control, 속성 기반 접근 제어)</h4>
<p>: 사용자(역할일 수 있음) / 리소스 속성(유형, 만든 사람, 중요도 등) / 환경(액세스 위치, 날짜 등)과 같이 보다 다양한 속성을 기반으로 접근 제어</p>
<p>RBAC은 단순하고 구현하기 쉽지만, 시스템이 복잡해지다보면 역할 폭발(역할이 수백, 수천개로 늘어나는..)의 문제가 발생할 수 있음
반면 ABAC은 구현이 까다롭고 규칙 구성이 어렵지만, 높은 수준의 제어가 가능함.</p>
<hr>

<p>참고 자료
<a href="https://www.okta.com/kr/identity-101/role-based-access-control-vs-attribute-based-access-control/">https://www.okta.com/kr/identity-101/role-based-access-control-vs-attribute-based-access-control/</a>
<a href="https://www.cloudflare.com/ko-kr/learning/access-management/role-based-access-control-rbac/">https://www.cloudflare.com/ko-kr/learning/access-management/role-based-access-control-rbac/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Annotated with @ConstructorBinding but defined as Spring component  해결]]></title>
            <link>https://velog.io/@bahar-j/Annotated-with-ConstructorBinding-but-defined-as-Spring-component-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@bahar-j/Annotated-with-ConstructorBinding-but-defined-as-Spring-component-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Thu, 24 Aug 2023 01:24:47 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">@Configuration
@ConfigurationProperties(&quot;example&quot;)
public class RedisConfig {
     @Setter
    private Map&lt;String, String&gt; property;

    private final ObjectMapper redisObjectMapper;

    public RedisConfig(ObjectMapper redisObjectMapper) {
        this.redisObjectMapper = redisObjectMapper;
    }
    ...
}</code></pre>
<p>위와 같은 방식으로 사용하던 클래스에서 스프링 부트 3.XX(+자바 17)로 업데이트 이후 오류가 발생했다. (빨간 줄과 함께 에러는 뜨지만 돌아는가는 케이스..)</p>
<pre><code class="language-java">Annotated with @ConstructorBinding but defined as Spring component </code></pre>
<p><a href="https://www.baeldung.com/configuration-properties-in-spring-boot#immutable-configurationproperties-binding">https://www.baeldung.com/configuration-properties-in-spring-boot#immutable-configurationproperties-binding</a>
<a href="https://docs.spring.io/spring-boot/docs/2.4.x/reference/html/spring-boot-features.html#boot-features-external-config-constructor-binding">https://docs.spring.io/spring-boot/docs/2.4.x/reference/html/spring-boot-features.html#boot-features-external-config-constructor-binding</a></p>
<p>알아본 바를 정리하면,..</p>
<ul>
<li><p>@ConfigurationProperties를 사용하면 기본적으로 ConstructorBinding을 사용하게 됨 (생성자를 이용한 바인딩)</p>
</li>
<li><p>@ConstructorBinding과 @Configuration이 같이 붙어있을 경우 다음과 같은 에러 발생
: Annotated with @ConstructorBinding but defined as Spring component </p>
</li>
</ul>
<p>-&gt; 그 이유는, @Configuration이 붙어있는 경우, 스프링은 이것을 빈으로 관리(생성자, 필드 등을 통해 DI를 해줌)
그러나 @ConstructorBinding이 붙어있는 경우, 이 클래스는 생성자를 통해 외부 property를 바인딩해줘야 함
따라서 스프링이 이 클래스를 어떻게 다뤄야할지가 모호해짐</p>
<p>=&gt; 따라서, @Configuration 등 스프링의 stereotype annotation이 붙은 클래스와 configuration property를 받아오기 위한 @ConfigurationProperties with @ConstructorBinding 어노테이션을 쓰는 클래스는 구분해줘야함 (그게 일반적인 사용 방법임)</p>
<p>수정된 코드는 아래와 같다.</p>
<pre><code class="language-java">@Configuration
@EnableConfigurationProperties(RedisConfig.RedisProperties.class)
public class RedisConfig {
    private final ObjectMapper redisObjectMapper;

    public RedisConfig(ObjectMapper redisObjectMapper) {
        this.redisObjectMapper = redisObjectMapper;
    }
    ...

    @Order(-1)
    @Bean(&quot;cacheRegionTemplateMap&quot;)
    public Map&lt;String, ReactiveRedisTemplate&lt;String, Object&gt;&gt; cacheRegionTemplateMap(RedisProperties redisProperties, DefaultListableBeanFactory factory, ClientResources clientResources) {
        Map&lt;String, ReactiveRedisTemplate&lt;String, Object&gt;&gt; cacheRegionTemplateMap = new HashMap&lt;&gt;();
        redisProperties.getController().keySet().forEach(region -&gt; {
            ...
        });

        return cacheRegionTemplateMap;
    }

    ...

    @ConfigurationProperties(&quot;example&quot;)
    public static class RedisProperties {
        private Map&lt;String, String&gt; property = new HashMap&lt;&gt;();

        public RedisProperties(Map&lt;String, String&gt; property) {
            this.property = property;
        }

        public Map&lt;String, String&gt; getProperty() {
            return property;
        }
    }
}
</code></pre>
<hr>

<p>@ConfigurationProperty의 코드 주석을 보면 아래와 같은 말이 있다.</p>
<blockquote>
<p>Annotation for externalized configuration. Add this to a class definition or a @Bean method in a @Configuration class if you want to bind and validate some external Properties (e.g. from a .properties file).
Binding is either performed by calling setters on the annotated class or, if @ConstructorBinding is in use, by binding to the constructor parameters.</p>
</blockquote>
<blockquote>
<p>외부 설정을 위한 어노테이션. 외부 property를 가져오고 싶으면 클래스 생성시에 이걸 붙이거나 @Configuration 클래스의 @Bean 메소드에 붙여라. 
바인딩은 어노테이션이 붙은 클래스의 setter를 통해 수행되거나, @ConstructorBinding이 붙어있으면 constructor를 이용해서 실행된다.</p>
</blockquote>
<p>아마 팀원 분이 이 주석을 보고 @Configuration 클래스에 붙이면 된다는 생각이 들어서 붙이지 않았을까 하는 생각이 든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TDD, 클린 코드] (6) 함수형 프로그래밍]]></title>
            <link>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-6-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-6-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Wed, 09 Aug 2023 00:51:14 GMT</pubDate>
            <description><![CDATA[<h4 id="프로그래밍-종류">프로그래밍 종류</h4>
<p>명령형 프로그래밍 - java
선언형 프로그래밍
함수형 프로그래밍 -&gt; 선언형 프로그래밍 스타일을 어느정도 익힐 수 있음</p>
<hr>

<h3 id="함수형-프로그래밍-장점">함수형 프로그래밍 장점</h3>
<ol>
<li>멀티 쓰레드, 멀티 cpu 환경에서 상태를 변경하지 않는 방식을 강조하는 함수형 프로그래밍의 이점 (&lt;-&gt; 객체지향)</li>
<li>데이터를 객체로 변환해서 다루는 비용 부담 -&gt; 대용량 데이터 환경에서</li>
<li>함수형 프로그래밍은 함수 단위 모듈화 &lt;-&gt; 객체 지향은 객체(메서드, 필드) 단위. 더 작은 단위 모듈화</li>
<li>더 빠른 작업 (객체는 설계가 더 오래 걸림. 메서드 분리보다 클래스 분리가 힘듦)</li>
<li>단순함으로의 복귀</li>
</ol>
<ul>
<li>함수형 프로그래밍은 인풋, 아웃풋이 항상 있음 =&gt; tdd에 더 유리한 부분</li>
</ul>
<h3 id="함수형-프로그래밍-특징">함수형 프로그래밍 특징</h3>
<ol>
<li>변경 불가능한 값을 활용 -&gt; 멀티 스레드 환경에서 이슈가 생길 가능성이 적어짐</li>
<li>1등시민으로서의 함수 -&gt; 함수가 메서드의 인자, 리턴값으로 사용될 수 있음</li>
<li>람다와 클로저 (람다: 익명함수)</li>
<li>고계 함수: 함수를 인자로 받고 함수를 리턴 가능</li>
<li><strong>사이드 이펙트가 없는 함수</strong> =&gt; 상태값 변경으로 인한 오류를 없앰</li>
</ol>
<ul>
<li>scheme,clojure 로 연습 가능</li>
</ul>
<hr>


<h3 id="함수형-프로그래밍에서-배우고-적용할-수-있는-부분">함수형 프로그래밍에서 배우고 적용할 수 있는 부분</h3>
<ul>
<li>기본틀은 oop 기반</li>
<li>메소드 내부 구현은 FP 지향</li>
<li>객체의 상태 관리는 불변 객체를 지향</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TDD, 클린 코드] (5) 책임 주도 설계]]></title>
            <link>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-5-%EC%B1%85%EC%9E%84-%EC%A3%BC%EB%8F%84-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-5-%EC%B1%85%EC%9E%84-%EC%A3%BC%EB%8F%84-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Wed, 09 Aug 2023 00:13:32 GMT</pubDate>
            <description><![CDATA[<p><strong>테스트 종류</strong></p>
<ol>
<li>도메인 객체 단위테스트</li>
<li>인수테스트</li>
<li>서비스 레이어 테스트</li>
</ol>
<p>=&gt; 계속 하고자 하는 부분은 서비스에 산재되어있던 비즈니스 로직을 연관된 도메인 객체로 옮기고 도메인 객체 단위테스트를 작성하는 것
=&gt; 이렇게 됐을 때 서비스 레이어의 mock 기반 테스트는 선택의 영역이 됨
=&gt; 필요 없는 테스트 코드는 그때그때 다 지우기 (유지보수 비용 줄이기)</p>
<p><strong>리팩토링</strong></p>
<ul>
<li>하루에 한시간 씩이라도 리팩토링하는 시간을 갖기</li>
<li>기능이 추가된다면 이때 리팩토링을 병행</li>
</ul>
<p><strong>클래스 분리, 인터페이스 추출의 기준</strong></p>
<ul>
<li>반복되는 부분은 부모 객체로 뽑기 (ex. AbstractEntity)</li>
<li>라이프 사이클이 같은 데이터들을 하나의 객체로 뽑기 (ex. questionBody)</li>
<li>비즈니스 로직이 복잡한 부분을 뽑아보고, 거기서 묶어보기</li>
<li>도메인 지식이 낮은 경우 필드들 중에 관련성이 있는 애들 뽑아보기</li>
</ul>
<hr>

<h3 id="테이블이-아닌-도메인-객체부터-설계하자">테이블이 아닌 도메인 객체부터 설계하자</h3>
<p>참고: <a href="https://www.youtube.com/watch?v=VjbBGjVRxfk">https://www.youtube.com/watch?v=VjbBGjVRxfk</a>
-&gt; 테이블, 쿼리, CRUD보다 비즈니스 로직에 집중
-&gt; 객체 설계로부터 시작하기 (이때는 테이블 구조 의식하지 않기)</p>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/2f69a22b-9269-4c16-9ac4-a40f2846c353/image.png" alt="">
-&gt; 테이블 없이 개발할때 이런 식으로 MockRepository를 만들어서 사용
-&gt; 단 실제 구현부(service) 쪽에서는 실제 TagRepository의 의존성을 갖기(그래야 이후 실제 repository로 변경하기 편함)
-&gt; 이처럼 db가 없는 상태에서도 충분히 비즈니스 로직 개발이 가능 
-&gt; 우선 객체 설계, 객체 간의 관계 맺는 것만 고민하며 개발 (객체가 특정 다른 객체의 list를 갖는 상태로 그냥 두기.)
-&gt; 비즈니스 로직 개발이 끝나면 그때 db를 고민 (pk, manyToOne, elementCollection, lazyLoading 등 객체지향적으로 짠 것과 db가 맞지 않는 부분을 수정)</p>
<ul>
<li><strong>이후 만들어진 n개 객체를 1개의 테이블로 맵핑</strong></li>
</ul>
<h4 id="도메인-객체-먼저-설계하는-것의-장점">도메인 객체 먼저 설계하는 것의 장점</h4>
<p>도메인 객체 설계를 먼저하고 리팩토링을 진행하면 db를 만들어놨을 때보다 리팩토링이 쉬워짐 (테이블 수정이 필요 없기 때문에)</p>
<h4 id="도메인-객체-먼저-설계하는-것의-어려움">도메인 객체 먼저 설계하는 것의 어려움</h4>
<ul>
<li>students가 <code>list&lt;nsUser&gt;</code>를 가지면 성능상의 이슈 
-&gt; 하다보면 그렇기 때문에 도메인 객체 설계시부터 다르게 해야겠네 하는 감이 생김 
-&gt; nsUserId, sessionId를 갖는 student를 설계 (session이 n개의 student를 가짐, 유저는 n개의 session을 가짐, 이렇게 두개가 맵핑되는 테이블(stuent)가 필요) 
-&gt; 단 ORM을 쓴다면 성능이슈가 없어서 student가 session, nsUser 자체를 가져도 됨. students는 <code>list&lt;student&gt;</code>를 가짐</li>
<li>pk, fk, 저장 시간 등 추가되어야할 칼럼 고려해야함</li>
<li>성능 때문에 역정규화가 필요한 경우도 있음</li>
</ul>
<h4 id="예제">예제</h4>
<p>DB 테이블 : Session, NsUser, Student<br>도메인 객체 : Session, NsUser, Student, Enrollment</p>
<p>특정 필드 ex Enrollment가 db 테이블이 아닌 경우에는,
얘를 필드로 갖지 않고,
메서드에서 걔를 new Enrollment(..)로 생성하여 사용할수도 있음
(맵핑과 비즈니스 로직을 분리. mybatis를 쓰면 이게 더 나을 수 있음)</p>
<p>jpa에서는 <code>session</code> - <code>enrollment</code> - <code>students</code> - <code>list&lt;student&gt;</code> 이렇게 되어있으면 자동으로 로딩을 해줌. 구조 변경 필요 없음
근데 마이바티스에서는 불가능. (어려운 부분이 많긴함.)
그러면 Session의 메서드에 students를 전달해줘서 강제로 맵핑해줄 수 있음. -&gt; jpa 쓰면 따로 조회해서 넘겨주지 않아도 자동으로 연결이 됨 (레이지로딩)</p>
<hr>

<h3 id="도메인-객체와-db-맵핑-객체-entity">도메인 객체와 DB 맵핑 객체 (entity)</h3>
<p>보통 db 맵핑 객체, 도메인 객체를 둘다 도메인객체라고 부르긴 하는데 도메인 객체와 entity는 엄밀히 따지면 다른 용어로, 두개의 쓰임을 구분할 수 있음 (이 경우 딱 떨어지는 정답은 없음)
-&gt; 도메인 객체를 DB 맵핑 객체로 쓰지 않는 걸 추천
-&gt; 분리하지 않는 경우 중복이 많이 생기긴 함.
-&gt; jpa 쓰면 분리하지 않아도 가능하긴 함(실용주의 노선. jpa는 레이지로딩이 되기 때문에 <code>list&lt;object&gt;</code>를 가지더라도 쓰지 않는 데이터 ex 학생 목록 를 로딩하지 않음.)
-&gt; 비즈니스 로직도 가지면서 디비 맵핑도 되는 식으로 도메인 객체랑 db와 맵핑되는 객체를 일반적으로는 혼용해서 많이씀 (상황에 따라 적절히 섞어쓰기)</p>
<p>=&gt; DB가 아닌 다른 서비스에서 데이터를 받는 경우에도, 다른 msa에서 데이터를 받는 객체랑 비즈니스 로직이 있는 우리쪽 도메인 객체를 분리해서 구현하기</p>
<h4 id="도메인-객체에서는-db-접근하지-않기">도메인 객체에서는 DB 접근하지 않기</h4>
<ul>
<li>기존에는 service에서 repository 접근을 하는데, 도메인 객체로 역할이 분담되다보면(ex. enroll) 이런 고민이 생김</li>
<li>대신 enroll이 바뀐 student를 반환하고, service에서 db 접근이 좋다고 생각</li>
</ul>
<p>도메인 객체에서는 객체 저장, 조회 등 db 접근은 직접하지 않는 것을 추천 -&gt; 이렇게 되면 단위 테스트, 설계가 더 어려워짐</p>
<hr>

<h3 id="out---in-vs-in---out">out -&gt; in vs. in -&gt; out</h3>
<p><code>out -&gt; in</code>
controller 쪽부터 개발. 일반적으로 많이 사용</p>
<p><code>in -&gt; out</code>
tdd에 적합한 방식. 핵심이 되는 도메인 객체부터 tdd로 개발.
개발하면서 아닌 것 같으면 버리고 다른 걸로 다시 도전</p>
<hr>

<h3 id="top-down-vs-bottom-up">top down vs. bottom up</h3>
<p><code>top down</code>
인터페이스 등을 완벽히 설계 후 구현 : 책임 주도 설계</p>
<blockquote>
<p>객체의 책임?
객체가 무엇을 알고, 무엇을 할 수 있는지 (field &amp; method)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/87b82915-5f83-4b97-bbbd-8c76b7f3441b/image.png" alt=""></p>
<blockquote>
<p>책임 주도 설계
책임을 먼저 찾고 그걸 어떤 객체가 맡을지 할당하는 방식</p>
</blockquote>
<p><code>bottom up</code>
일단 구현하고 지속적으로 리팩토링 (토비님 강의에 좀 더 가까움)</p>
<p>=&gt; 처음에는 구현 위주(bottom up) 방식으로 개발해보고,
이후 익숙해지면 인터페이스를 먼저 고민하는 식(책임 주도 설계, top down)으로도 구현해보기
=&gt; 단 처음부터 너무 완벽한 설계를 하려는 욕심은 버리자
=&gt; 한가지 방식에만 매몰되지 않고, 여러 방식을 도전하며 조금씩 개선된 설계를 해보기</p>
<ul>
<li>일정 부분 설계를 하고 도메인 객체도 만들어본 다음 테스트 코드가 따라가는 것도 괜찮다고 생각</li>
<li>그러나 지속적인 리팩토링하려면 테스트코드가 밑바탕이 되어야하니까 결국 테스트 코드는 필요</li>
</ul>
<hr>

<h3 id="책임-주도-설계">책임 주도 설계</h3>
<ol>
<li><p>시스템의 책임을 파악, 시스템 책임을 더 작은 책임으로 나눔
<img src="https://velog.velcdn.com/images/bahar-j/post/8fc8509b-6b64-4d88-847c-78a9c28731dc/image.png" alt=""></p>
</li>
<li><p>책임을 어떤 객체에 할당할지 결정
<img src="https://velog.velcdn.com/images/bahar-j/post/ebf09add-eb7b-4384-815a-700f4b9785b6/image.png" alt=""></p>
</li>
<li><p>객체들을 협력하게 만듦
<img src="https://velog.velcdn.com/images/bahar-j/post/88feb886-12e4-4666-9835-28eb4a873752/image.png" alt=""></p>
</li>
</ol>
<blockquote>
<p>역할 : 다른 것으로 교체할 수 있는 책임의 집합
연극에 비유하면,
연극 = 협력
배역 = 역할
배우 = 객체
어떤 역할은 여러 객체가 수행할 수 있음.</p>
</blockquote>
<p><strong>자동차 경주 게임 예시</strong>
<img src="https://velog.velcdn.com/images/bahar-j/post/9d16c369-9470-440d-b94b-a6b09ef957a1/image.png" alt="">
<img src="https://velog.velcdn.com/images/bahar-j/post/6776def6-807f-4aaf-844d-c3ec7e1af0cd/image.png" alt=""></p>
<p>이동 가능 여부 결정하는 역할</p>
<ul>
<li>random 값에 따라 이동 여부 결정</li>
<li>시간에 따라 이동 여부 결정
=&gt; 각각 strategy(객체)는 같은 역할(이동 가능 여부 결정)을 함</li>
</ul>
<p>=&gt; 공통의 역할을 잘 추출하면 이걸 실현하는 클래스는 다양하게 변경 가능하도록 설계 가능
=&gt; 역할 == 인터페이스</p>
<h4 id="컴파일-타임-의존성-vs-런타임-의존성">컴파일 타임 의존성 vs 런타임 의존성</h4>
<p>역할을 추출 했다면,
컴파일 타임 의존성이 아닌 런타임 의존성을 갖게 해야함</p>
<pre><code class="language-java">// 컴파일 타임 의존성
MovingStrategy strategy = new RandomValueMovingStrategy();

// 런타임 의존성
public void move(MovingStrategy strategy) {

}</code></pre>
<p>=&gt; 의존 관계를 외부에서 주입(Dependancy Injection)하기 때문에 런타임 의존성은 언제든지 바꿔치기 가능 
스프링에서는 빈 팩토리에서 서로 다른 객체를 연결해주는데(DI), 스프링이 없으면 이걸 직접 해주면 됨. </p>
<hr>

<h4 id="사다리-타기-예제">사다리 타기 예제</h4>
<p>객체가 가지는 메시지에 집중</p>
<p>로직과 input, output이 구분된다면?</p>
<pre><code class="language-java">public interface LadderCreator {
  Ladder create(int countOfPerson, int height);
}

public interface Ladder {
  PlayResults play();
  PlayResult play(int source);
}

public class PlayResult {
  private Position source;
  private Position target;
}

public interface Line {
  int move(int position);
}

// 실제 사람과 결과를 맵핑
public class Result Processor {
  private List&lt;Person&gt; people;
  private List&lt;Result&gt; result;
  LottoResults toResults(PlayResults results) {
  }
}</code></pre>
<p>=&gt; 이렇게 구현체 말고 뼈대만 먼저 설계를 함</p>
<p>=&gt; 인터페이스의 구현체를 만듦
=&gt; 그럼 나중에 성능이 떨어져서 바꾸고 싶을때 규격에 맞는 구현체만 바꿔주면 됨</p>
<hr>

<h3 id="cycliccircular-dependancy-해결-방법">cyclic(circular) dependancy 해결 방법</h3>
<p>=&gt; 인터페이스를 추출하다보면 인터페이스와 구현체 사이에 cyclic dependancy가 생기는 경우가 많음 (세개 패키지가 순환하는 경우도 cyclic)
<img src="https://velog.velcdn.com/images/bahar-j/post/120f6255-0a69-4c55-90a0-e4eb3976cc52/image.png" alt="">
=&gt; interface package(engine)와 구현체 package(nextstep) 간에 의존관계가 양방향이 돼버리는 경우, 중간에 둘을 연결해주는 또다른 패키지가 필요</p>
<pre><code class="language-java">public class LadderFactoryBean {
  public static LadderCreator createLadderFactory() {
    LineCreator lineCreator = new NextStepLineCreator();
    return new NextStepLadderCreator(lineCreator);
  }
}</code></pre>
<p>=&gt; 이게 원래 스프링 프레임워크의 DI 컨테이너에서 해주는 역할</p>
<ul>
<li><p>cyclic dependancy를 가지면 ddd, msa로 갈때 쪼개는게 힘들어짐 (sonarqube 같은 정적 분석도구를 이용해서 찾고 해결하기. ci에 연결해서 매일 새벽에 리포팅 하는 방식도 좋음)</p>
</li>
<li><p>인스턴스 변수를 10개까지 줄이자</p>
</li>
<li><p>강의 후 오브젝트 읽으면서 적용해보기(특히 3장)</p>
</li>
<li><p>나중에 TDD를 익히고 책도 읽고 적용하며 역량을 내재화한 후에 ATDD나 DDD 듣기</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TDD, 클린 코드] (4) 인터페이스 분리]]></title>
            <link>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%B6%84%EB%A6%AC</link>
            <guid>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%B6%84%EB%A6%AC</guid>
            <pubDate>Sun, 21 May 2023 08:51:40 GMT</pubDate>
            <description><![CDATA[<h4 id="클래스-분리-후-클래스간-의존관계를-어떻게-연결할까">클래스 분리 후 클래스간 의존관계를 어떻게 연결할까?</h4>
<blockquote>
<ol>
<li>상속 관계 (Is - a 관계)</li>
<li>조합 관계(has - a 관계)</li>
</ol>
</blockquote>
<p>-&gt; 둘 다 가능한 경우 조합을 먼저 트라이 해보는게 좋음.
코드의 재사용성 측면에서는 상속이 유리하지만, 유연성 측면에서는 조합이 더 유리.
조합도 코드를 재사용하는 거긴함 (좀 더 코드가 많아진다는 단점)
<strong>가장 상위의 메소드 시그니처 등이 변경되었을 때 상속의 경우 파급 효과 때문에 유지보수가 힘들어짐.</strong></p>
<br>

<p>ex)</p>
<pre><code class="language-java">public class WinningLotto extends ArrayList&lt;Lotto&gt; {

}</code></pre>
<p>-&gt; 상속 관계 
-&gt; ArrayList의 메소드들을 다 외부에 노출하는 것. (add, remove..)
-&gt; arrayList의 기능을 확장하는게 아니기 때문에 적합하지 않음
-&gt; 일단은 조합을 먼저 고려하기</p>
<hr>

<h4 id="클래스가-서로-의존한다는-건">클래스가 서로 의존한다는 건..</h4>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/ab827833-1ce3-4c11-a73f-f8b41760e329/image.png" alt="">
<a href="https://velog.io/@cjh8746/%EA%B0%9D%EC%B2%B4%EC%99%80-%EC%9D%98%EC%A1%B4%EC%84%B1">객체와 의존성</a></p>
<p>파라미터에 특정 클래스를 갖는 것도 의존 관계. 다만 인터페이스를 추출하고 실제 호출하는 쪽에서 구현 클래스를 지정하는 방식으로 루즈 커플링 가능.</p>
<hr>

<h3 id="인터페이스-분리">인터페이스 분리</h3>
<p>movable(수레, 기차 등을 예측), movingStrategy(움직이는 기준)
-&gt; 미래를 예측하기 어렵다. 그런데 디자인 패턴을 활용하면 멋있어보인다 ㅋㅋ
-&gt; 오버엔지니어링이 될 가능성. 유지보수가 오히려 어려워지는 경우도 있다.
=&gt; 따라서 새로운 요구사항이 들어왔을 때 빠르게 리팩토링하는 게 더 좋을 수도 있다(처음부터 완벽한 설계를 하고자 하면 이후에 바꾸기가 아까워질 수 있기 때문에..)</p>
<h4 id="인터페이스를-잘-찾으려면">인터페이스를 잘 찾으려면..</h4>
<ul>
<li>변화가 자주 발생하는 부분을 찾아야함(서비스에 대한 이해도가 높아야함)</li>
<li>변화가 자주 발생하는 부분(인터페이스 + 구현 클래스)과 발생하지 않는 부분(구현 클래스)을 분리하는 센스가 필요.</li>
</ul>
<h4 id="인터페이스-추출의-양적-기준">인터페이스 추출의 양적 기준</h4>
<ul>
<li>요구사항이 변화될때마다 if else가 추가되는 부분. </li>
<li>메서드 하나가 길어지는 부분.</li>
</ul>
<h4 id="예제">예제</h4>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/abd8b50c-7164-4f7d-a877-41978326f261/image.png" alt="">-&gt; Configuration 클래스에서 operator들이 들어있는 map을 빈으로 만들어서 쓸 수도 있다 </p>
<br>
<br>


<p><img src="https://velog.velcdn.com/images/bahar-j/post/e008e959-b346-4d32-9a18-eae24e5cfa1b/image.png" alt="">-&gt; map 대신 enum으로 전환</p>
<br>
<br>

<p><img src="https://velog.velcdn.com/images/bahar-j/post/f3b497be-3f26-4e3f-ac12-402f0a2910cd/image.png" alt=""> -&gt; 조건이 단순히 string에 따른 구분보다 복잡해지는 경우, 어떤 조건을 만족하는지 확인하는 메서드를 인터페이스에 추가할 수 있다.
<img src="https://velog.velcdn.com/images/bahar-j/post/c0786704-82c2-4d24-b1c6-abf3187d8ad2/image.png" alt=""> -&gt; 이런식으로 활용</p>
<br>
<br>

<blockquote>
<p>실제로 스프링의 인터페이스를 뜯어보면 쌍으로 다니는 애들이 많다 -&gt; 조건을 만족하면 얘를 실행해라..</p>
</blockquote>
<hr>

<h4 id="인터페이스의-구현-클래스들에-중복-코드가-많이-생긴다면-인터페이스-구현클래스-사이에-추상클래스를-놓아보자">인터페이스의 구현 클래스들에 중복 코드가 많이 생긴다면, 인터페이스-구현클래스 사이에 추상클래스를 놓아보자</h4>
<pre><code class="language-java">// 구현 클래스마다 겹치는 메서드 1
public int size() {
     return entrySet().size();
}
// 구현 클래스마다 겹치는 메서드 2
public boolean isEmpty() {
    return size() == 0;
}
// 구현 클래스마다 겹치는 메서드 3
public boolean containsValue(Object value) {
    Iterator&lt;Entry&lt;K,V&gt;&gt; i = entrySet().iterator();
    if (value==null) {
        while (i.hasNext()) {
            Entry&lt;K,V&gt; e = i.next();
            if (e.getValue()==null) return true;
        }
    } else {
      while (i.hasNext()) {
         Entry&lt;K,V&gt; e = i.next();
            if (value.equals(e.getValue())) return true;
       }
    }
    return false;
}
// 달라지는 부분 -&gt; abstract로 정의
public abstract Set&lt;Entry&lt;K,V&gt;&gt; entrySet();</code></pre>
<hr>

<h4 id="cf-마커-인터페이스">cf. 마커 인터페이스</h4>
<pre><code class="language-java">/**
 * Marker interface that indicates that a {@link WebExceptionHandler} is used to render
 * errors.
 *
 * @author Brian Clozel
 * @since 2.0.0
 */
@FunctionalInterface
public interface ErrorWebExceptionHandler extends WebExceptionHandler {

}</code></pre>
<p>-&gt; 아무 역할도 안하는 마커 인터페이스도 있음
-&gt; 나중에 이 인터페이스를 통해 어떤 클래스가 이 클래스를 implementation 했는지 안했는지 체크 후 동작.
-&gt; 혹은, WebExceptionHandler가 좀 더 추상적인 개념이고 그 사이에 한 단계 구체화된 개념을 놓고 싶을때도 사용하는 듯. (위 예시의 케이스..)</p>
<h4 id="cf-커스텀-어노테이션">cf. 커스텀 어노테이션</h4>
<pre><code class="language-java">@interface
public class Example {

}</code></pre>
<pre><code class="language-java">@Around(value = “@annotation(june.Example)”)</code></pre>
<p>aspect와 함께 쓰면 어노테이션이 달려있을 때 특정 동작을 하게 할 수 있음.</p>
<hr>

<h3 id="스트랭귤러-패턴">스트랭귤러 패턴</h3>
<p>레거시 코드가 있는 상태에서 개선 및 개발하는게 훨씬 더 어렵다.
-&gt; 이때 기존 레거시를 포기하고 아예 생산하는 방식은 절대 지양
-&gt; as-is와 to-be가 일정기간 공존하면서 점진적으로 리팩토링 (데드라인에 대한 압박을 줄이며 리팩토링 가능)
-&gt; 다 리팩토링 되면 레거시를 대체 </p>
<ul>
<li>느리고, 귀찮더라도 테스트코드(Unit Test, Acceptance Test) 먼저 작성하고 리팩토링하기(테스트코드가 없는 경우) -&gt; 그래야 방패막이 생긴다. 물론 로직이 간단한 경우라면 바로 옮길 수도 있다.</li>
</ul>
<hr>

<h3 id="핵심-비즈니스-로직은-어디에-위치해야-할까">핵심 비즈니스 로직은 어디에 위치해야 할까?</h3>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/a5c59068-87c9-4a27-8e2b-710c6261ae61/image.png" alt=""></p>
<ul>
<li>핵심 비즈니스 로직은 service가 아니라 domain에서 담당해야한다. (즉 각 도메인 객체들이 핵심 비즈니스 로직을 나눠가져야한다.)</li>
<li><blockquote>
<p>테스트 하기 쉬운 부분과 어려운 부분을 쪼개는 관점에서도, service에 둘 경우 비지니스 로직 + 데이터베이스 쿼리 로직이 섞여있기 때문에 테스트하기가 더 어려워진다.</p>
</blockquote>
</li>
</ul>
<br>

<h4 id="서비스-레이어의-역할">서비스 레이어의 역할</h4>
<ol>
<li>도메인 객체 로딩</li>
<li>도메인 객체에 메시지 보내기</li>
<li>상태가 바뀐 도메인 객체를 db에 반영
<img src="https://velog.velcdn.com/images/bahar-j/post/94b0ac8d-b696-49d5-b77b-5f30358ff973/image.png" alt=""></li>
</ol>
<br>

<p>-&gt; 이렇게 할 경우 서비스 레이어는 로직이 거의 없어져서 단위테스트가 필요없다 (도메인 객체만 단위테스트하면 됨)
-&gt; 도메인 객체가 테이블과 1:1 매칭되어 로직까지 들어갔을때 복잡하다면.. 클래스 분리 + 일급 컬렉션 적용 등이 필요!! 
-&gt; 그러면 분리되었을떄 걔와 관계된 메서드들이 따라가게 된다</p>
<br>
<br>

<ul>
<li>만약 두개 이상의 도메인이 로직에 참여한다면, 별도의 객체로 만들기</li>
<li>TDD를 잘하는 사람은 mock을 최후의 수단으로만 사용한다. (mocking을 하면 테스트 코드가 필연적으로 더 읽기 어려워진다.)</li>
<li>개념 하나에 하나의 단어만 사용하기(같은 추상적 개념을 다른 단어로 표기하지 말자)</li>
<li>테스트 하기 어려운 부분과 쉬운 부분을 쪼갠 후 이것들의 의존관계를 엮을 때 DI 방식을 사용한다</li>
<li>DTO와 도메인 객체를 분리하기(도메인은 가능한 한 setter 없이)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TDD, 클린 코드] (3) 클래스 분리, 그리고 개선 과정에서의 고민들]]></title>
            <link>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C</guid>
            <pubDate>Mon, 15 May 2023 14:01:54 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">new 인터페이스 == 이름을 갖지 않는 익명 클래스 == 람다</code></pre>
<ul>
<li>자바는 가장 작은 동작 단위가 클래스 -&gt; 람다처럼 함수로 구현해도 결국 내부 구현은 클래스이다</li>
</ul>
<ul>
<li>인터페이스 추출해서 넣어주는거 자체가 DI(Dependancy Injection) -&gt; 소프트웨어의 유연성을 위해 스프링 이전부터 많이 쓰던 기법.</li>
</ul>
<p><code>리팩토링 전</code></p>
<pre><code class="language-java">public class Car {
    ...

    public Car() {
        ...
    }

    public void moveCar() {
          RandomMovingStrategy strategy = new RandomMovingStrategy() // Car 클래스와 RandomMovingStrategy가 강결합됨
          move(strategy);
      }
}</code></pre>
<p><code>리팩토링 후</code></p>
<pre><code class="language-java">public class Car {
    ...
    private MovingStrategy strategy;

    public Car(MovingStragegy movingStrategy) { // DI를 통한 루즈 커플링
        ...
        this.strategy = movingStrategy;
    }

    public void moveCar() {
          move(strategy);
      }
}</code></pre>
<p>-&gt; 테스트하기 좋은 코드를 염두하고 짜다보면 자연스럽게 이런 식으로 유연한 설계를 하게될 가능성이 높아짐</p>
<br>

<ul>
<li><p>Service는 객체의 상태값을 유지하지는 않음. 상태값을 유지하는 객체는 Domain 객체.</p>
</li>
<li><p>객체는 작은 객체부터 바깥쪽 방향으로 만드는게 좋음</p>
</li>
<li><p>처음에는 리팩토링 단계에서 랩핑을 하는데, 경험이 쌓이다보면 바로바로 랩핑할 부분을 발견할 수 있게 됨 (처음에는 기준을 만들기보다는 무작정 랩핑해보기)</p>
</li>
<li><p>equals, hashCode, toString을 override를 잘 안하는 이유는 객체지향이 잘 안지켜지기 때문 -&gt; 객체지향을 잘 지켜서 하다보면 랩핑한 객체끼리 비교하거나 하는 상황이 많아져서 이런 애들이 많이 쓰임.</p>
</li>
</ul>
<ul>
<li>자바 객체와 데이터베이스 테이블은 1:1 관계이면 안됨.
테이블 하나에 자바 객체는 여러개가 되는게 맞음.
안그러면 god object가 돼서 유지보수가 너무 힘듦.</li>
</ul>
<ul>
<li><p>랩핑했으면 꺼내지말고 객체에 메시지를 보내기 (실제로는 행동할 수 없는 대상이지만(ex. WinNumbers) 의인화해서 생각하기)</p>
</li>
<li><p>a -&gt; b -&gt; c -&gt; d -&gt; e 
이런식으로 의존 뎁스가 깊어지는건 좋은 현상 (위임만 하는 메서드들이 많아짐)
다만 이때 점 두개 이상 쓰지 말기(상태값을 가지는 객체를 노출하고 거기서 다시 호출하는 형태) -&gt; 캡슐화 위반. 버그 유발. 디미터 법칙 위반 (오브젝트 그래프 상 옆집 친구하고만 놀아라)</p>
</li>
<li><p>반환값도 원시값이 아닌 랩핑된 객체로 반환하기
ex) int가 아닌 Rank를 반환하면 6개의 값만 반환되도록 제한된 것.</p>
</li>
<li><p>리팩토링 과정에서 고쳐야할 메서드가 많다면 둘 다 유지하는 것도 방법(무분별하게 접근하지 안도록 접근 제한자는 수정..)</p>
</li>
<li><p>메서드의 수를 줄이고, 생성자 수를 늘리자</p>
</li>
<li><p><code>주 생성자</code>는 받은 값을 인스턴스 변수 초기화하는 역할
<code>부 생성자</code>는 다 주생성자를 호출하는 방식!</p>
</li>
<li><p>생성자에 로직이 생기면 생성자를 private으로 바꾸고 정적 팩토리 메소드를 만들기</p>
</li>
</ul>
<hr>


<h4 id="스프링-프레임워크의-di와-일반-코드에서-di의-차이점">스프링 프레임워크의 DI와 일반 코드에서 DI의 차이점</h4>
<blockquote>
<p><code>스프링에서 DI 방법</code></p>
</blockquote>
<ol>
<li>생성자를 통한 인젝션  -&gt; 이게 가장 많이 쓰임</li>
<li>(setter) 메서드를 통한 인젝션 -&gt; 너무 무분별하게 쓰게 됨. 외부에서 언제든 바꿔치기 가능(테스트 코드 등에서)</li>
<li>필드 인젝션 -&gt; 테스트 용이성이 떨어짐</li>
</ol>
<p>그런데 우리가 지금 구현할 때 <code>메서드 인젝션</code>은 <code>스프링의 setter 인젝션</code>과는 다름.
스프링은 일반 메서드까지는 관여 못하기 때문에 메서드 인젝션을 지양했지만, 우리가 구현할 때에는 둘 다 괜찮음. 상황에 따라 어느게 더 좋을지 판단하기</p>
<pre><code class="language-java">car.move(() -&gt; 3); // 메서드 인젝션
new Car(() -&gt; 3, “june”); // 생성자 인젝션</code></pre>
<hr>

<h4 id="클래스-분리">클래스 분리</h4>
<ul>
<li><p>메서드의 인자 수를 2개 이하로 줄이기 -&gt; 관련성 있는 객체들을 묶어보기 </p>
</li>
<li><p>클래스의 인스턴스 변수가 많을 때에도 마찬가지. 인스턴스 변수 수를 2개 이하로 줄여보기</p>
</li>
<li><p>1) 같은 역할을 하고 2) 라이프 사이클이 같을 때 같은 클래스로 묶으면 좋음</p>
</li>
<li><p>private 메서드는 원래 public 메서드를 통해 테스트하는게 맞음.
그런데도 private을 테스트하고 싶은 유혹이 든다면?
방법 1) default로 접근제한자를 바꿔서 패키지 단위에서 테스트
방법 2) 라이브러리(보통 리플렉션을 활용) 이용해서 테스트
방법 3) <strong>다른 클래스가 해당 메서드를 가져야하는게 아닌지 고민해보기</strong></p>
</li>
</ul>
<blockquote>
<p><strong>클래스 분리 정량적 원칙</strong></p>
</blockquote>
<ol>
<li>원시값 포장</li>
<li>일급 컬렉션</li>
<li>메서드 인자 수 많을때</li>
<li>인스턴스 변수 수 많을 때</li>
<li>private을 테스트하고 싶어질때</li>
</ol>
<hr>

<h4 id="immutable-vs-mutable">immutable vs mutable</h4>
<p><code>mutable</code></p>
<pre><code class="language-java">public void add() {
 number ++;
}</code></pre>
<p><code>immutable</code></p>
<pre><code class="language-java">public Positive add() {
 return new Positive(number + 1);
}</code></pre>
<blockquote>
<p>immutable 객체의 메모리 문제?
원시값들과 Integer, Double.. 이런 객체들은 캐싱이 됨
IntegerCache처럼 캐시로 가짐. (ex. -128 ~ +128 까지 캐싱..)
객체가 새로 매번 생기는게 문제라면 캐싱하면 됨</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TDD, 클린 코드] (2) TDD, OOP를 위한 원칙들]]></title>
            <link>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-2-TDD-OOP%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9B%90%EC%B9%99%EB%93%A4</link>
            <guid>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-2-TDD-OOP%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9B%90%EC%B9%99%EB%93%A4</guid>
            <pubDate>Tue, 02 May 2023 15:07:41 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>테스트하기 어려운 코드를 object graph의 상위쪽으로 옮긴다 (옮기기 전 단계는 결국 테스트할 수 없어지지만, 우리는 모든 코드를 테스트하는게 목표는 아님)
-&gt; 테스트 하기 어려운 부분을 분리해서 핵심로직을 테스트하는게 목적</p>
</li>
<li><p>메소드의 크기가 최대 10라인을 넘지 않도록 구현</p>
</li>
<li><p>method가 한 가지 일만 하도록 최대한 작게 만들어라.</p>
</li>
<li><p>TDD를 잘하려면 특정 시점의 상태를 잘 생각해내야함.</p>
<ul>
<li>예를 들어, 우승자를 구하는 로직을 테스트한다면, 경기가 끝난 상태의 자동차들을 Given으로 줘야함.</li>
</ul>
</li>
<li><p>모델(도메인) 객체에서는 getter, setter 쓰지 마라. DTO, 혹은 view에 전달할 때는 getter 허용</p>
</li>
<li><p>생성자는 다른 생성자를 호출하도록 구현</p>
</li>
<li><p>테스트 코드를 위해 만든 생성자로 잘못 세팅하는 경우가 생기는걸 막으려면… 팩토리 클래스를 만들어서 실제 관리 포인트를 하나로 만드는 방법도 있다.</p>
</li>
</ul>
<pre><code class="language-java">public class carFactory {
    public static void create() {

    }
}</code></pre>
<ul>
<li><p>생성자는 마음껏 늘리면서 public 메소드의 수는 최대한 줄여라</p>
</li>
<li><p>접근제어자는 최소한만 오픈 -&gt; 필요하면 더 열기.</p>
</li>
<li><p>상태값이 없는 클래스의 메서드는 Static으로 해도 괜찮다</p>
</li>
<li><p>가장 좋은건 클래스의 인스턴스 변수가 클래스 내 모든 메서드에서 쓰일 수 있도록 설계되는 것.</p>
</li>
<li><p>테스트 하기 어려운 부분은 쿨하게 테스트 안해도 됨 -&gt; 테스트 하기 쉬운 부분과 어려운 부분을 분리하는 연습 필요(출력, DB CRUD.. 이런건 엔드투엔드 테스트, 통합테스트에서 해야함. 단위테스트X. 핵심 비즈니스로직이 없음)</p>
</li>
<li><p>모킹은 가능한 사용하지 않기(가독성이 떨어지고 유지보수가 어렵다)</p>
</li>
<li><p>자바에서도 final 적극 활용 (ex_변경되지 않는 메서드의 매개변수..)</p>
</li>
<li><p>UI, DB 등과 의존관계를 가지지 않는 핵심 도메인 영역을 집중 설계</p>
</li>
<li><p>view &lt;-&gt; controller &lt;-&gt; domain
도메인과 뷰는 의존관계를 갖지 않는다.
갖더라도 view -&gt; domain 방향만 허용</p>
</li>
<li><p>테스트는 경계값으로 (모든 값으로 할 수 없기 때문에)</p>
</li>
<li><p>total, sub,average, max, min, record, string, pointer 등 한정자를 써야한다면 이름의 끝에! (ex. expenseTotal..)</p>
</li>
<li><p>오픈소스 코드를 많이 읽어보기 (JDK, Spring 프레임워크 등)</p>
</li>
<li><p>도메인 지식을 쌓기 위해 노력</p>
</li>
<li><p>private 메서드가 늘어나는 것에 대한 거부감을 없애기</p>
</li>
<li><p>private 메서드들은 퍼블릭을 통해 테스트 하기.
priavte에 로직이 너무 많다면 여러 클래스의 역할을 하나에서 하는게 아닌지 의심해봐야함.</p>
</li>
<li><p>클래스의 인스턴스 변수를 최대한 줄여라 (즉 클래스가 가지는 상태값.) -&gt; 상태값을 여러군데에서 유지하면 변경될때마다 바꿔줘야함. 데이터 중복의 문제.. (ex) cars와 winners를 따로 가질 필요 없음. 그냥 필요할 때 winners를 구하는게 훨씬 좋음)</p>
</li>
<li><p>비즈니스 로직과 UI 로직을 분리(ex. car에서 print 메서드를 갖는것)</p>
</li>
<li><p>fixture -&gt; 테스트를 위해 필요한 초기 데이터. 테스트의 인스턴스 변수는 모든 테스트에 공통으로 필요한 픽스쳐만. 테스트마다 달라지는거는 각 테스트로 옮기기
@BeforeEach에는 각 테스트에서 중복으로 사용하는 픽스쳐만 초기화</p>
</li>
<li><p>한번 만들어진 후 외부에 의해 값을 바꿀 수 없는 Immutable 객체로 바꾸기 </p>
<ul>
<li>ex) 리턴할때 new Position(n) 과 같은 식으로 반환 -&gt; 인스턴스가 많이 만들어진다는 단점. 이정도의 성능 저하는 이슈가 되면 그때 바꿔도 괜찮음.</li>
</ul>
</li>
<li><p>값의 유효 범위 -&gt; 객체에서 관리</p>
</li>
<li><p>무항 메서드를 지향하기. 3개 이상은 가급적 사용X. 4개 이상은 절대x(특별한 이유가 있어도)</p>
</li>
<li><p>3개 이상의 인스턴스 변수를 가진 클래스를 만들지 마라</p>
</li>
<li><blockquote>
<p>너무 많으면 관련있는 애들을 묶어서 새로운 클래스로 만들기</p>
</blockquote>
</li>
</ul>
<hr>


<h4 id="클래스-분리의-정량적-기준">클래스 분리의 정량적 기준</h4>
<ol start="3">
<li>모든 원시값과 문자열을 포장 -&gt; ex RandomNumber</li>
<li>일급 콜렉션 사용</li>
</ol>
<hr>


<h4 id="cf-cqrs">cf) cqrs</h4>
<p>값을 바꾸도록 쿼리하는 부분과 값을 가져오는 부분을 분리하는 것과 분리하지 않는것.
1)
public Position move() {
  position = position + 1;
  return this;
}</p>
<p>2) 
public void move() {
  position = position + 1;
}</p>
<p>public int getPosition() {
  return position;
}
뭐가 더 맞고 어떤 상황에서 뭐가 더 좋은지 본인이 기준을 찾기.. 백프로 정답은 없음.</p>
<hr>


<h4 id="인터페이스-추출이-필요한-경우">인터페이스 추출이 필요한 경우</h4>
<p>구현체가 실제 코드 쪽에 1개, 그리고 테스트 쪽에도 존재
-&gt; 이게 결국 DI(의존 관계 주입). 이걸 잘써야 유연하고 테스트 쉬운 코드가 만들어짐
-&gt; 자바 8에 있는 람다(함수를 인자로 전달 == 인터페이스의 구현체, 익명 클래스, 구현해야할 메소드가 하나일때만 람다로 대체 가능, @FuntionalInterface 를 통해 강제 가능)를 쓰면 코드가 매우 간결해짐</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TDD, 클린 코드] (1) TDD, OOP 맛보기]]></title>
            <link>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-with-Java</link>
            <guid>https://velog.io/@bahar-j/TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-with-Java</guid>
            <pubDate>Sat, 08 Apr 2023 15:58:40 GMT</pubDate>
            <description><![CDATA[<h3 id="tdd">TDD</h3>
<pre><code class="language-java">TDD = Test First Development + Refactoring</code></pre>
<p>TDD는 단순 테스트 기법이 아니라, 요구사항을 분석하고 설계하는 기법이다.</p>
<br>

<h3 id="방법">방법</h3>
<ol>
<li>요구 사항을 작은 단위로 쪼갠다</li>
<li>각 요구 사항에 맞는 테스트 코드를 작성한다.</li>
<li>테스트 코드를 하나 완성할 때마다(최소 요구 사항 단위) 테스트를 통과하기 위한 실제 기능 코드를 구현한다.(이 과정에서는 코드가 더러워져도 괜찮다)</li>
<li>일단 pass 했다면 리팩토링을 한다 (객체지향 생활 체조 원칙 참고)</li>
<li>이 과정을 반복한다.</li>
</ol>
<br>

<h3 id="객체지향-생활-체조-원칙">객체지향 생활 체조 원칙</h3>
<p>규칙 1: 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.
규칙 2: else 예약어를 쓰지 않는다.
규칙 3: 모든 원시값과 문자열을 포장한다.
규칙 4: 한 줄에 점을 하나만 찍는다.
규칙 5: 줄여쓰지 않는다(축약 금지).
규칙 6: 모든 엔티티를 작게 유지한다.
규칙 7: 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
규칙 8: 일급 콜렉션을 쓴다.
규칙 9: 게터/세터/프로퍼티를 쓰지 않는다.</p>
<p>-&gt; 리팩토링을 해야할 곳을 찾는 것이 쉽지 않기 때문에 위와 같은 정량적인 기준에 맞춰 코드를 보며 고칠 부분을 찾는 연습을 하자.
-&gt; 위 규칙에 따르다보면 자연스레 메서드 분리 / 클래스 분리가 가능하다.</p>
<br>

<h3 id="클래스class">클래스(class)</h3>
<p>객체지향에서 클래스는 <code>객체의 능동적인 관리자</code>이다.
즉 getter, setter를 남발하며 클래스를 자료구조로만 이용하는 건 바람직하지 않다.
객체는 캡슐화된 데이터의 대표자(representative)이다. 따라서 객체를 스스로 결정을 내리고 행동할 수 있는 자립적인 엔티티로 생각하자(코드를 읽어봤을 때 객체가 독립된 사람인 것처럼 읽힌다면 올바른 방향)</p>
<h4 id="cf-클래스의-이름">cf) 클래스의 이름</h4>
<p>-er로 끝나는 이름(무엇을 하는지에 기반한 이름)을 쓰지 말라 (ex. cashFormatter)
무엇인지에 기반한 이름을 지어라 (ex. cash)</p>
<h4 id="예시-코드">예시 코드</h4>
<pre><code class="language-java">/* 리팩 전 */
public static void main(String[] args) {
  // 데이터베이스 연결
  DBConnection connection = new DBConnection();

  // 상품 금액 확인
  Milk milk = connection.findItem(“우유”);
  int price = milk.getPrice();

  // 결제
  Person pobi = connection.findPerson(“포비”);
  Wallet wallet = pobi.getWallet();
  wallet.setAmount(wallet.getAmount() - price);

  // 상품 지급
  pobi.setItem(milk);

  // 데이터베이스 저장
  connection.save(pobi);
}

/* 리팩 후 */
public static void main(String[] args) {
  // 데이터베이스 연결
  DBConnection connection = new DBConnection();

  // 상품 및 구매자 조회
  Milk milk = connection.findItem(“우유”);
  Person pobi = connection.findPerson(“포비”);

  // 결제 및 상품 지급
  milk.sellTo(pobi);

  // 데이터베이스 저장
  connection.save(pobi);
}
</code></pre>
<br>

<h3 id="메서드method">메서드(method)</h3>
<p>1) 빌더(builder)
: 무언가를 반환하는 메서드. 빌더 메서드의 이름은 명사로 짓는다.
단, boolean을 반환하는 메서드는 가독성을 위해 형용사로 짓는다.</p>
<p>2) 조정자(manipulator)
: 엔티티를 수정하는 메서드. 반환값이 없다. 조정자의 이름은 동사로 짓는다.</p>
<br>

<h4 id="그-외-작은-팁">그 외 작은 팁...</h4>
<ul>
<li>각종 라이브러리에서 제공하는 api들이 의도한대로 동작하는지 확인하는 테스트를 작성해보는 것도 좋다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[WebFlux flatMap(map), doOnNext, doOnSuccess, then 차이]]></title>
            <link>https://velog.io/@bahar-j/WebFlux-flatMapmap-doOnNext-doOnSuccess-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@bahar-j/WebFlux-flatMapmap-doOnNext-doOnSuccess-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Wed, 26 Oct 2022 09:09:42 GMT</pubDate>
            <description><![CDATA[<h3 id="flatmap혹은-map과-doonnext혹은-doonsuccess의-차이">flatMap(혹은 map)과 doOnNext(혹은 doOnSuccess)의 차이</h3>
<p>일단 flatMap과 doOnNext의 차이는,
flatMap은 앞에서 전달받은 아이템을 다른 형태로 변환하여 방출하는 반면, doOnNext는 로깅, api 콜과 같은 부가적인 동작을 하되, 전달받은 아이템을 변환하여 넘기는 동작은 하지 않는다는 점이다. 즉, source에 대한 transforming은 하지 않는다. (<a href="https://stackoverflow.com/questions/60103158/what-is-the-difference-between-map-and-doonnext-in-flux-i-e-project-reactor/60105107#60105107">참고</a>)</p>
<blockquote>
<p>cf) <code>map과 flatMap의 차이</code>
map은 일반 개체를 반환하는 반면, flatMap은 publisher(Mono/Flux)를 반환한다.
또 map은 sync 연산자로, 호출자와 동일한 스레드에서 실행되고, 순서를 보장하는 반면,
flatMap은 sync / async 연산자로, 두 가지 동작이 모두 가능하다. (<a href="https://www.baeldung.com/java-reactor-map-flatmap">참고</a>) -&gt; 아마 flatMapSequential 같은 연산자도 있기 때문이 아닐까...?</p>
</blockquote>
<blockquote>
<p>cf) <code>concatMap</code>
concatMap은 flatMap과 동작이 같은데, 순서를 보장한다.</p>
</blockquote>
<blockquote>
<p>cf) <code>interleaving in WebFlux</code>
webflux에서는 데이터 흐름에서 다른 데이터가 끼어드는 것을 인터리빙이라고 한다.</p>
</blockquote>
<br>
<hr>

<br>


<h3 id="doonnext와-doonsuccess의-차이">doOnNext와 doOnSuccess의 차이</h3>
<p><code>doOnNext</code> : 데이터가 성공적으로 &#39;방출&#39;되었을 때 실행됨 (즉 데이터가 empty이면 안되고 존재해야함)
<code>doOnSuccess</code> : 앞의 publisher가 성공적으로 &#39;완료&#39;되었을 때 실행됨 (즉 데이터가 empty여도 됨)</p>
<p>-&gt; 한편 둘 다 error인 상황에서는 실행되지 않음.
-&gt; 둘 다 스트림에 영향을 끼치지 않음(일반적으로 log 같은 것에 사용). 즉 여기서 반환된 값은 다음 스트림으로 전달되지 않음(이전 스트림의 값이 다음 스트림으로 전달됨)</p>
<pre><code class="language-java">
Mono.error(new RuntimeException(&quot;Something wrong&quot;))
    .doOnNext(i -&gt; System.out.println(&quot;On next: &quot; + i))
    .doOnSuccess(i -&gt; System.out.println(&quot;On success: &quot; + i))
    .doOnError(i -&gt; System.out.println(&quot;On error: &quot; + i))
    .block();
</code></pre>
<pre><code class="language-java">On error: java.lang.RuntimeException: Something wrong</code></pre>
<p><a href="https://stackoverflow.com/questions/55976963/is-there-a-difference-between-doonsuccess-vs-doonnext-for-a-mono">참고</a></p>
<h3 id="then">then</h3>
<p>then도 doOnSuccess와 마찬가지로 앞의 publisher가 성공적으로 &#39;완료&#39;되었을 때 실행됨(empty여도 괜찮음) 
그런데 앞의 두 operator(doOnNext, doOnSuccess)와 달리 기존 스트림의 값을 바꿀수는 있지만, 이전 스트림의 값을 전달받지는 않는다.</p>
<pre><code class="language-java">updateIfRequired.then(testService.doSomething(a))
                .then(testRepository.saveSomething(a));</code></pre>
<p>이전 스트림의 값을 전달받아 쓰고 싶다면 flatMap이나 map을 쓸 수 있지만, 이 경우는 empty 상황에서 스트림이 complete 되고 이후 작업은 cancel된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[abstract와 interface의 실제 활용]]></title>
            <link>https://velog.io/@bahar-j/abstract%EC%99%80-interface%EC%9D%98-%EC%8B%A4%EC%A0%9C-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@bahar-j/abstract%EC%99%80-interface%EC%9D%98-%EC%8B%A4%EC%A0%9C-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Thu, 06 Oct 2022 17:15:31 GMT</pubDate>
            <description><![CDATA[<h3 id="abstract">abstract</h3>
<ul>
<li><p>상속에서 쓰임.</p>
</li>
<li><p>추상 클래스가 설계도인 인터페이스를 구현(<code>implements</code>)하는 경우</p>
<pre><code class="language-java">public abstract class Car implements Transport {
  @Override
  public String ride(Object person) { return &quot;default ride&quot;; }

  @Override
  public Boolean drive() { return true; }

  @Override
  public &lt;T&gt; T getOff() { return null; }

  public &lt;T&gt; T openWindow() { return null; }

  public abstract String hookHorn();
}</code></pre>
</li>
</ul>
<p>-&gt; 위 예시와 같이 Transport 인터페이스에 정의된 메서드들(반드시 모두 public abstract, 생략 가능)을 override하며 default가 되는 값을 반환해줄 수 있고, 정의되지 않은 추가 메서드들을 만들어줄 수도 있다(openWindow, hookHorn 같은..)</p>
<p>-&gt; 이때 Transport 인터페이스에 정의된 모든 메서드들을 Car 추상 클래스에도 적어줘야하는건 아니다.</p>
<br>

<ul>
<li>추상 클래스가 추상 클래스를 확장(<code>extends</code>)하는 경우<pre><code class="language-java">public abstract class SportsCar extends Car {
  @Override
  public String ride(Object person) { return &quot;default sports car ride&quot; ; }
  public abstract String accelerate();
}</code></pre>
</li>
<li><blockquote>
<p>오버라이딩을 통해 새로운 디폴트 값을 반환하고 싶거나, 새로운 abstract method(모델일 경우 필드)를 추가해주고 싶을 때</p>
</blockquote>
</li>
</ul>
<blockquote>
<p><strong>이때 주의할 점</strong>
추상 클래스를 상속 받은 클래스는 추상 클래스에 정의된 모든 추상 메서드를 오버라이딩을 통해 구현해주어야한다. 그렇지 않으면, 이 클래스 또한 추상 클래스가 되어야 한다.</p>
</blockquote>
<br>

<hr>

<br>

<h3 id="interface">interface</h3>
<ul>
<li><p>설계도 </p>
</li>
<li><p>인터페이스가 인터페이스를 확장(<code>extends</code>)하는 경우</p>
<pre><code class="language-java">public interface BlockHoundIntegration extends Comparable&lt;BlockHoundIntegration&gt; {

  /**
   * Lets an integration apply the customizations (see {@link BlockHound.Builder})
   * before BlockHound is installed.
   *
   * @param builder an instance of {@link BlockHound.Builder} that is being installed
   */
  void applyTo(BlockHound.Builder builder);

  @Override
  default int compareTo(BlockHoundIntegration o) {
      return 0;
  }
}</code></pre>
</li>
<li><blockquote>
<p>여기서는 BlockHoundIntegration 인터페이스가 Comparable 인터페이스를 확장하였다. 이때 이 인터페이스에서 compareTo를 오버라이딩하지 않았다면 BlockHoundIntegration을 이후에 구현(implements)할 클래스에서 해당 메서드를 구현해줘야한다.
하지만 이 경우에는 default 키워드를 사용하면서 compareTo를 오버라이딩하여 구현해주었다. </p>
</blockquote>
</li>
</ul>
<p>-&gt; default 키워드는 인터페이스 내에서 (예외적으로) 일반 메서드 구현를 가능할 수 있게 만드는 키워드로, java 8부터 사용 가능하다.</p>
<p>-&gt; 왜 하필 default 키워드가 붙냐면(디폴트 키워드는 원래 생략되는 애인데), 메서드에 아무 접근제어자가 붙지 않을 경우 interface에서는 public abstrac가 생략된 것으로 간주하기 때문이다.</p>
<blockquote>
<p>인터페이스는 설계도를 만드는 작업인데, 이 설계도가 수정되지 않는게 가장 좋은 설계이겠지만, 
어쩔 수 없이 수정해야할 경우 이를 implements한 모든 클래스를 수정하는 것을 막기 위해 인터페이스에서 default 키워드를 사용할 수 있도록 허락해주었다.
(즉 공통적으로 구현할거 같은 메서드를 상위 인터페이스에서 한번만 구현할 수 있도록..!!)</p>
</blockquote>
<br>

<ul>
<li><p>인터페이스 내부에 inner class를 써주는 경우 </p>
<pre><code class="language-java">public interface Transport {

  (public static final) class Car implements Transport {}

  @Getter
  @RequiredArgsConstructor
  class Taxi implements Transport {

      private final Driver driver;
  }
}</code></pre>
</li>
<li><blockquote>
<p>인터페이스 내부의 이너 클래스(public static final이어야함)는 해당 interface를 구현해줘야한다. 이를 이용해서 Transport라는 공통 부모 타입을 갖는 Car, Taxi를 만들어주고 외부에서 이를 new Transport().Car()와 같은 식으로 인스턴스화해서 사용할 수 있다.</p>
</blockquote>
</li>
</ul>
<blockquote>
<p><strong>final이 붙은 class?</strong>
final이 class 앞에 붙으면 이 클래스는 부모 클래스가 되는 것이 불가능하다. 즉 상속이 불가능하다.
따라서 해당 클래스 내의 모든 메소드는 overrinding(재정의) 될 수 없다.</p>
</blockquote>
<blockquote>
<p><strong>final이 붙은 method?</strong>
final이 붙은 메소드 또한 오버라이딩이 불가능하다.
이를 이용하면, 부모 클래스에서 정의한 메소드를 자식 클래스에서 재정의하지 않고 그대로 쓰도록 하는 것이 가능하다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP 완벽 가이드] 13장 다이제스트 인증]]></title>
            <link>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-13%EC%9E%A5-%EB%8B%A4%EC%9D%B4%EC%A0%9C%EC%8A%A4%ED%8A%B8-%EC%9D%B8%EC%A6%9D</link>
            <guid>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-13%EC%9E%A5-%EB%8B%A4%EC%9D%B4%EC%A0%9C%EC%8A%A4%ED%8A%B8-%EC%9D%B8%EC%A6%9D</guid>
            <pubDate>Tue, 26 Jul 2022 15:24:38 GMT</pubDate>
            <description><![CDATA[<h3 id="131-다이제스트-인증의-개선점">13.1 다이제스트 인증의 개선점</h3>
<ul>
<li>비밀번호를 그대로 보내지 않음 -&gt; 비밀번호를 비가역적으로 섞은 지문(fingerprint) 혹은 요약(digest)를 보냄  </li>
<li>재전송 방지를 위한 난스 이용 -&gt; 난스를 비밀번호에 섞으면 난스가 바뀔 때마다 요약도 바뀌게 만들어줌</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP 완벽 가이드] 15장 엔티티와 인코딩]]></title>
            <link>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-15%EC%9E%A5-%EC%97%94%ED%8B%B0%ED%8B%B0%EC%99%80-%EC%9D%B8%EC%BD%94%EB%94%A9</link>
            <guid>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-15%EC%9E%A5-%EC%97%94%ED%8B%B0%ED%8B%B0%EC%99%80-%EC%9D%B8%EC%BD%94%EB%94%A9</guid>
            <pubDate>Tue, 26 Jul 2022 15:23:39 GMT</pubDate>
            <description><![CDATA[<h3 id="15-엔터티와-인코딩">15. 엔터티와 인코딩</h3>
<p>HTTP는 다음을 보장</p>
<ul>
<li>객체는 올바르게 식별됨 (Content-Type, Content-Language)</li>
<li>객체는 올바르게 압축이 풀림(Content-Length, Content-Encoding)</li>
<li>객체는 항상 최신(엔티티 검사기, 캐시 만료 제어)</li>
<li>사용자의 요구를 만족(Accept)</li>
<li>네트워크 사이를 빠르고 효율적으로 이동(범위 요청, 델타 인코딩, 그 외의 데이터 압축)</li>
<li>조작되지 않고 온전하게 도착(전송 인코딩, Cotent-MD5 체크섬)</li>
</ul>
<br>
<br>

<h3 id="151-메시지는-컨테이너-엔티티는-화물">15.1 메시지는 컨테이너, 엔티티는 화물</h3>
<ul>
<li>Content-Language: 전달되는 객체와 각장 잘 대응되는 자연어</li>
<li>Content-Location: 요청 시점을 기준으로 객체의 또 다른 위치</li>
<li>Content-Range: 이 엔티티가 전체의 어느 부분에 해당하는지</li>
<li>Content-Encoding: 객체 데이터에 대해 행해진 변형</li>
<li>Content-MD5: 엔티티 본문의 콘텐츠에 대한 체크섬</li>
</ul>
<br>
<br>

<h3 id="1521-잘림-검출">15.2.1 잘림 검출</h3>
<p>Content-Length가 없다면 클라이언트는 커넥션이 정상적으로 닫힌 것인지 메시지 전송 중에 서버에 충돌이 발생한 것인지 구분하지 못함.<br>메시지 잘림은 캐싱 프록시 서버에 특히 취약 -&gt; 이를 방지하기 위해 <code>캐싱 프록시 서버는 Content-Length 헤더를 지니지 않은 HTTP 본문은 보통 캐싱하지 않음</code> </p>
<br>
<br>

<h3 id="1524-콘텐츠-인코딩">15.2.4 콘텐츠 인코딩</h3>
<p>HTTP는 콘텐츠 <code>인코딩을 통해 보안을 강화하거나 압축</code><br>이때 <code>Content-Length 헤더는 인코딩된 본문의 길이를 바이트 단위로 정의</code> (원본의 길이가 아님)  </p>
<br>
<br>

<h3 id="1522-엔티티-본문-길이-판별을-위한-규칙">15.2.2 엔티티 본문 길이 판별을 위한 규칙</h3>
<ol>
<li>HEAD 처럼 본문을 갖지 않는 HTTP 메시지에서는 Content-Length 헤더가 무시됨  </li>
<li>메시지가 Transfer-Encoding 헤더를 보함한다면, 메시지가 커넥션이 닫혀서 먼저 끝나지 않는 이상 엔티티는 &#39;0 바이트 청크&#39;라고 불리는 특별한 패턴으로 끝나야함  </li>
<li>Transfer-Encoding 헤더 필드를 갖는 메시지를 받았다면 반드시 Content-Length 헤더를 무시해야함  </li>
<li>멀티파트 유형은 자신의 크기를 스스로 결정할 수 있는 유일한 엔티티 본문 유형  </li>
</ol>
<br>
<br>

<h3 id="1552-콘텐츠-인코딩-유형">15.5.2 콘텐츠 인코딩 유형</h3>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/bd7503d1-750f-4f3c-a7de-560d2709cfe4/image.png" alt=""></p>
<br>
<br>

<h3 id="1553-accept-encoding-헤더">15.5.3 Accept-Encoding 헤더</h3>
<p>HTTP 요청이 Accept-Encoding 헤더를 포함하지 않는다면 서버는 클라이언트가 어떤 인코딩이든 받아들일 수 있는 것으로 간주  </p>
<pre><code class="language-yml">Accept-Encoing: gzip;q=1.0, identity; q=0.5, *;q=0</code></pre>
<p>Q 값은 선호도, *는 그 외 모두를 의미  </p>
<br>
<br>

<h3 id="1556-콘텐츠-인코딩과-전송-인코딩">15.5.6 콘텐츠 인코딩과 전송 인코딩</h3>
<p><code>콘텐츠 인코딩</code>은 콘텐츠 포맷과 긴밀하게 연관됨 -&gt; ex) 텍스트 파일은 흔힉 gzip으로 압축하지만 JPEG 파일은 그렇게 하지 않음(JPEG는 gzip으로 잘 압축되지 않음)<br><code>전송 인코딩</code>은 구조적인 이유 떄문에 적용되는 것. 콘텐츠의 포맷과는 독립적 -&gt; 메시지 데이터가 네트워크에 전송되는 방법을 바꾸기 위해 적용. ex) 청크 인코딩 
<img src="https://velog.velcdn.com/images/bahar-j/post/dfe11a1d-443f-4edb-b7c2-3b220a06104a/image.png" alt=""></p>
<br>

<blockquote>
<p><code>청크 인코딩?</code><br>메시지를 일정 크기의 청크로 쪼개어 순차적으로 보내는 것<br>이를 이용하면 메시지를 보내기 전에 전체 크기를 알 필요가 없어짐<br>본문이 동적으로 생성되는 상황에서, 서버는 그중 일부를 버퍼에 담은 후 청크 크기가 되면 보냄<br>수신자가 본문을 재구축하는 절차는 전송 순서와 반대      </p>
</blockquote>
<br>

<blockquote>
<p>미래에 전송 인코딩이 필수가 된다면 ?
청크 전송 인코딩이 최상위에 적용되어야 함 -&gt; HTTP/1.1은 청크 인코딩만은 최소한 지원하기 때문  </p>
</blockquote>
<br>
<br>

<h3 id="1563-청크와-지속-커넥션">15.6.3 청크와 지속 커넥션</h3>
<p>지속 커넥션 + 서버에서 콘텐츠가 동적으로 생성되는 상황에서는 메시지를 보내기 전에 본문의 길이를 알아내는 것이 불가능<br>-&gt; 이런 상호아에서 청크 인코딩이 해법을 제공<br>서버는 크기가 0인 청크로 본문이 끝났음을 알리고 다음 응답을 위해 커넥션을 열린 채로 유지할 수 있음</p>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/44f1fe04-c37a-4753-9935-4be28a3892da/image.png" alt=""></p>
<blockquote>
<p><code>트레일러</code> ?<br>본문의 콘텐츠가 먼저 생성되어야 한다거나 하는 등의 이유로 메시지 시작 시점에서는 그 값을 알 수 없는 추가적인 헤더 필드를 담을 수 있음<br>Transfer-Encoing, Trailer, Content-Length를 제외한 어떤 HTTP 헤더도 트레일러로 보낼 수 있음  </p>
</blockquote>
<br>
<br>

<h3 id="1582-조건부-요청과-검사기">15.8.2 조건부 요청과 검사기</h3>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/5306900c-10c1-4052-8e89-01a41b2a0670/image.png" alt=""></p>
<br>
<br>

<h3 id="159-범위-요청">15.9 범위 요청</h3>
<p>HTTP는 클라이언트가 문서의 일부분이나 특정 범위만 요청할 수 있게 해줌<br><img src="https://velog.velcdn.com/images/bahar-j/post/8c5ff280-7c13-49a9-adab-c1cb83025e4b/image.png" alt=""></p>
<p>서버는 클라이언트에게 자신의 범위를 받아들일 수 있는지 응답에 Accept-Range 헤더를 포함시키는 방법으로 알려줄 수 있음  </p>
<br>

<p><img src="https://velog.velcdn.com/images/bahar-j/post/a2e79cc5-d748-49da-b495-2f3994eb01ac/image.png" alt=""></p>
<p><code>단, 범위 요청은 클라이언트와 서버가 같은 버전의 문서를 갖고 있을 때만 의미가 있다는 것을 유의</code>  </p>
<br>
<br>

<h3 id="1510-델타-인코딩">15.10 델타 인코딩</h3>
<p>객체 전체가 아닌 변경된 부분에 대해서만 통신하여 전송량을 최적화는 HTTP 프로토콜의 확장<br><img src="https://velog.velcdn.com/images/bahar-j/post/7e7d3c52-9294-4b33-bbc6-67da7aa31145/image.png" alt=""></p>
<p>A-IM(Accept-Instance-Manipulation) 헤더: 클라이언트가 서버에게 자신이 페이지에 대한 델타를 받아들일 수 있음을 알려주는 헤더  </p>
<p>그러나, 델타 인코딩은 전송 시간을 줄일 수는 있지만 구현 복잡도가 높음<br>델타 인코딩을 지원하는 서버는 자신이 제공하는 페이지가 변경되는 매 순간의 사본을 유지하고 있어야함 -&gt; 그래야 클라이언트가 요청을 보냈을 때 변경된 부분을 알아낼 수 있음<br>-&gt; 문서를 제공하는데 걸리는 시간이 주는 대신 디스크 공간이 많이 필요  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP 완벽 가이드] 14장 보안 HTTP]]></title>
            <link>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-14%EC%9E%A5-%EB%B3%B4%EC%95%88-HTTP</link>
            <guid>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-14%EC%9E%A5-%EB%B3%B4%EC%95%88-HTTP</guid>
            <pubDate>Tue, 26 Jul 2022 15:21:43 GMT</pubDate>
            <description><![CDATA[<h3 id="1411-https">14.1.1 HTTPS</h3>
<p>HTTPS는 HTTP의 하부에 전송 레벨 암호 보안 계층을 제공함으로써 동작<br>이 보안 계층은 Secure Socket Layer(SSL) 혹은 Transport Layer Security(TLS)를 이용해 구현된다  </p>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/e6f482e0-6c0f-48f4-9e6c-70134b7e4cc6/image.png" alt=""></p>
<br>
<br>

<h3 id="142-디지털-암호학">14.2 디지털 암호학</h3>
<p>SSL, HTTPS에는 암호 인코딩 기법이 적용된다  </p>
<ul>
<li>대칭키 암호 체계 : 인코딩과 디코딩에 같은 키를 사용하는 알고리즘  </li>
<li>비대칭키 암호 체계 : 인코딩, 디코딩에 다른 키를 사용하는 알고리즘  </li>
<li>디지털 서명 : 메시지가 위조 / 변조되지 않았음을 인증하는 체크섬  </li>
</ul>
<br>
<br>

<h3 id="1422-암호cipher">14.2.2 암호(cipher)</h3>
<p>암호는 메시지를 인코딩, 이후 비밀 메시지를 디코딩하는 방법<br><img src="https://velog.velcdn.com/images/bahar-j/post/dc584d13-d52d-4acc-a787-8fc9acd80d11/image.png" alt=""></p>
<br>
<br>

<h3 id="1432-공유키-발급하기">14.3.2 공유키 발급하기</h3>
<br>

<blockquote>
<ul>
<li><code>대칭키 암호법</code><br>한 쌍의 호스트가 하나의 인코딩/디코딩 키를 사용  </li>
</ul>
</blockquote>
<br>

<blockquote>
<ul>
<li><code>대칭키 암호의 단점</code><br>발송자와 수신자가 서로 대화하려면 둘 다 공유키를 가져야 한다는 것  </li>
<li><blockquote>
<p><code>클라이언트와 서버가 은밀하게 대화를 나누려면 서버가 수천 개의 키를 생성하고 기억해야함</code><br>(ex. N개의 서버가 각각 N-1개의 클라이언트랑 대화를 하려면 대략 N^2개의 비밀키가 필요)  </p>
</blockquote>
</li>
</ul>
</blockquote>
<br>
<br>

<h3 id="1432-공개키-암호법">14.3.2 공개키 암호법</h3>
<br>

<blockquote>
<ul>
<li><code>공개키 (비대칭) 암호법</code><br>두 개의 비대칭 키를 사용. 
하나는 호스트의 메시지를 인코딩하기 위한 것. 다른 하나는 그 호스트의 메시지를 디코딩하기 위한 것.<br>이때 인코딩 키는 모두에게 공개되어있음  </li>
<li><blockquote>
<p>모든 사람이 같은 키로 X에게 메시지를 인코딩해서 보낼 수는 있지만, X를 제외한 누구도 그 메시지를 디코딩할 수 없음  </p>
</blockquote>
</li>
</ul>
</blockquote>
<br>

<p><img src="https://velog.velcdn.com/images/bahar-j/post/f72ec142-5279-4d85-aeea-15e516f7dc36/image.png" alt=""></p>
<br>

<blockquote>
<p>표준화된 공개키 기술 묶음을 만드는 것의 중요성 때문에, Public-Key Infrastructure(PKI) 표준화 작업이 25년 넘게 계속 진행 중 (ex. RPC 5280)</p>
</blockquote>
<br>
<br>

<h3 id="1441-rsa">14.4.1 RSA</h3>
<p>공개키 비대칭 암호의 과제는 아래 내용을 알고 있다해도 비밀인 개인 키를 계산할 수 없다는 것  </p>
<ul>
<li>공개키</li>
<li>네트워크 스누핑을 통해 얻은 암호문의 일부  </li>
<li>메시지와 그것을 암호화한 암호문(인코더에 임의의 텍스트를 넣고 실행해서 획득)  </li>
</ul>
<p>-&gt; 이를 만족하는 공개키 암호 체계 중 유명한 하나가 RSA 알고리즘  </p>
<br>
<br>

<h3 id="1442-혼성-암호-체계와-세션-키">14.4.2 혼성 암호 체계와 세션 키</h3>
<br>

<blockquote>
<ul>
<li><code>공개키 암호의 단점</code><br>알고리즘 계산이 느린 편  </li>
</ul>
</blockquote>
<br>

<p>-&gt; 그래서 <code>실제로는 대칭과 비대칭키를 섞어서 씀</code><br>-&gt; 노드들 사이의 <code>의사소통 채널을 수립할 때는 공개 키 암호를 사용</code><br>-&gt; 채널을 통해 <code>임시의 무작위 대칭 키를 생성하고 교환하여 이후의 나머지 데이터를 암호화할 때는 빠른 대칭 키를 사용</code>  </p>
<br>
<br>

<h3 id="145-디지털-서명">14.5 디지털 서명</h3>
<br>

<ol>
<li>서명은 <code>메시지를 작성한 저자가 누군지 알려줌</code> -&gt; 저자는 저자의 극비 개인 키를 갖고 있기 때문에, 오직 저자만이 이 체크섬을 계산할 수 있음. 그래서 체크섬은 저자의 개인 서명(signature)처럼 동작  </li>
<li>서명은 <code>메시지 위조를 방지</code> -&gt; 송신 중인 메시지를 수정하면 체크섬은 더이상 맞지 않음. 또 체크섬은 저자의 비밀 개인 키와 관련되어 침입자는 위조된 메시지에 대해 올바른 체크섬을 날조할 수 없음.</li>
</ol>
<br>

<ul>
<li>노드 A는 가변 길이 메시지를 정제하여 고정된 길이의 요약(digest)로 만듦  </li>
<li>노드 A는 그 요약에, 사용자의 개인 키를 매개변수로 하는 &#39;서명&#39; 함수를 적용해서 서명과 메시지를 노드 B에 전송 </li>
<li>메시지를 받은 노드 B는 공개키를 이용한 역함수를 적용하여 서명을 검사 -&gt; 이 결과가 노드 B가 갖고 있는 버전의 요약과 일치하지 않는다면, 메시지는 송신 중에 위조되었거나 발송자가 노드 A가 보낸 메시지가 아닌 것.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/f0f837ab-f766-4bbf-b957-166f07c60896/image.png" alt=""></p>
<br>
<br>

<h3 id="146-디지털-인증서">14.6 디지털 인증서</h3>
<p>디지털 인증서(certs)는 신뢰할 수 있는 기관으로부터 보증 받은 사용자나 회사에 대한 정보를 담고 있다.  </p>
<br>
<br>

<h3 id="1461-인증서의-내부">14.6.1 인증서의 내부</h3>
<p>인증서는 다음 내용을 담고 있다  </p>
<ul>
<li>대상의 이름(사람, 서버, 조직 등)</li>
<li>유효 기간  </li>
<li>인증서 발급자(누가 이 인증서를 보증하는지)  </li>
<li>인증서 발급자의 디지털 서명<br>...</li>
</ul>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/f7e0694e-b4cb-4a41-9959-dad6ba7b8587/image.png" alt=""></p>
<br>
<br>

<h3 id="1463-서버-인증을-위해-인증서-사용하기">14.6.3 서버 인증을 위해 인증서 사용하기</h3>
<ol>
<li>사용자가 HTTPS를 통한 안전한 웹 트랜잭션을 시작할 때, 최신 브라우저는 자동으로 접속한 서버에서 디지털 인증서를 가져온다.  </li>
</ol>
<p>-&gt; 만약 서버가 인증서를 갖고 있지 않다면, 보안 커넥션은 실패  </p>
<p>&lt;서버 인증서가 포함하는 필드&gt;</p>
<ul>
<li>웹 사이트의 이름과 호스트 명 </li>
<li>웹 사이트의 공개키 </li>
<li>서명 기관의 이름</li>
<li>서명 기관의 서명 </li>
</ul>
<ol start="2">
<li>브라우저가 인증서를 받으면, 서명 기관을 검사 </li>
</ol>
<p>-&gt; 만약 그 기관이 공공이 신뢰할만한 서명 기관이라면 브라우저는 그것의 공개키를 이미 알고 있을 것(보통 브라우저에 여러 서명 기관의 인증서가 미리 설치된 상태)
-&gt; 만약 서명 기관이 모르는 곳이라면, 서명 기관을 신뢰하는지 확인하기 위한 대화상자를 보여줌  </p>
<ol start="3">
<li>&#39;디지털 서명&#39;에서 이야기했던 바와 같이 브라우저는 서명을 검증  </li>
</ol>
<br>
<br>

<h3 id="1471-https-개요">14.7.1 HTTPS 개요</h3>
<p>HTTPS는 그냥 보안 전송 계층을 통해 전송되는 HTTP<br>HTTPS는 HTTP 메시지를 TCP로 보내기 전에 먼저 그걸 암호화하는 보안 계층으로 보냄  </p>
<br>
<br>

<h3 id="1472-https-스킴">14.7.2 HTTPS 스킴</h3>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/0b5ead0b-f1e6-4c38-bac6-4ab47ae41175/image.png" alt=""></p>
<br>

<p><strong><code>SSL 트래픽은 바이너리 프로토콜</code></strong> 이기 때문에, 바이너리 SSL 트래픽을 전송하여 HTTP와는 완전히 다르다.  </p>
<br>
<br>

<h3 id="1473-보안-전송-셋업">14.7.3 보안 전송 셋업</h3>
<p>일단 TCP 연결이 되고 나면, 클라이언트와 서버는 암호법 매개변수와 교환 키를 협상하면서 SSL 계층을 초기화 (SSL 핸드셰이크)<br><img src="https://velog.velcdn.com/images/bahar-j/post/8dc17664-04ab-4ace-8b38-67759ac07dd0/image.png" alt=""></p>
<br>
<br>

<h3 id="1474-ssl-핸드셰이크">14.7.4 SSL 핸드셰이크</h3>
<p>핸드셰이크에서는 다음과 같은 일이 일어난다</p>
<ul>
<li>프로토콜 버전 번호 교환  </li>
<li>양쪽이 알고 있는 암호 선택  </li>
<li>양쪽의 신원을 인증  </li>
<li>채널을 암호화하기 위한 임시 세션 키 생성  </li>
</ul>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/4f64cda7-0e6e-469c-a999-8b192d9d574f/image.png" alt=""></p>
<br>
<br>

<h3 id="1475-서버-인증서">14.7.5 서버 인증서</h3>
<ul>
<li>클라이언트 인증서 -&gt; 보통 쓰지 않음  </li>
<li>서버 인증서 -&gt; HTTPS에서 무조건 요구</li>
</ul>
<br>
<br>

<h3 id="1476-사이트-인증서-검사">14.7.6 사이트 인증서 검사</h3>
<br>

<p>&lt;웹 서버 인증서 검사를 위한 알고리즘의 수행 단계&gt;</p>
<ol>
<li>날짜 검사<br>브라우저 인증서가 여전히 유효함을 확인  </li>
<li>서명자 신뢰도 검사<br><code>만약 신뢰할 만한 CA가 A 사이트에 서명을 하고, A 사이트가 어떤 사이트 인증서에 서명을 한다면, 브라우저는 그 인증서를 올바른 CA 경로에서 파생된 것으로 받아들일 수 있음</code>  </li>
<li>서명 검사<br><code>서명 기관이 믿을 만하다고 판단하면, 브라우저는 서명기관의 공개키를 서명에 적용하여 그의 체크섬과 비교해봄으로써 인증서의 무결성을 검사</code>  </li>
<li>사이트 신원 검사<br>대부분의 브라우저는 인증서의 도메인 이름이 대화 중인 서버의 도메인 이름과 비교하여 맞는지 검사  </li>
</ol>
<p>-&gt; 몇몇 CA는 와일드카드 표현이 들어있는 인증서를 만들기도 함 (ex. *.google.com)  </p>
<br>
<br>

<h3 id="1477-가상-호스팅과-인증서">14.7.7 가상 호스팅과 인증서</h3>
<p>사용자가 인증서의 이름과 정확히 맞지 않는 가상 호스트 명에 도착했다면 경고 팝업이 뜸<br>-&gt; 가상 호스팅인 경우도 그럴 수 있음<br>-&gt; 이런 문제를 피하기 위해 웹사이트는 보안 트랜잭션을 시작한 모든 사용자를 서버 인증서에 있는 호스트 명으로 리다이렉트   </p>
<br>
<br>

<h3 id="1481-openssl">14.8.1 OpenSSL</h3>
<p>OpenSSLdms SSL과 TLS의 가장 인기 있는 오픈 소스 구현  </p>
<br>
<br>

<h3 id="149-프락시를-통한-보안-트래픽-터널링">14.9 프락시를 통한 보안 트래픽 터널링</h3>
<br>

<p>프록시는 방화벽 라우터가 HTTP 트래픽의 교환을 허락한 유일한 장치<br>-&gt; HTTPS가 프록시와도 잘 동작할 수 있게 하기 위해, <code>클라이언트가 프록시에게 어디에 접속하려고 하는지 말해주는 방법을 약간 수정해야 한다</code><br>-&gt; 인기 있는 기법 중 하나가 <code>HTTPS SSL 터널링 프로토콜</code>  </p>
<br>

<blockquote>
<p><strong><code>HTTPS SSL 터널링 프로토콜</code></strong><br>클라이언트는 먼저 프록시에게 자신이 연결하고자 하는 안전한 호스트와 포트를 말해줌<br><code>클라이언트는 이 내용을 프록시가 읽을 수 있도록 암호화가 시작되기 전의 평문으로 말해줌</code><br>이를 위해 CONNECT 메서드를 이용해서 평문으로 된 endpoint 정보를 전송 -&gt; 이후 클라이언트와 서버 사이에 데이터가 direct로 오갈 수 있는 터널을 만듦<br><img src="https://velog.velcdn.com/images/bahar-j/post/d8d32e9e-a913-438c-9abc-b2b954e7aa49/image.png" alt="">
<img src="https://velog.velcdn.com/images/bahar-j/post/89657d4f-fc90-44ff-8279-64f635f15e27/image.png" alt=""></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP 완벽 가이드] 12장 기본 인증]]></title>
            <link>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-12%EC%9E%A5-%EA%B8%B0%EB%B3%B8-%EC%9D%B8%EC%A6%9D</link>
            <guid>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-12%EC%9E%A5-%EA%B8%B0%EB%B3%B8-%EC%9D%B8%EC%A6%9D</guid>
            <pubDate>Tue, 12 Jul 2022 16:03:12 GMT</pubDate>
            <description><![CDATA[<h3 id="1212-인증-프로토콜과-헤더">12.1.2 인증 프로토콜과 헤더</h3>
<p>인증은 내가 누구인지 증명하는 것.<br>HTTP에는 <code>기본 인증</code>과 <code>다이제스트 인증</code>이 있음</p>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/31f5ae1f-efb9-467f-83dc-4e5f96e13a31/image.png" alt=""></p>
<br>

<p>&lt;기본 인증 예시&gt;
<img src="https://velog.velcdn.com/images/bahar-j/post/75c15586-5f0a-4535-8253-3f817f518549/image.png" alt=""></p>
<p>(a) 요청을 보냄<br>(b) 서버가 사용자에게 인증요구를 보냄. 401 Unauthorized + WWW-Authenticate 헤더(어디서 어떻게 인증할지 설명)<br>(c) 클라이언트는 Authorization 헤더에 인코딩된 비밀번호와 그 외 인증 파라미터(주로 base-64 인코딩)를 담아서 요청을 보냄 -&gt; ...이라고 하지만 당연히 비밀번호를 바로 인코딩해서 이렇게 보내는 경우는 없음. 로그인에서 많이 쓰는 JWT 로그인의 경우는 JWT를 여기에 담기도 함.<br>(d) 응답. 추가적으로 인증 알고리즘에 대한 정보를 Authentication-Info 헤더에 기술할 수도 있음</p>
<br>
<br>

<h3 id="1213-보안-영역">12.1.3 보안 영역</h3>
<p>웹 서버는 기밀 문서를 보안 영역(realm) 그룹으로 나눔<br>-&gt; realm 파라미터를 WWW-Authenticate에 함께 기술<br><img src="https://velog.velcdn.com/images/bahar-j/post/f33a5e59-8a68-4d28-a927-1f7a26239421/image.png" alt=""></p>
<br>
<br>

<h3 id="1223-프록시-인증">12.2.3 프록시 인증</h3>
<p>중개 프록시 서버를 통해서도 인증 가능<br>프록시 서버는 접근 정책을 중앙 관리할 수 있기 때문에 단일 관리 포인트로 많이 사용<br>웹 서버 인증과 헤더명 및 상태 코드가 조금 다름<br><img src="https://velog.velcdn.com/images/bahar-j/post/d2244f61-4af2-43cb-b578-2ec41cc47f66/image.png" alt=""></p>
<br>
<br>


<h3 id="123-기본-인증의-보안-결함">12.3 기본 인증의 보안 결함</h3>
<p>기본 인증은 많은 보안 결함을 가지고 있기 떄문에, 모든 HTTP 트랜잭션을 SSL 암호화 채널을 통해 보내거나, 보안이 더 강화된 다이제스트 인증 같은 프로토콜을 사용하는 것이 좋음.  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP 완벽 가이드] 11장 클라이언트 식별과 쿠키]]></title>
            <link>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-11%EC%9E%A5-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%8B%9D%EB%B3%84%EA%B3%BC-%EC%BF%A0%ED%82%A4</link>
            <guid>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-11%EC%9E%A5-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%8B%9D%EB%B3%84%EA%B3%BC-%EC%BF%A0%ED%82%A4</guid>
            <pubDate>Tue, 12 Jul 2022 16:02:07 GMT</pubDate>
            <description><![CDATA[<h3 id="113-클라이언트-ip-주소">11.3 클라이언트 IP 주소</h3>
<p>서버에서 클라이언트 IP를 알 수 있는 방법</p>
<ul>
<li>nginx 같은 웹 서버에서는 기본적으로 remote_addr 헤더를 통해 클라이언트 IP를 얻을 수 있음</li>
<li>그러나 nginx에 도달하기 전에 프록시, 로드밸런서 등을 거치는 경우에는 이러한 클라이언트 IP가 프록시의 IP로 변경될 수 있음   </li>
<li><blockquote>
<p>이 문제를 해결하기 위해 Client-ip, X-Forwarded-For 같은 HTTP 확장 헤더를 사용  </p>
</blockquote>
</li>
</ul>
<br>

<blockquote>
<p><code>로드밸런서의 Inline(Proxy) / DSR Mode</code><br><img src="https://velog.velcdn.com/images/bahar-j/post/83ad9d91-0849-4d2a-91d9-13966ace389b/image.jpeg" alt="">
왼쪽은 서버에서 <code>나가는 트래픽(outbound traffic)도 LB를 거치는</code> Inline 모드.<br>오른쪽은 서버에서 <code>나가는 트래픽이 Direct Server Return하는</code> DSR 모드.<br>보통 서버에서 나갈 때 LB를 한 번 더 거치며 생기는 병목을 해소하기 위해 많은 기능을 필요로 하지 않는 경우에는 더 많은 트래픽을 감당할 수 있는 DSR 모드를 사용.<br>-&gt; Inline 모드일 경우 client ip가 로드밸런서의 ip로 바뀌는 문제를 가지지만, <code>DSR 모드에서는 client ip가 그대로 유지되어 LB에서 별다른 헤더 추가 없이도 nginx에서 remote_addr로 바로 받아올 수 있음</code> (Inline 모드일 경우 LB에서 client IP를 식별할 수 있는 HTTP 헤더를 따로 추가해줌)  </p>
</blockquote>
<br>
<br>

<h3 id="웹-사이트가-사용자의-세션을-트래킹하는-방법">웹 사이트가 사용자의 세션을 트래킹하는 방법</h3>
<p>cf) 세션 : 분리된 HTTP 트랜잭션을 하나로 모아주는 개념</p>
<ol>
<li>뚱뚱한 URL   </li>
</ol>
<p>-&gt; request param에 이전 트랜잭션 정보를 계속 담아주는 방식<br>-&gt; 점점 url이 비대해짐. 당연히 보안상 문제.</p>
<br>

<ol start="2">
<li>쿠키 : 클라이언트 PC의 쿠키 파일에 데이터를 저장(브라우저마다 저장 방식은 조금씩 다름)하여 웹 페이지들끼리 공유한다.  <blockquote>
<p>&lt;쿠키를 이용한 데이터 전송 과정&gt;<br>1) 브라우저로 웹 사이트 접속
2) 서버가 데이터를 저장한 쿠키 생성하여 브라우저로 전송 
3) 브라우저는 서버에서 받은 쿠키를 쿠키 파일로 저장 
4) 브라우저가 재접속 
5) 서버는 해당 브라우저에 맞는 데이터 처리 위해 브라우저에게 지난번 받은 쿠키 전송 요청
6) 브라우저는 쿠키 정보를 서버로 전달 
7) 서버가 받은 쿠키 정보로 작업</p>
</blockquote>
</li>
</ol>
<br>

<blockquote>
<p>&lt;쿠키의 종류&gt;</p>
<p>1) Persistence 쿠키</p>
<ul>
<li>클라이언트 디스크에 파일로 정보 저장 (컴퓨터를 재시작해도 남아있음)</li>
<li>사용자가 만료 시간 지정 가능</li>
<li>setMaxAge()(쿠키 유효 시간 설정) 메서드에 인자 값으로 양수 설정하여 파일에 저장하면 생성됨
2) Session 쿠키</li>
<li>브라우저가 사용하는 메모리에 생성, 브라우저 종료 시 자동 소멸.</li>
<li>세션(Session)과 같이 사용됨</li>
<li>setMaxAge() 메서드에 인자 값으로 음수를 설정하거나 해당 메서드를 사용하지 않는 경우 Session 쿠키로 저장됨</li>
</ul>
</blockquote>
<br>

<ol start="3">
<li>세션 : 서버의 메모리에 데이터를 저장하여 웹 페이지들이 공유할 수 있도록 한다.</li>
</ol>
<ul>
<li>세션은 브라우저당 한 개, 즉 사용자당 한 개가 생성</li>
<li>서버가 여러개일 경우 각 서버의 세션이 공유되지 않아 레디스를 session storage로 씀 (sticky session이라는 것도 있지만 잘 쓰지는 않음)</li>
</ul>
<br>
<br>

<h3 id="쿠키와-광고">쿠키와 광고</h3>
<p>쿠키는 사용자가 방문한 웹사이트에서 사용자에게 편리한 기능을 위해 만드는 <code>First Party Cookie</code>와 보통 광고 업체에서 사용자 정보 트래킹을 위해 만드는 <code>Third Party Cookie</code>가 있다.<br>써드 파티 쿠키는 누구나 만들 수 있지만, 퍼스트 파티 쿠키는 웹 사이트의 host만이 만들 수 있다. 쿠키는 브라우저를 방문했을 때 만들어지고, 페이지를 옮겨다녀도 남아있으며, 마지막으로 방문한 날짜로부터 유효기간이 계속 늘어나는 등의 방식으로 사용자 정보를 추적하는데, 써드 파티 쿠키 역시 이런 방식으로 광고를 위한 데이터를 수집한다.<br>애플에는 쿠키와 유사하게 사용자를 트래킹할 수 있는 고유식별자(IDFA)가 있다.<br>한편 크롬을 비롯한 많은 브라우저들이 써드 파티 쿠키를 개인정보 침해 문제를 이유로 2022-2023 안에 단계적으로 지원하지 않기로 결정했다. 애플 또한 IDFA를 default 옵션으로 제공하지 않고 앱을 사용하는 사용자에게 동의 여부를 묻도록 방침을 바꿨다.<br><a href="https://termly.io/resources/articles/first-party-cookies-vs-third-party-cookies/">First-Party vs. Third-Party Cookies: The Differences Explained</a><br><a href="https://report.roa.ai/article/171765">비판 여론으로 쿠키 지원 중단 연기한 구글, FLoC 관련 쟁점 총정리</a> -&gt; 구글에서 대체제로 제시한 FLoC</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP 완벽 가이드] 10장 HTTP 2.0]]></title>
            <link>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-10%EC%9E%A5-HTTP-2.0</link>
            <guid>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-10%EC%9E%A5-HTTP-2.0</guid>
            <pubDate>Tue, 12 Jul 2022 16:01:04 GMT</pubDate>
            <description><![CDATA[<h3 id="101-http20의-등장-배경">10.1 HTTP/2.0의 등장 배경</h3>
<p><code>HTTP/1.1</code>은 <code>구현의 단순성과 접근성</code>에 주안점을 둠<br>-&gt; 성능이 희생됨 (ex. 응답을 받아야만 그 다음 요청을 보낼 수 있음)<br>-&gt; 이런 문제를 해결하기 위해 병렬 커넥션(커넥션을 동시에 여러개 맺는건데, 근본적인 해결책은 아님), 파이프라인 커넥션(사용되지 않음) 등이 도입되었지만 해결되지 않음<br><br>
이런 이유로 등장한 것이 구글의 <code>SPDY(스피디) 프로토콜</code><br>-&gt; 하나의 TCP 커넥션에서 여러 요청을 동시에 보낼 수 있음(멀티플렉싱 가능) &amp; 서버 푸시 가능<br>-&gt; 2012년 10월 3일, HTTP 작업 그룹은 SPDY를 기반으로 HTTP/2.0 프로토콜을 설계하기로 결정함  </p>
<br>
<br>
<br>

<h2 id="103-http11과의-차이점">10.3 HTTP/1.1과의 차이점</h2>
<h3 id="1031-프레임">10.3.1 프레임</h3>
<p>HTTP/2.0은 모든 메시지를 프레임에 담아 전송
<img src="https://velog.velcdn.com/images/bahar-j/post/b385ca49-9fb9-40a3-aa60-19b290ebcf0d/image.png" alt=""></p>
<br>
<br>

<h3 id="1032-스트림과-멀티플렉싱">10.3.2 스트림과 멀티플렉싱</h3>
<p><code>스트림</code>은 HTTP/2.0 커넥션을 통해 클라이언트와 서버 사이에서 교환되는 프레임들의 독립된 <code>양방향 시퀀스</code>이다.<br>한쌍의 HTTP 요청과 응답은 하나의 스트림을 통해 이루어진다.  </p>
<br>

<blockquote>
<p>HTTP/1.1의 특징
하나의 TCP 커넥션을 통해 요청을 보냈을 때, 그에 대한 응답이 도착하고 나서야 같은 TCP 커넥션으로 다시 요청을 보낼 수 있다<br>-&gt; 그래서 보통 한번에 여러개의 TCP 커넥션을 만들어 동시에 여러 개의 요청을 보낸다.<br>-&gt; 그렇지만 TCP 커넥션을 무한정 만들 수는 없고, 오늘날의 웹 페이지는 한번에 수십 수백건의 요청을을 보내야한다. 
(-&gt; 파이프라인 커넥션이 있지만, 이건 단점이 있어 널리 사용되지 않는다.)</p>
</blockquote>
<br>

<p><strong><code>HTTP/2.0의 특징</code></strong></p>
<ul>
<li>하나의 커넥션에서 여러 개의 스트림이 동시에 열릴 수 있다(<code>멀티 플렉싱</code>)  </li>
<li><code>스트림은 우선순위를 가질 수도 있다</code>(보다 중요한 리소스, 예를 들어 이미지보다는 html 파일에 더 높은 우선순위를 줄 수 있다. 그러나 <code>우선순위에 따르는 것은 의무사항은 아님</code>)</li>
<li><code>모든 스트림은 31비트의 무부호 정수로 된 고유한 식별자</code>를 갖는다 (스트림이 클라이언트에 의해 초기화되었다면 이 식별자는 반드시 홀수, 서버라면 반드시 짝수 / 새로 생성되는 스트림의 식별자는 이전 스트림 혹은 예약된 스트림보다 커야함 -&gt; 규칙을 어기는 식별자를 받으면 PROTOCOL_ERROR라는 응답 코드의 커넥션 에러로 응답)</li>
<li>서버와 클라이언트는 <code>협상 없이 스트림을 만듦</code> (ACK 필요 없음)  </li>
<li><code>한번 사용된 스트림 식별자는 다시 사용될 수 없음</code> -&gt; 식별자가 고갈될 때에는 커넥션을 다시 맺으면 됨</li>
<li><code>동시에 여러개의 스트림을 이용하면 스트림이 블록될 우려</code>가 있음 -&gt; WINDOW_UPDATE 프레임을 이용한 흐름제어를 통해 스트림이 서로 간섭해서 망가지는 것을 막아줌</li>
</ul>
<br>
<br>

<h3 id="1033-헤더-압축">10.3.3 헤더 압축</h3>
<p><code>HTTP/2.0은 1.1과 달리 헤더를 압축</code> -&gt; 예전과 달리 하나의 웹페이지가 요청을 수십 수백개 보내기 때문에 헤더의 크기가 크면 회전 지연, 대역폭에 영향
HAPCK 명세에 정의된 헤더 압축 방법으로 압축한 후 헤더 블록 조각들로 쪼개져서 전송됨 -&gt; 받는 쪽에서 복원 (헤더를 쓰지 않는 경우라도 무조건 압축 해제를 수행해야함. 그럴 수 없다면 COMPRESSION_ERROR와 함께 커넥션을 끊어야함)  </p>
<br>
<br>

<h3 id="1034-서버-푸시">10.3.4 서버 푸시</h3>
<ul>
<li>HTTP/2.0에서 서버는 클라이언트가 요청하기 전에 리소스를 클라이언트에 푸시할 수 있다(예를 들어 html 문서를 요청받았다면 해당 문서가 링크하고 있는 이미지, css, javascript 파일 등을 푸시)  </li>
<li>리소스를 푸시하려는 서버는 먼저 클라이언트에 <code>PUSH_PROMISE 프레임을 보내 푸시할 것임을 알려야 한다</code>(클라이언트가 해당 자원을 별도로 요청하는 상황을 막기 위해) -&gt; 클라이언트가 이를 받으면 해당 프레임의 스트림은 클라이언트 입장에서 &#39;reserved(remote)&#39; 상태가 된다. -&gt; 클라이언트는 <code>RST_STREAM 프레임을 보내 푸시를 거절</code>할 수 있다.</li>
<li>서버 푸시를 사용하기로 했더라도, 중간의 프록시가 훼방을 놓을 수 있다</li>
<li>서버는 <code>오직 안전하고, 캐시 가능하고, 본문을 포함하지 않는 요청에 대해서만 푸시를 할 수 있다</code></li>
<li>푸시할 리소스는 클라이언트가 명시적으로 보낸 요청과 연관된 것이어야 한다 -&gt; 원 요청을 위해 만들어진 스트림을 통해 PUSH_PROMISE 프레임을 보냄
클라이언트는 반드시 서버가 푸시한 리소스를 CORS 정책에 따라 검사해야 한다.</li>
<li>서버 푸시를 끄고 싶다면 SETTING_ENABLE_PUSH를 0으로 설정</li>
</ul>
<br>
<br>
<br>

<h2 id="104-알려진-보안-이슈">10.4 알려진 보안 이슈</h2>
<h3 id="1041-중개자-캡슐화-공격intermediary-encapsulation-attacks">10.4.1 중개자 캡슐화 공격(Intermediary Encapsulation Attacks)</h3>
<p>HTTP/2.0은 헤더 필드로 어떤 문자열이든 허용한다. -&gt; 따라서 HTTP/2.0 메시지를 중간의 프록시가 HTTP/1.1로 변환할 때 메시지가 위조될 가능성이 있다 (반대로 HTTP/1.1 -&gt; 2.0으로 변환할 때는 이런 문제가 없음)</p>
<br>
<br>

<h3 id="1042-긴-커넥션-유지로-인한-개인정보-누출-우려">10.4.2 긴 커넥션 유지로 인한 개인정보 누출 우려</h3>
<p>HTTP/2.0은 1.1보다 훨씬 긴 시간 커넥션을 유지한다. 어떤 사용자가 브라우저를 사용할 때 그 사용자는 이전에 브라우저를 사용했던 사용자가 무엇을 했는지 알아낼 가능성이 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP 완벽 가이드] 9장 웹 로봇
]]></title>
            <link>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-9%EC%9E%A5</link>
            <guid>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-9%EC%9E%A5</guid>
            <pubDate>Tue, 12 Jul 2022 16:00:01 GMT</pubDate>
            <description><![CDATA[<h3 id="942-웹-사이트와-robotstxt-파일들">9.4.2 웹 사이트와 robots.txt 파일들</h3>
<p>웹 관리자는 웹 사이트의 모든 콘텐츠에 대한 차단 규칙을 종합적으로 기술한 robots.txt 파일을 생성할 책임이 있다.<br>-&gt; 없다면 로봇은 제약없이 사이트에 접근한다.<br>-&gt; 401 혹은 403 권한 없음으로 응답하면 로봇은 해당 사이트로의 접근을 아예 해서는 안된다.<br>-&gt; 503 일시적 실패라면 일정 시간 후 다시 시도한다<br>-&gt; 3XX 리다이렉션 응답이라면 로봇은 리소스가 발견될 때까지 리다이렉트를 따라간다  </p>
<br>

<p><strong>HTTP 크롤러의 요청 예시</strong></p>
<pre><code class="language-yml">GET /robots.txt HTTP/1.0
Host: www.joes-hardware.com
User-Agent: Slurp/2.0
Date: Wed Oct 3 20:22:48 EST 2001</code></pre>
<p><strong>robots.txt 예시</strong></p>
<pre><code class="language-yml"># slurp, webcrawler가 우리 사이트의 공개된 영역을 크롤링하는 것을 허용
User-Agent: slurp
User-Agent: webcrawler
Disallow: /private

User-Agent: *
Disallow:</code></pre>
<p>-&gt; 이때 로봇은 자신이 이해할 수 없는 필드는 무시해야함 (보수적으로 동작)  </p>
<br>
<br>
<br>


<h3 id="943-html-로봇-제어-meta-태그">9.4.3 HTML 로봇 제어 META 태그</h3>
<p>html의 meta 태그를 통해서 로봇이 개별 페이지에 접근하는 것을 좀 더 직접적으로 제한할 수 있다.  </p>
<p>ex)</p>
<pre><code class="language-html">&lt;META NAME=&quot;ROBOTS&quot; CONTENT=&quot;NOINDEX&quot;&gt; // 이 페이지를 처리하지 말고 무시
&lt;META NAME=&quot;ROBOTS&quot; CONTENT=&quot;NOFOLLOW&quot;&gt; // 이 페이지가 링크한 페이지를 무시
&lt;META NAME=&quot;ROBOTS&quot; CONTENT=&quot;INDEX&quot;&gt; // 이 페이지의 콘텐츠를 인덱싱해도 됨
&lt;META NAME=&quot;ROBOTS&quot; CONTENT=&quot;FOLLOW&quot;&gt; // 이 페이지가 링크한 페이지를 크롤링해도 됨
&lt;META NAME=&quot;ROBOTS&quot; CONTENT=&quot;NOARCHIVE&quot;&gt; // 이 페이지의 캐시를 만들어서는 안됨
&lt;META NAME=&quot;ROBOTS&quot; CONTENT=&quot;ALL&quot;&gt; // ALL=INDEX + FOLLOW   
&lt;META NAME=&quot;ROBOTS&quot; CONTENT=&quot;NONE&quot;&gt; // NOINDEX + NOFOLLOW</code></pre>
<br>
<br>
<br>

<h3 id="96-검색-엔진">9.6 검색 엔진</h3>
<p>웹 로봇을 가장 광범위하게 사용하는 것이 <code>인터넷 검색엔진</code><br>-&gt; 웹 크롤러들이 검색엔진에게 웹에 존재하는 문서들을 가져다줌<br>-&gt; 검색엔진은 이를 이용해서 어떤 문서에 어떤 단어들이 존재하는지 index를 생성  </p>
<br>
<br>

<h3 id="962-현대적인-검색엔진-아키텍처">9.6.2 현대적인 검색엔진 아키텍처</h3>
<p>오늘날 검색엔진들은 전 세계 웹 페이지들에 대해 <code>풀 텍스트 색인(full-text indexes)</code>이라고 불리는 복잡한 로컬 데이터베이스를 생성<br>-&gt; 그러나 웹페이지는 매 순간 변화하기 때문에 풀 텍스트 색인은 <code>웹의 특정 순간에 대한 스냅샷</code>에 불과함</p>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/ff58b7a2-e423-4510-af6a-c912e8a17965/image.png" alt=""></p>
<p>-&gt; 게이트웨이 프로그램은 사용 검색 질의를 추출하고 풀 텍스트 인덱스 검색을 할 때 사용되는 표현식으로 변환함  </p>
<br>
<br>

<h3 id="962-현대적인-검색엔진-아키텍처-1">9.6.2 현대적인 검색엔진 아키텍처</h3>
<p>풀 텍스트 인덱스는 단어 하나를 입력받아 그 단어를 포함하고 있는 문서를 즉각 알려줄 수 있는 데이터베이스<br><img src="https://velog.velcdn.com/images/bahar-j/post/45166756-4a65-49c7-987f-746c1cfaea73/image.png" alt=""></p>
<br>
<br>

<h3 id="965-검색-결과를-정렬하고-보여주기">9.6.5 검색 결과를 정렬하고 보여주기</h3>
<p>검색을 했을 때 보통 정확도 등의 순서로 보여주는데,<br>이를 위해 많은 검색 엔진은 웹 크롤링에서 얻은 정보(ex. 해당 페이지를 가리키는 링크들의 수)를 활용함<br>-&gt; 대부분 웹 사이트 운영자들은 이런 알고리즘을 이용해 자신의 페이지를 상위 랭크 시키려 노력함  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 캐시 (HTTP 캐시)]]></title>
            <link>https://velog.io/@bahar-j/%EC%9B%B9-%EC%BA%90%EC%8B%9C-HTTP-%EC%BA%90%EC%8B%9C</link>
            <guid>https://velog.io/@bahar-j/%EC%9B%B9-%EC%BA%90%EC%8B%9C-HTTP-%EC%BA%90%EC%8B%9C</guid>
            <pubDate>Wed, 29 Jun 2022 14:31:52 GMT</pubDate>
            <description><![CDATA[<p>백엔드에 레디스가 있다면 프론트엔드에는 웹 캐시(http cache)가 있다. </p>
<blockquote>
<p>웹 캐시의 종류
<code>브라우저 캐시</code> : 사용자 컴퓨터의 로컬 저장소에 이전 방문한 웹페이지의 정적 자원을 저장해 사용한다.
<code>프록시 웹 캐시</code> : 클라이언트와 origin 서버 간의 프록시
-&gt; ex) CDN : 콘텐츠 전송 네트워크(Content Distribution Network). 이를 통해 트래픽을 지역화</p>
</blockquote>
<p>웹 캐시는 캐시에 대한 정보를 전달하기 위해(캐시의 유효기간, 업데이트 날짜 등) http 헤더를 적극적으로 활용한다.</p>
<p>예를 들어, 브라우저를 렌더링하는데 다음과 같은 스크립트가 있다고 하자</p>
<pre><code class="language-html">&lt;script src=&quot;test.js&quot;&gt;&lt;/script&gt;</code></pre>
<p>이런 스크립트 태그에서 필요로 하는 js, 그리고 css, 이미지 등 원본을 직접 변경하지 않는한, 동적으로 변경될 가능성이 없는 리소스를 정적 파일이라고 부른다.
그리고 이런 태그가 있을 때에, 브라우저는 웹 서버에 test.js를 GET으로 요청한다(image, 음악 등도 같은 방식)
그럼 이때 요청을 받은 웹 서버(ex.nginx)는 해당 요청을 원본 파일을 지니고 있는 서버로 넘겨줄것인지,
아니면 브라우저가 캐싱하고 있는 데이터를 그대로 쓰라고 할 것인지를 결정한다.</p>
<p>정적 파일에 변경 사항이 없고 캐시에 데이터가 있는 경우, 
nginx는 이를 파악하여 304 NOT MODIFED 응답(원본 리소스에 비해 매우 작음)을 보낸다.</p>
<p>이와 같은 동작을 위해 nginx를 설정해주는 방법은 다음 블로그에서 확인!</p>
<p><a href="https://linuxhint.com/cache-static-resources-https-nginx/">How to cache static resources using HTTP caching in Nginx</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP 완벽 가이드] 8장 통합점: 게이트웨이, 터널, 릴레이]]></title>
            <link>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-8%EC%9E%A5-%ED%86%B5%ED%95%A9%EC%A0%90-%EA%B2%8C%EC%9D%B4%ED%8A%B8%EC%9B%A8%EC%9D%B4-%ED%84%B0%EB%84%90-%EB%A6%B4%EB%A0%88%EC%9D%B4</link>
            <guid>https://velog.io/@bahar-j/HTTP-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-8%EC%9E%A5-%ED%86%B5%ED%95%A9%EC%A0%90-%EA%B2%8C%EC%9D%B4%ED%8A%B8%EC%9B%A8%EC%9D%B4-%ED%84%B0%EB%84%90-%EB%A6%B4%EB%A0%88%EC%9D%B4</guid>
            <pubDate>Tue, 28 Jun 2022 15:30:41 GMT</pubDate>
            <description><![CDATA[<h3 id="81-게이트웨이">8.1 게이트웨이</h3>
<p>게이트웨이는 HTTP 트래픽을 다른 프로토콜로 자동 변환 </p>
<br>
<br>

<h3 id="82-프로토콜-게이트웨이">8.2 프로토콜 게이트웨이</h3>
<h4 id="1-http--서버-측-웹-게이트웨이">1) HTTP/* : 서버 측 웹 게이트웨이</h4>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/ffe4ec41-821f-4f69-b7d4-e8ebeaf67107/image.png" alt=""></p>
<br>

<h4 id="2-httphttps--서버-측-보안-게이트웨이">2) HTTP/HTTPS : 서버 측 보안 게이트웨이</h4>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/da39a29c-1df5-41b1-878f-12f5b14fc6dc/image.png" alt=""></p>
<br>

<h4 id="3-httpshttp--클라이언트-측-보안-가속-게이트웨이--ssl-오프로딩-ssl-터미네이션">3) HTTPS/HTTP : 클라이언트 측 보안 가속 게이트웨이 (= SSL 오프로딩, SSL 터미네이션)</h4>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/ae63b3d2-acc5-4635-b098-896faf511cf4/image.png" alt=""></p>
<p>-&gt; <strong><code>원 서버의 부하를 줄여주지만, 게이트웨이와 원 서버 사이에 있는 네트워크가 안전한지 확실히 확인하고 써야함</code></strong></p>
<br>
<br>

<h3 id="83-리소스-게이트웨이">8.3 리소스 게이트웨이</h3>
<p><strong><code>애플리케이션 서버도 게이트웨이의 일종</code></strong>. 애플리케이션 서버는 게이트웨이와 목적지 서버를 한 개의 서버로 결합하고 클라이언트와는 HTTP로 통신한다.</p>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/78e18c1c-999a-4189-ad85-118f502fb133/image.png" alt=""></p>
<p>애플리케이션 게이트웨이에서 <strong><code>유명했던 최초의 API는 공용 게이트웨이 인터페이스(Common Gateway Interface, CGI)</code></strong> 였다.</p>
<br>

<blockquote>
<p><code>CGI</code><br>특정 URL에 대한 HTTP 요청에 따라 프로그램을 실행, 프로그램의 출력을 수집, HTTP 응답으로 회신하는데 웹 서버가 사용하는 표준화된 인터페이스 집합</p>
</blockquote>
<br>
<br>

<h3 id="85-터널">8.5 터널</h3>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/b412c80d-c39b-45c7-8517-a61db8483d47/image.png" alt=""></p>
<ul>
<li>HTTP 커넥션을 통해서 HTTP가 아닌 트래픽을 전송하는 데 사용  </li>
<li>HTTP CONNECT 메서드를 사용해서 커넥션을 맺음  </li>
</ul>
<br>

<h3 id="852-데이터-터널링-시간-커넥션-관리">8.5.2 데이터 터널링, 시간, 커넥션 관리</h3>
<p>터널을 통해 전달되는 데이터는 게이트웨이에서 볼 수 없어서, 게이트웨이는 터널을 통해 전달되는 패킷의 순서나 흐름에 대한 어떤 가정도 할 수 없다.  </p>
<br>

<h3 id="853-ssl-터널링">8.5.3 SSL 터널링</h3>
<p>웹 터널은 원래 방화벽을 통해서 암호화된 SSL 트래픽을 전달하기 위해 개발되었다.
터널을 이용하면 SSL 트래픽을 HTTP 커넥션으로 전송하여 80 포트의 HTTP 만을 허용하는 방화벽을 통과시킬 수 있다.
-&gt; 하지만 터널은 악의적인 트래픽이 사내로 유입되는 경로가 될 수 있으므로 주의가 필요하다</p>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/eabea9d2-cd3c-439e-bfc1-2fde90031d5c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bahar-j/post/bccd949b-39b8-49ca-aaed-7456d33fead0/image.png" alt=""></p>
<br>
<br>

<h3 id="86-릴레이">8.6 릴레이</h3>
<p>HTTP 명세를 완전히 준수하지 않는 심플한 HTTP 프록시로, 커넥션을 맺기 위해 HTTP 통신을 한 다음, 바이트를 맹목적으로 전달<br><img src="https://velog.velcdn.com/images/bahar-j/post/5cdb89d9-9ec5-43f0-b7e1-331f07378c2c/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>