<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dayeon-choi.log</title>
        <link>https://velog.io/</link>
        <description>노력하는 초보 개발자</description>
        <lastBuildDate>Fri, 08 Apr 2022 10:12:04 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dayeon-choi.log</title>
            <url>https://velog.velcdn.com/cloudflare/dayeon-choi/0a6b2714-6c1f-44e7-b77c-80881c7d40fc/sample-profile02.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dayeon-choi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dayeon-choi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[벨로그 이전👋]]></title>
            <link>https://velog.io/@dayeon-choi/%EB%B2%A8%EB%A1%9C%EA%B7%B8-%EC%9D%B4%EC%A0%84-qacxkrqj</link>
            <guid>https://velog.io/@dayeon-choi/%EB%B2%A8%EB%A1%9C%EA%B7%B8-%EC%9D%B4%EC%A0%84-qacxkrqj</guid>
            <pubDate>Fri, 08 Apr 2022 10:12:04 GMT</pubDate>
            <description><![CDATA[<p>2021년 고등학교 3학년부터 취업을 하고 적응하는 과정에서
운영하던 벨로그는 해당 게시물을 마지막으로 막을 내리려 한다.</p>
<br/>

<p>연동되어 있는 계정은 학교 계정으로, 연동 계정을 변경할 수 있는 방법이 아직까지는 없는 것 같다🥲
이제는 보다 장기적으로 사용할 수 있는 개인 계정으로 꾸준히 사용할 수 있는 개인 계정으로 다시 생성하는 것이 좋을 것 같다는 생각으로..
눈물을 머금고 새 벨로그 계정과 티스토리로 운영하려고 한다.
그래도 학교 계정으로 시작한 벨로그를 이제는 진짜 개발자로 성장하여 새로운 계정으로 시작한다는게 뭔가 상징적이지 않은가!? 라고 마인드컨트롤 하며.. 진짜 안녕😂</p>
<br/>

<p>개인적으로 개발 &#39;블로그&#39;로 정착하여 사용할 수 있는 공간으로는 티스토리가 좋은 것 같고,
보기에 깔끔하고 예쁘게, 그리고 마크다운으로 바로바로 확인할 수 있는 면에서는 벨로그가 좋은 것 같다.
둘 다 장단점이 명확한 것 같다. 역시 두 마리의 토끼를 한번에 잡을 수는 없는 걸까 마음이 아프다.</p>
<br/>

<p>티스토리: <a href="https://dev-dayeon.tistory.com/">https://dev-dayeon.tistory.com/</a>
새 벨로그: <a href="https://velog.io/@dayeon0_0dev">https://velog.io/@dayeon0_0dev</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[03장] 계획세우기]]></title>
            <link>https://velog.io/@dayeon-choi/03%EC%9E%A5-%EA%B3%84%ED%9A%8D%EC%84%B8%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@dayeon-choi/03%EC%9E%A5-%EA%B3%84%ED%9A%8D%EC%84%B8%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Sun, 20 Mar 2022 15:05:29 GMT</pubDate>
            <description><![CDATA[<h3 id="초기-탐색">초기 탐색</h3>
<ul>
<li>중요한 사용자 스토리를 가능한 한 모두 확정하려고 하지만, 전부는 않는다</li>
<li>스토리 카드에 그 스토리 몇몇 포인트만 적음</li>
</ul>
<br/>

<h3 id="스파이크-분할-속도">스파이크, 분할, 속도</h3>
<ul>
<li>너무 크거나 너무 작은 스토리는 추정하기 어려움</li>
<li>스토리가 분할되거나 합쳐지면 다시 추정해야함</li>
<li>한두개의 스토리로 프로토타입을 만들어 속도를 예측하는 단계를 <strong>스파이크</strong></li>
</ul>
<br/>

<h3 id="릴리즈-계획-세우기">릴리즈 계획 세우기</h3>
<ul>
<li>주어진 속도를 기준으로 비용을 알 수 있음</li>
<li>이 시점에서 정확성은 중요하지 않음</li>
<li>릴리즈 계획은 속도가 점점 더 정확해짐에 따라 조정될 수 있음</li>
</ul>
<br/>

<h3 id="반복-계획-세우기">반복 계획 세우기</h3>
<ul>
<li>보통 2주</li>
<li>모든 스토리 구현이 완료되지 않은 경우에도 반복은 정해진 날짜에 끝남</li>
</ul>
<br/>

<h3 id="태스크-계획-세우기">태스크 계획 세우기</h3>
<ul>
<li>임이의 테스크를 포인트로 추정</li>
<li>개인적인 예산, 어떤 팀원도 자신의 예산보다 더 많은 포인트에 참여하지 않음</li>
<li>그들의 예산이 모두 사용될 때까지 계속</li>
</ul>
<br/>

<h3 id="반환점">반환점</h3>
<ul>
<li>반복이 반쯤 진행됐을 떄 팀은 미팅을 가짐</li>
<li>계획한 스토리의 반 정도가 완료되어있어야 함</li>
</ul>
<br/>

<h3 id="반복">반복</h3>
<ul>
<li>2주마다, 현재 반복이 끝나고 다음 반복 시작</li>
<li>고객은 시연된 프로젝트를 통해 외관, 느낌, 성능을 평가해야 함</li>
</ul>
<br/>

<hr>
<blockquote>
<p><em>반복에서 반복으로, 릴리즈에서 릴리즈로 넘어가면서, 프로젝트는 예측 가능하고 안정적인 리듬을 찾아간다.</em></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[02장] 익스트림 프로그래밍 소개]]></title>
            <link>https://velog.io/@dayeon-choi/02%EC%9E%A5-%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%A6%BC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@dayeon-choi/02%EC%9E%A5-%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%A6%BC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Wed, 02 Mar 2022 12:44:57 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em>개발자로서 우리가 기억해야 할 것은 XP가 마을에서 유일한 게임이 아니라는 것이다.</em></p>
</blockquote>
<hr>
<h2 id="익스트림-프로그래밍-실천방법">익스트림 프로그래밍 실천방법</h2>
<ul>
<li>애자일 방법 중에서도 가장 유명한 익스트림 프로그래밍 (XP)</li>
<li>단순하면서도 서로 의존적인 실천 방법의 집합으로 구성되어 있음</li>
</ul>
<br/>

<h4 id="--고객-팀-구성원">- 고객 팀 구성원</h4>
<ul>
<li>XP 팀의 고객은 기능 요소를 정의하고 우선순위를 매기는 개인 또는 그룹</li>
<li>경우에 따라 고객은 개발자와 동업하는 분석가, 마케팅 전무가의 그룹일 수 있음</li>
<li>고객은 사용자를 대신하는 대리인, 구매자일 수 있으나 XP에서는 누구든 팀의 멤버이며, 팀에서 일할 수 있음</li>
<li>→ 가까이 있을 수 있을 뿐만 아닌, 실제 고객을 대신할 수 있고, 그럴 의지가 있는 사람을 찾아라</li>
</ul>
<br/>

<h4 id="--사용자-스토리">- 사용자 스토리</h4>
<ul>
<li>프로젝트 일정 계획 수립을 위해 요구사항에 대해 알아야하지만 자세히 알 필요는 X</li>
<li>요구사항의 구체적인 세부 내용은 변동적이므로 의미없는 노력일 수 있음</li>
<li>XP를 사용할 땐 고객과 대화함으로 요구사항의 세부 내용에 대한 감은 잡지만, 세부 사항을 기록하지는 않음 &#39;색인 카드 키워드&#39;</li>
</ul>
<br/>

<h4 id="--짧은-반복">- 짧은 반복</h4>
<ul>
<li><strong>반복 계획</strong><ul>
<li>보통 2주 단위로 진행, 마이너 공개 <font color="gray">(최종 제품에 반영 미확)</font></li>
<li>개발자는 이전 반복의 완성도를 측정하여 반복의 예산 수립, 고객은 예산 이하의 한도로 반복 스토리 선택</li>
<li>반복 시작 후, 고객은 반복 동안 스토리 정의나 우선순위를 바꾸지 않는 것에 동의</li>
</ul>
</li>
<li><strong>릴리즈 계획</strong><ul>
<li>릴리즈는 대게 3개월 의미, 메이저 공개 <font color="gray">(최종 제품에 반영)</font></li>
<li>XP 팀은 종종 다음 약 6번의 반복 일정을 정밀하게 표현한 릴리즈 계획을 수립</li>
<li>이전 릴리즈의 완성도를 측정하여 릴리즈 예산 수립, 고객은 구현 스토리 순서 결정 가능</li>
</ul>
</li>
</ul>
<br/>

<h4 id="--인수-테스트">- 인수 테스트</h4>
<ul>
<li>사용자 스토리의 세부 사항은 고객이 명시한 인수 테스트의 형태로 기록</li>
<li>인수 테스트는 자동, 반복적으로 실행될 수 있는 스크립트 언어의 한 종류로 작성</li>
<li>시스템이 고객이 명시한 대로 동작하는지 여부를 검증 <font color="gray">(EX: QA 부서 운영..)</font></li>
<li>테스트를 통과하면 통과한 테스트의 본문에 추가되고 다시 실패하는 것이 허용되지 않아 시스템 다운을 방지</li>
</ul>
<br/>

<h4 id="--짝-프로그래밍">- 짝 프로그래밍</h4>
<ul>
<li>모든 운영 코드는 같은 워크스테이션으로 일하는 프로그래머 짝들에 의해 작성</li>
<li>코드 작성 멤버와 입력된 코드를 보며 에러와 개선점을 찾는 멤버의 긴밀한 상호작용</li>
<li>역할은 자주 바뀜, 한 시간 동안 몇번이라도 교대로 작업 가능</li>
</ul>
<br/>

<h4 id="--테스트-주도-개발">- 테스트 주도 개발</h4>
<ul>
<li>모든 운영 코드는 실패하는 단위 테스트를 통과하기 위해 작성됨</li>
<li>테스트 케이스와 코드를 작성하는 사이의 간격은 1분 정도로 매우 빠름, 함께 진화, 테스트 케이스가 코드보다 약간 앞서가는 정도</li>
<li>모듈 별 독립적으로 테스트될 수 있게 코드 분리 필요</li>
<li>리펙토링 용이하게 만듬</li>
</ul>
<br/>

<h4 id="--공동-소유권">- 공동 소유권</h4>
<ul>
<li>개인이 어떤 한 모듈이나 기술에 대해 책임을 지거나 <strong>더한 권한을 갖지 않음</strong></li>
<li>XP가 전문성을 부정한다? → X. 팀원들의 역량 강화와 넓은 전문성 부여 가능</li>
</ul>
<br/>

<h4 id="--지속적인-통합">- 지속적인 통합</h4>
<ul>
<li>프로그래머는 자신의 코드를 체크인하고 하루에 몇 번씩 그것을 통합</li>
<li>첫 번째로 체크인한 사람을 우선으로 나머지 사람의 코드를 병합</li>
<li>XP 팀은 <strong>비차단 소스 제어 방식</strong> <font color="gray">(nonblocking source control, 아무때나 어떤 모듈이라도 체크아웃하도록 허용)</font></li>
<li>프로그래머가 모듈을 수정하고 난 뒤에 그것을 다시 체크인하려면, 먼저 그 모듈을 체크인한 다른 사람이 수정한 부분과 병합할 준비가 되어있어야 할 것</li>
<li>모듈에 대한 빈번한 체크 필요</li>
</ul>
<br/>

<h4 id="--지속-가능한-속도">- 지속 가능한 속도</h4>
<ul>
<li>빨리 골인하려면 팀이 지속 가능한 속도로 달려 에너지와 경각심을 보존해야 함</li>
<li>XP 규칙은 팀이 초과 근무를 하지 않도록 하는 것, 예외는 릴리즈 마지막 주!</li>
</ul>
<br/>

<h4 id="--열린-작업-공간">- 열린 작업 공간</h4>
<ul>
<li>두서너개의 워크스테이션이 설치된 테이블, 짝이 나란히 앉을 수 있는 의자, 벽엔 상황 차트, 태스크 명세, UML 다이어그램..., 낮은 웅성거림</li>
<li>상황실이라는 환경에서 일하는 것이 두 배 정도 생산성을 향상할 수 있다고 함</li>
</ul>
<br/>

<h4 id="--계획-세우기-게임">- 계획 세우기 게임</h4>
<ul>
<li>업무와 개발의 <strong>책임 분리</strong>가 계획 세우기 게임의 정수</li>
<li>업무 관련 인력은 기능 요소가 얼마나 중요한지 결정, 개발자는 그 기능 요소를 구현하는 데 얼마나 비용이 들 것인지 결정</li>
</ul>
<br/>

<h4 id="--단순한-설계">- 단순한 설계</h4>
<ul>
<li>XP 팀은 그들의 설계를 가능한 한 단순하고 표현적으로 만듦</li>
<li>반복에 대해 작업하기로 계획했던 스토리에만 초점을 맞추어 공략</li>
<li>한 반복에서 다음 반복으로 넘어갈 때 <strong>시스템의 설계를 마이그레이션</strong>해서 현재 구현 스토리에 가장 적합한 설계가 되도록 함</li>
<li>3가지 XP 지침<ol>
<li>어떻게든 동작하는 가장 단순한 것을 생각</li>
<li>필요하지 않을 것이라는 가정에서 시작</li>
<li>중복 코드 사용 X</li>
</ol>
</li>
</ul>
<br/>

<h4 id="--리팩토링">- 리팩토링</h4>
<ul>
<li>코드는 부패하기 쉬움, 구조는 퇴화함 → 리펙토링으로 반전!!!!</li>
<li>리팩토링 후엔 단위 테스트 실시, 프로그래머는 1시간 혹은 30분마다 리팩토링 실시</li>
</ul>
<br/>

<h4 id="--메타포">- 메타포</h4>
<ul>
<li>XP의 모든 방식 중 가장 이해하기 어려운 것에 속함</li>
<li>퍼즐을 하나로 묶는 강력한 요소인 전체 그림, 전체 시스템을 하나로 묶는 큰 그림</li>
<li>종종 메타포는 시스템을 이름으로 요약, 시스템의 요소에 기호를 부여하고 그 관계를 정의하는 것을 도와줌</li>
</ul>
<br/>

<hr>
<h2 id="기억에-남는">기억에 남는...</h2>
<blockquote>
<p><em>사용자 스토리 (user story)란 현재 진행 중인 요구사항에 관한 대화의 연상 기호다.</em></p>
</blockquote>
<blockquote>
<p><em>반복 계획(iteration plan)은 개발자가 세운 예산에 따라 고객이 선택한 사용자 스토리의 집합이다.</em></p>
</blockquote>
<blockquote>
<p><em>릴리즈는 절대적인 것이 아니다. 고객은 요구 내용을 언제든지 변경할 수 있다. 스토리를 취소하고, 새로운 스토리를 작성하고, 스토리의 우선순위를 변경할 수도 있다.</em></p>
</blockquote>
<blockquote>
<p><em>짝은 어떤 모듈이라도 점검하고 개선할 권리를 갖는다. 프로그래머는 하나의 개별적인 모듈이나 기술에 대해 개인적으로 책임을 지지 않는다.</em></p>
</blockquote>
<blockquote>
<p><em>소프트웨어 프로젝트는 단거리 경주가 아니라 마라톤이다. 출발선에서 가능한 빠르게 레이스를 시작하는 팀은 결승점에 도달하기 한참 전에 탈진하고 말 것이다.</em></p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[01장] 애자일 실천방법]]></title>
            <link>https://velog.io/@dayeon-choi/1-1%EC%9E%A5-%EC%95%A0%EC%9E%90%EC%9D%BC-%EC%8B%A4%EC%B2%9C%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@dayeon-choi/1-1%EC%9E%A5-%EC%95%A0%EC%9E%90%EC%9D%BC-%EC%8B%A4%EC%B2%9C%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sat, 26 Feb 2022 15:12:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em>교회  첨탑 위의 풍향계가 강철로 만들어졌다 해도, 바람에 따라 움직이는 중요한 기술을 이해하지 않았다면 곧 폭풍에 부서졌을 것이다.</em></p>
</blockquote>
<br/>

<h2 id="애자일-연합">애자일 연합</h2>
<p>소프트웨어 팀이 빠르게 일하고 변화에 반응할 수 있도록 하는 가치와 원칙을 세우기 위한 전문가들의 모임, <strong>애자일 연합</strong></p>
<h4 id="애자일-소프트웨어-개발-선언문">애자일 소프트웨어 개발 선언문</h4>
<ul>
<li><strong>프로세스와 툴보다 개인의 상호작용이 우선</strong><ul>
<li>Tip, 간소하게 시작하라</li>
<li>팀을 구성하는 일은 환경을 구축하는 일보다 더 중요, &quot;대화&quot;</li>
</ul>
</li>
<li><strong>포괄적인 문서보다 동작하는 소프트웨어가 우선</strong><ul>
<li>지나친 문서화는 X</li>
<li>문서와 코드의 동기화, 최대 12~24p의 요약적인 문서, 좋은 기록은 코드와 팀</li>
<li>그 필요가 급박하고 중요하지 않다면 아무 문서도 만들지 마라 <font color="gray">→마틴의 문서화 제1법칙</font></li>
</ul>
</li>
<li><strong>계약 협상보다 고객 협력이 우선</strong><ul>
<li>성공적인 프로젝트를 위해서는 규칙적인 고객의 피드백 필수</li>
</ul>
</li>
<li><strong>계획을 따르는 것보다 변화에 대한 반응이 우선</strong><ul>
<li>탄력적이고 업무와 기술의 변화에 준비가 되어있는 계획</li>
<li>다음 2주간의 세부적인 계획 수립, 다음 3개월간의 개략적인 계획, 그 이후는 아주 대강의 계획</li>
</ul>
</li>
</ul>
<br/>

<h2 id="원칙">원칙</h2>
<ul>
<li>우리의 최고 가치는 유용한 소프트웨어의 빠르고 지속적인 공개를 통한 고객의 만족</li>
<li>개발 후반부의 요구사항 변경 환영, 애자일 프로세스는 고객의 경쟁 우위를 위해 변화를 이용</li>
<li>개발 중인 소프트웨어를 2주에서 2달 사이, 혹은 더 짧은 시간 간격으로 자주 공개</li>
<li>의욕적인 개인들을 중심으로 프로젝트를 구상, 환경과 필요로 하는 지원 제공과 해냄에 대한 믿음</li>
<li>가장 효율 &amp; 효과적인 정보 공유는 직접 일대일로 대화하는 것</li>
<li>개발 중인 소프트웨어가 진척 상황의 일차적 척도</li>
<li>우수 기술과 좋은 설계에 대한 지속적 관심은 속도 향상과 비례</li>
<li>아직 끝내지 않은 일의 양을 최대화하는 예술은 필수</li>
<li>최고의 아키텍처, 요구사항, 설계는 조직적인 팀에서 나옴</li>
<li>팀은 규칙적으로 더 효과적인 방법, 적절한 조율과 조정에 힘써야 함</li>
</ul>
<br/>

<hr>
<br/>

<blockquote>
<p><em>소프트웨어 프로젝트의 과정은 먼 미래까지 계획될 수가 없다.</em></p>
</blockquote>
<blockquote>
<p><em>애자일 팀은 천국에 광대한 시스템을 세우려고 시도하지 않는다. 그보다는 항상 목표와 일치하는 가장 단순한 길을 택한다. 내일의 문제를 예상하는 데 지나친 관심을 두지 않으며, ...</em></p>
</blockquote>
<blockquote>
<p><em>애자일 팀은 자기 조직적인 팀이다</em></p>
</blockquote>
<br/>

<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[14장] 보안 HTTP]]></title>
            <link>https://velog.io/@dayeon-choi/14%EC%9E%A5-%EB%B3%B4%EC%95%88-HTTP</link>
            <guid>https://velog.io/@dayeon-choi/14%EC%9E%A5-%EB%B3%B4%EC%95%88-HTTP</guid>
            <pubDate>Sun, 17 Oct 2021 07:44:21 GMT</pubDate>
            <description><![CDATA[<h1 id="141-http를-안전하게-만들기">14.1 HTTP를 안전하게 만들기</h1>
<ul>
<li>웹은 안전한 방식의 HTTP를 필요로 함</li>
<li>HTTP의 보안 버전은 효율적, 이식성, 관리 용이, 적응력이 요구됨</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><strong>서버 인증</strong>
위조된 서버가 아님을 알 수 있어야 함</li>
<li><strong>클라이언트 인증</strong>
진짜 사용자임을 인증할 수 있어야 함</li>
<li><strong>무결성</strong>
위조된 데이터로부터 안전해야 함</li>
<li><strong>암호화</strong>
서버와 클라이언트는 도청에 대한 걱정 없이 대화 가능해야 함</li>
<li><strong>효율</strong>
저렴한 클라이언트나 서버도 이용할 수 있도록 빠른 알고리즘을 가져야 함</li>
<li><strong>편재성</strong>
프로토콜은 모든 클라이언트 서버에서 지원되어야 함</li>
<li><strong>관리상 확장성</strong>
누구든, 어디서든 즉각적인 보안 통신 가능해야 함</li>
<li><strong>적응성</strong>
현재 알려진 최선의 보안 방법을 지원해야 함</li>
<li><strong>사회적 생존성</strong>
사회의 문화적, 정치적 요구를 만족해야 함</li>
</ul>
<h3 id="1411-https">14.1.1 HTTPS</h3>
<ul>
<li>HTTP를 안전하게 만드는 방식 중 가장 인기있는 것</li>
<li><code>https://</code></li>
<li>모든 HTTP 요청과 응답 데이터는 네트워크로 보내지기 전에 암호화됨</li>
<li>SSL (안전 소켓 계층, Secure Sockets Layer)을 이용하여 구현됨</li>
</ul>
<br/>

<h1 id="142-디지털-암호학">14.2 디지털 암호학</h1>
<blockquote>
</blockquote>
<p><strong>디지털 암호학에서 다루는 내용</strong></p>
<ul>
<li><strong>암호</strong>
텍스트를 아무나 읽지 못하도록 인코딩하는 알고리즘</li>
<li><strong>키</strong>
암호의 동작을 변경하는 숫자로 된 매개변수</li>
<li><strong>대칭키 암호 체계</strong>
인코딩과 디코딩에 같은 키를 제공하는 알고리즘</li>
<li><strong>비대칭키 암호 체계</strong>
인코딩과 디코딩에 다른 키를 사용하는 알고리즘</li>
<li><strong>공개키 암호법</strong>
비밀 메시지를 전달하는 수백만 대의 컴퓨터를 쉽게 만들 수 있는 시스템</li>
<li><strong>디지털 서명</strong>
메시지가 위조 혹은 변조되지 않았음을 입증하는 체크섬</li>
<li><strong>디지털 인증서</strong>
신뢰할 만한 조직에 의해 서명되고 검증된 신원 확인 정보</li>
</ul>
<h3 id="1421-비밀-코드의-기술과-과학">14.2.1 비밀 코드의 기술과 과학</h3>
<ul>
<li>암호법은 메시지 인코딩과 디코딩에 대한 과학이자 기술</li>
<li>메시지의 도청, 변조를 방지하기 위해 사용</li>
<li>누군가가 메시지, 트랜잭션의 저자임을 증명하는 데도 사용</li>
</ul>
<h3 id="1422-암호cipher">14.2.2 암호(cipher)</h3>
<ul>
<li>암호법은 암호라 불리는 비밀 코드에 기반</li>
<li>암호란 메시지를 인코딩하는 어떤 특정한 방법과 나중에 그 비밀 메시지를 디코딩하는 방법</li>
<li>평문 → 암호 적용 → 암호문</li>
</ul>
<h3 id="1423-암호-기계">14.2.3 암호 기계</h3>
<ul>
<li>기술이 진보하며 복잡한 암호로 메시지를 코딩하고 디코딩하는 기계, 암호 기계를 만들기 시작</li>
<li>단순히 회전을 하는 대신 글자들을 대체하고, 그 순서를 바꾸었으며, 가르고 토막냄</li>
</ul>
<h3 id="1424-키가-있는-암호">14.2.4 키가 있는 암호</h3>
<ul>
<li>누군가 기계를 훔치더라도 올바른 다이얼 설정(키 값) 없이는 디코더 동작 불가</li>
<li>암호 매개변수를 키라고 부름</li>
<li>이 가상 암호 기계들은 서로 다른 키 값을 가지고 있기 때문에 제각각 동작</li>
</ul>
<h3 id="1425-디지털-암호">14.2.5 디지털 암호</h3>
<ul>
<li>두 가지의 주요한 발전<ul>
<li>속도 및 기능에 대한 기계 장치의 한계에서 벗어나, 복잡한 인코딩/디코딩 알고리즘 가능</li>
<li>매우 큰 키를 지원하는 것이 가능해져, 무작위 추측 키에 의한 크래킹 어려워짐</li>
</ul>
</li>
</ul>
<br/>

<h1 id="143-대칭키-암호법">14.3 대칭키 암호법</h1>
<ul>
<li>대칭키 암호 알고리즘은 인코딩과 디코딩에 같은 키를 사용</li>
</ul>
<h3 id="1431-키-길이와-열거-공격enumeration-attack">14.3.1 키 길이와 열거 공격(Enumeration Attack)</h3>
<ul>
<li>대부분의 경우, 인코딩 및 디코딩 알고리즘은 공개적으로 알려져 있으므로 키만이 유일한 비밀</li>
<li>무차별로 모든 키 값을 대입해 보는 공격을 열거 공격이라고 함</li>
<li>가능한 키 값이 많을 수록 유리</li>
</ul>
<h3 id="1432-공유키-발급하기">14.3.2 공유키 발급하기</h3>
<ul>
<li>대칭키 암호의 단점 중 하나는 발송자, 수신자가 서로 대화하려면 둘 다 공유키를 가져야한다는 것</li>
<li>기억해야 할 비밀 키가 늘어나게 된다는 단점</li>
</ul>
<br/>

<h1 id="144-공개키-암호법">14.4 공개키 암호법</h1>
<ul>
<li>두 개의 비대칭 키를 사용</li>
<li>인코딩을 위한 하나, 디코딩을 위한 하나</li>
<li>키의 분리는 메시지의 인코딩을 누구나 할 수 있도록 해주는 동시에, 메시지를 디코딩하는 능력은 소유자에게만 부여</li>
</ul>
<h3 id="1441-rsa">14.4.1 RSA</h3>
<blockquote>
</blockquote>
<p><strong>공개키 비대칭 암호의 과제</strong></p>
<ul>
<li><p>공개키(누구나 얻을 수 있음)</p>
</li>
<li><p>가로채서 얻은 암호문의 일부(네트워크를 스누핑해서 획득)</p>
</li>
<li><p>메시지와 그것을 암호화한 암호문(인코더에 임의의 텍스트를 넣고 실행해서 획득)</p>
</li>
<li><p>이 모든 요구를 만족하는 공개키 암호 체계 중 유명한 하나는 MT에서 발명되고 있음</p>
</li>
<li><p>RSA의 소스 코드까지 주어졌다고 하더라도 암호를 크래킹하여 해당하는 개인 키를 찾아내는 것은 매우 어려움</p>
</li>
</ul>
<h3 id="1442-혼성-암호-체계와-세션-키">14.4.2 혼성 암호 체계와 세션 키</h3>
<ul>
<li>공개키의 암호 방식의 알고리즘의 단점은 계산이 느린 경향이 있다는 것</li>
<li>때문에 대칭키와 비대칭 방식을 섞어 사용</li>
</ul>
<br/>

<h1 id="145-디지털-서명">14.5 디지털 서명</h1>
<ul>
<li>누가 메시지를 썼는지 알려주고 그 메시지가 위조되지 않았음을 인증</li>
</ul>
<h3 id="1451-서명은-암호-체크섬이다">14.5.1 서명은 암호 체크섬이다</h3>
<ul>
<li>서명은 메시지를 작성한 저자가 누군지 알려줌</li>
<li>서명은 위조를 방지함</li>
<li>디지털 서명은 보통 비대칭 공개키에 의해 생성</li>
</ul>
<br/>

<h1 id="146-디지털-인증서">14.6 디지털 인증서</h1>
<ul>
<li>흔히 certs라고 불리는 디지털 인증서</li>
<li>신뢰할 수 있는 기관으로 부터 보증받은 사용자나 회사에 대한 정보를 담고 있음</li>
</ul>
<h3 id="1461-인증서의-내부">14.6.1 인증서의 내부</h3>
<ul>
<li>대상의 이름, 유효기간, 인증서 발급자, 인증서 발급자의 디지털 서명</li>
</ul>
<h3 id="1462-x509-v3-인증서">14.6.2 X.509 v3 인증서</h3>
<ul>
<li>불행히, 디지털 인증서에 대한 전 세계적인 단일 표준은 없음</li>
<li>미묘하게 다른 여러가지 스타일의 디지털 인증서들이 존재</li>
</ul>
<h3 id="1463-서버-인증을-위한-인증서-사용하기">14.6.3 서버 인증을 위한 인증서 사용하기</h3>
<ul>
<li>사용자가 HTTPS를 통한 안전한 웹 트랜잭션을 시작할 때, 최신 브라우저는 자동으로 접속한 서버에서 디지털 인증서 가져옴</li>
<li>웹 사이트의 이름과 호스트명, 웹 사이트의 공개키, 서명 기관의 이름, 서명 기관의 서명</li>
</ul>
<br/>

<h1 id="147-https의-세부사항">14.7 HTTPS의 세부사항</h1>
<ul>
<li>HTTPS는 HTTP의 가장 유명한 보안 버전</li>
<li>HTTPS는 HTTP 프로토콜에 대칭, 비대칭 인증서 기반 암호 기법의 강력한 집합을 결합한 것</li>
<li>HTTPS는 인터넷 애플리케이션의 성장 + 웹 기반 전자상거래의 성장을 고속화</li>
</ul>
<h3 id="1471-https-개요">14.7.1 HTTPS 개요</h3>
<ul>
<li>HTTPS는 그냥 보안 전송 계층을 통해 전송되는 HTTP</li>
<li>암호화되지 않은 HTTP 메시지를 TCP를 보내기 전 보안 계층으로 보냄</li>
</ul>
<h3 id="1472-https-스킴">14.7.2 HTTPS 스킴</h3>
<ul>
<li>오늘날 보안 HTTP는 선택, 웹 서버에게 명시 작업이 필요 → URL의 스킴</li>
<li>HTTPS 프로토콜에서 URL의 스킴 접두사는 <code>https</code></li>
</ul>
<h3 id="1473-보안-전송-셋업">14.7.3 보안 전송 셋업</h3>
<ul>
<li>암호화되지 않은 HTTP에서, 클라이언트는 웹 서버의 80번 포트로 TCP 커넥션을 열고, 요청 메시지를 보내고, 응답 메시지를 받고, 커넥션을 닫음</li>
<li>HTTPS에서의 절차는 SSL 보안 계층 때문에 약간 더 복잡</li>
<li>443 포트로 연결, 암호법 매개변수와 교환 키 협상으로 SSL 계층 초기화, 핸드셰이크 완료되면 SSL 초기화 완료, 클라이언트가 요청 메시지를 보안 계층에 보냄</li>
</ul>
<h3 id="1474-ssl-핸드셰이크">14.7.4 SSL 핸드셰이크</h3>
<blockquote>
</blockquote>
<p><strong>핸드셰이크에서 일어나는 일들</strong></p>
<ul>
<li><p>프로토콜 버전 번호 교환</p>
</li>
<li><p>양쪽이 알고 있는 암호 선택</p>
</li>
<li><p>양쪽의 신원 인증</p>
</li>
<li><p>채널을 암호화하기 위한 임시 세션 키 생성</p>
</li>
<li><p>암호화된 HTTP 데이터가 네트워크를 오가기도 전에, SSL은 통신을 시작하기 위해 상당한 양의 핸드셰이크 데이터를 주고 받음</p>
</li>
</ul>
<h3 id="1475-서버-인증서">14.7.5 서버 인증서</h3>
<ul>
<li>SSL은 서버 인증서를 클라이언트로 나르고 다시 클라이언트 인증서를 서버로 날라주는 상호 인증 지원</li>
<li>보안 HTTPS 트랜잭션은 항상 서버 인증서를 요구</li>
<li>서버 인증서는 조직의 이름, 주소, 서버 DNS 도메인 이름, 그 외의 정보를 보여주는 X.509 v3에서 파생된 인증서</li>
</ul>
<h3 id="1476-사이트-인증서-검사">14.7.6 사이트 인증서 검사</h3>
<ul>
<li>최신 웹 브라우저들 대부분은 인증서에 대해 간단하게 기본적인 검사를 하고 그 결과를 더 철저한 검사를 할 수 있는 방법과 함께 사용자들에게 알려줌</li>
<li>날짜 검사 → 서명자 신뢰도 검사 → 서명 검사 → 사이트 신원 검사</li>
</ul>
<h3 id="1477-가상-호스팅과-인증서">14.7.7 가상 호스팅과 인증서</h3>
<ul>
<li>가상 호스트(하나의 서버에 여러 호스트명)으로 운영되는 사이트 보안 트래픽을 다루는 것은 까다로운 경우도 많음</li>
</ul>
<br/>

<h1 id="148-진짜-https-클라이언트">14.8 진짜 HTTPS 클라이언트</h1>
<ul>
<li>SSL은 복잡한 바이너리 프로토콜</li>
</ul>
<h3 id="1481-openssl">14.8.1 OpenSSL</h3>
<ul>
<li>OpenSSL은 SSL과 TLS의 가장 인기 있는 오픈 소스 구현</li>
<li>OpenSSL 프로젝트는, 강렬한 다목적 암호법 라이브러리인 동시에 SSL과 TLS 프로토콜을 구현한 강건하고 완전한 기능을 갖춘 상용 수준의 툴킷을 개발하고자 한 결과물</li>
</ul>
<h3 id="1482-간단한-https-클라이언트">14.8.2 간단한 HTTPS 클라이언트</h3>
<ul>
<li>(생략)</li>
</ul>
<h3 id="1483-우리의-단순한-openssl-클라이언트-실행하기">14.8.3 우리의 단순한 OpenSSL 클라이언트 실행하기</h3>
<ul>
<li>(생략)</li>
</ul>
<br/>

<h1 id="149-프락시를-통한-보안-트래픽-터널링">14.9 프락시를 통한 보안 트래픽 터널링</h1>
<ul>
<li>클라이언트는 종종 그들을 대신하여 웹 서버에 접근해주는 웹 프락시 서버를 이용</li>
<li>HTTPS가 프락시와도 잘 동작할 수 있도록 하기 위해, 클라이언트가 프락시에게 어디에 접속하려고 하는지 말해주는 방법을 약간 수정해야 함 → HTTPS SSL 터널링 프로토콜</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[12장] 기본 인증]]></title>
            <link>https://velog.io/@dayeon-choi/12%EC%9E%A5-%EA%B8%B0%EB%B3%B8-%EC%9D%B8%EC%A6%9D</link>
            <guid>https://velog.io/@dayeon-choi/12%EC%9E%A5-%EA%B8%B0%EB%B3%B8-%EC%9D%B8%EC%A6%9D</guid>
            <pubDate>Sun, 26 Sep 2021 11:07:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>HTTP는 자체적인 인증 관련 기능을 제공한다</p>
<h1 id="121-인증">12.1 인증</h1>
<ul>
<li>인증은 당신이 누구인지 증명하는 것</li>
<li>ex) 자동 전화기 PIN 번호, 비밀번호 입력...</li>
</ul>
<h3 id="1211-http의-인증요구응답-프레임워크">12.1.1 HTTP의 인증요구/응답 프레임워크</h3>
<ul>
<li>HTTP는 사용자 인증을 하는 데 사용하는 자체 인증요구/응답 프레임워크를 제공</li>
<li>과정<ul>
<li>웹 애플리케이션이 HTTP 요청 메시지 받음</li>
<li>서버는 요청 처리 대신 사용자가 누구인지 알 수 있게 <strong>인증 요구</strong>로 응답</li>
<li>사용자가 다시 요청을 보낼 때는 인증 정보를 첨부</li>
</ul>
</li>
</ul>
<h3 id="1212-인증-프로토콜과-헤더">12.1.2 인증 프로토콜과 헤더</h3>
<ul>
<li>HTTP는 필요에 따라 고쳐 쓸 수 있는 제어 헤더를 통해 다른 인증 프로토콜에 맞는 확장 프레임워크 제공</li>
<li>기본 인증</li>
<li>다이제스트 인증</li>
</ul>
<h3 id="1213-보안-영역">12.1.3 보안 영역</h3>
<ul>
<li>HTTP는 각 리소스마다 다른 접근 조건을 다룸</li>
<li>웹 서버는 기밀문서를 보안 영역(realm) 그룹으로 나눔</li>
</ul>
<hr>
<h1 id="122-기본-인증">12.2 기본 인증</h1>
<ul>
<li>기본 인증은 가장 잘 알려진 HTTP 인증 규약 (대부분의 주요 클라이언트, 서버에 구현 되어있음)</li>
<li>서버는 200 대신 401 상태 코드와 함께 클라이언트가 접근하려 했던 보안 영역을 기술해 응답 (인증 요구 시작)</li>
</ul>
<h3 id="1221-기본-인증의-예">12.2.1 기본 인증의 예</h3>
<ul>
<li>인증 요구: 서버→클라이언트<ul>
<li>realm은 요청을 받은 문서 집합의 이름을 따옴표로 감싼 것, 해당 정보를 통해 사용자가 어떤 비밀번호를 사용해야 하는지 알 수 있음</li>
</ul>
</li>
<li>응답: 클라이언트→서버<ul>
<li>네트워크 트래픽에 사용자 이름과 비밀번호가 노출되지 않도록 함</li>
</ul>
</li>
</ul>
<h3 id="1222-base-64-사용자-이름비밀번호-인코딩">12.2.2 Base-64 사용자 이름/비밀번호 인코딩</h3>
<ul>
<li>HTTP 기본 인증은 사용자 이름과 비밀번호를 클론으로 이어서 합치고, base-64 인코딩 메서드로 인코딩</li>
<li>base-64<ul>
<li>8비트 바이트로 이루어진 시퀀스를 6비트 덩어리의 시퀀스로 변환</li>
<li>국제 문자나 HTTP 헤더에서 사용할 수 없는 문자를 포함한 사용자 이름, 비밀번호를 보낼 때 유용</li>
<li>노출 문제 예방</li>
</ul>
</li>
</ul>
<h3 id="1223-프락시-인증">12.2.3 프락시 인증</h3>
<ul>
<li>중개 프락시 서버를 통해 인증 가능</li>
<li>프락시 서버를 거치게 하여 사용자를 인증</li>
<li>프락시 서버에서 접근 정책을 중앙 관리 가능</li>
</ul>
<hr>
<h1 id="123-기본-인증의-보안-결합">12.3 기본 인증의 보안 결합</h1>
<ul>
<li>기본 인증은 SSL 같은 암호 기술과 혼용</li>
<li>기본 인증은 사용자 이름과 비밀번호를 악의적인 개인들에게 숨기려고 암호화 된 데이터 전송과 함께 연계해 사용 가능함</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[11장] 클라이언트 식별과 쿠키]]></title>
            <link>https://velog.io/@dayeon-choi/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/@dayeon-choi/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>Sun, 19 Sep 2021 14:37:29 GMT</pubDate>
            <description><![CDATA[<h2 id="111-개별-접촉">11.1 개별 접촉</h2>
<ul>
<li>HTTP는 익명으로 사용하며 상태가 없고 요청과 응답으로 통신하는 프로토콜임<ul>
<li>서버는 클라이언트가 보낸 요청을 처리하고 나서 그 응답을 클라이언트로 전송</li>
<li>웹 서버는 요청을 보낸 사용자를 식별하거나 방문자가 보낸 연속적인 요청을 추적하기 위해 약간의 정보 이용 가능</li>
</ul>
</li>
<li>현대에는 개인화된 서비스 제공을 원함</li>
</ul>
<h4 id="개별-인사">개별 인사</h4>
<ul>
<li>온라인 쇼핑이 개인에게 맞춰져 있는 것처럼 느끼게 하기 위해 개인 환영 메시지, 내용 제공</li>
</ul>
<h4 id="사용자-맞춤-추천">사용자 맞춤 추천</h4>
<ul>
<li>고객의 흥미가 무엇인지 학습하여 특별한 정보 제공</li>
</ul>
<h4 id="저장된-사용자-정보">저장된 사용자 정보</h4>
<ul>
<li>사용자의 정보를 저장 후 식별하여 사용자가 반복적으로 처리해야 하는 과정들을 생략할 수 있도록 도움</li>
</ul>
<h4 id="세션-추적">세션 추적</h4>
<ul>
<li>HTTP 트랜젝션은 상태가 없음</li>
<li>각 요청은 독립적으로 이루어짐</li>
<li>사용자가 사이트와 상호작용할 수 있도록 사용자의 상태를 남김. 웹 사이트는 사용자에게서 오는 HTTP 트랜젝션을 식별할 방법이 필요</li>
</ul>
<hr>
<h2 id="112-http-헤더">11.2 HTTP 헤더</h2>
<blockquote>
</blockquote>
<p><strong>사용자에 대한 정보를 전달하는 7가지 헤더</strong></p>
<ul>
<li>From<ul>
<li>요청</li>
<li>사용자의 이메일 주소</li>
</ul>
</li>
<li>User-Agent <ul>
<li>요청</li>
<li>사용자의 브라우저</li>
</ul>
</li>
<li>Referer<ul>
<li>요청</li>
<li>사용자가 현재 링크를 타고 온 근원 페이지</li>
</ul>
</li>
<li>Authorization<ul>
<li>요청</li>
<li>사용자 이름과 비밀번호</li>
</ul>
</li>
<li>Client-ip<ul>
<li>확장</li>
<li>클라이언트의 IP 주소</li>
</ul>
</li>
<li>X-Forwarded-For<ul>
<li>확장</li>
<li>클라이언트의 IP 주소</li>
</ul>
</li>
<li>Cookie<ul>
<li>확장</li>
<li>서버가 생성한 ID 라벨</li>
</ul>
</li>
</ul>
<hr>
<h2 id="113-클라이언트-ip-주소">11.3 클라이언트 IP 주소</h2>
<ul>
<li>초기 웹 선구자들은 사용자 식별에 클라이언트 IP 주소를 사용하려 함</li>
<li>안타깝게도 클라이언트 IP 주소로 사용자를 식별하는 방법은 약점들을 가짐<ul>
<li>클라이언트 IP는 사용자가 아닌, 사용하는 컴퓨터를 가리킴. 여러 사람이 같은 컴퓨터를 사용한다면 식별 불가능</li>
<li>로그인하면 동적으로 IP를 할당. 로그인한 시간에 따라 사용자는 매번 다른 주소를 받으므로 식별 불가능</li>
<li>등등 여러 문제들이 존재</li>
</ul>
</li>
</ul>
<hr>
<h2 id="114-사용자-로그인">11.4 사용자 로그인</h2>
<ul>
<li>IP 주소로 사용자를 식별하려는 수동적인 방법보다, 사용자의 이름과 비밀번호로 인증할 것을 요구</li>
<li>사이트에서 한번만 로그인하면 브라우저는 해식별정보 토큰을 헤더에 담아 서버로 전송하여 한 세션 내내 식별 유지 가능</li>
</ul>
<hr>
<h2 id="115-뚱뚱한-url">11.5 뚱뚱한 URL</h2>
<ul>
<li>어떤 웹 사이트는 사용자의 URL마다 버전을 기술하여 식별 및 추적 진행</li>
<li>사용자가 해당 사이트를 돌아다니면, 웹 서버는 URL에 있는 상태 정보를 유지하는 하이퍼링크를 동적으로 생성</li>
<li><strong>사용자의 상태 정보를 포함하는 URL</strong></li>
<li>세션, 방문으로 묶는 용도로 뚱뚱한 URL 사용</li>
</ul>
<blockquote>
</blockquote>
<p><strong>뚱뚱한 URL의 문제점</strong></p>
<ul>
<li>못생긴 URL<ul>
<li>브라우저에 보이는 뚱뚱한 URL은 새 사용자에게 혼란을 줌</li>
</ul>
</li>
<li>공유하지 못하는 URL<ul>
<li>뚱뚱한 URL은 특정 사용자와 세션에 대한 상태 정보를 포함 (유출 가능성)</li>
</ul>
</li>
<li>캐시를 사용할 수 없음<ul>
<li>URL로 만드는 것은 URL이 달라지기 때문에 기존 캐시에 접근할 수 없다는 것을 의미</li>
</ul>
</li>
<li>서버 부하 가중<ul>
<li>서버는 뚱뚱한 URL에 해당하는 HTML 페이지를 다시 그려야 함</li>
</ul>
</li>
<li>이탈<ul>
<li>사용자가 링크를 타고 다른 사이트로 이동하거나 특정 URL을 요청해서 의도치 않게 뚱뚱한 URL을 이탈하기 쉬움</li>
</ul>
</li>
<li>세션 간 지속성의 부재<ul>
<li>사용자가 특정 뚱뚱한 URL을 북마킹하지 않는 이상, 로그아웃하면 모든 정보를 잃음</li>
</ul>
</li>
</ul>
<hr>
<h2 id="116-쿠키">11.6 쿠키</h2>
<ul>
<li>쿠키는 사용자를 식별하고 세션을 유지하는 방식 중에서 현재까지 가장 널리 사용하는 방식</li>
<li>쿠키는 앞서 설명한 문제들을 겪지는 않지만, 쿠키만으로 안되는 일에는 앞서 설명한 기술들을 함께 사용</li>
</ul>
<h3 id="1161-쿠키의-타입">11.6.1 쿠키의 타입</h3>
<ul>
<li>세션 쿠키<ul>
<li>사용자가 사이트를 탐색할 때, 관련한 설정과 선호 사항들을 저장하는 임시 쿠키</li>
<li>브라우저를 닫으면 삭제</li>
</ul>
</li>
<li>지속 쿠키<ul>
<li>삭제되지 않고 더 길게 유지될 수 있음</li>
<li>지속 쿠키는 디스크에 저장되어, 브라우저를 닫거나 재시작하더라도 남아있음</li>
</ul>
</li>
</ul>
<h3 id="1162-쿠키는-어떻게-동작하는가">11.6.2 쿠키는 어떻게 동작하는가</h3>
<ul>
<li>사용자가 웹 사이트에 방문하면 웹 사이트는 서버가 사용자들에게 붙인 모든 (식별)스티커를 읽을 수 있음</li>
<li>웹 서버는 사용자를 식별하기 위한 유일한 값을 쿠키에 할당</li>
<li>쿠키는 어떤 정보든 포함 가능하지만, 서버가 사용자 추적 용도로 생성한 유일한 단순 식별 번호만 포함하기도 함</li>
</ul>
<h3 id="1163-쿠키-상자-클라이언트-측-상태">11.6.3 쿠키 상자: 클라이언트 측 상태</h3>
<ul>
<li><p>쿠키의 기본적인 발상은 브라우저가 서버 관련 정보를 저장하고, 사용자가 해당 서버에 접근할 때마다 그 정보를 함께 전송하게 하는 것</p>
</li>
<li><p>종류</p>
<ul>
<li><strong>구글 크롬 쿠키</strong><ul>
<li>SQLite 파일에 쿠키를 저장</li>
</ul>
</li>
<li><strong>마이크로소프트 인터넷 익스플로러 쿠키</strong><ul>
<li>캐시 디렉터리에 각각의 개별 파일로 쿠키를 저장</li>
<li>파일에 있는 각 쿠키 첫 번째 줄은 쿠키의 이름</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="1164-사이트마다-각기-다른-쿠키들">11.6.4 사이트마다 각기 다른 쿠키들</h3>
<ul>
<li>브라우저는 수백 수천 개의 쿠키를 가지고 있을 수 있지만, 쿠키 전부를 모든 사이트에 보내지는 않음</li>
<li>보통 각 사이트에 2~3개의 쿠키만을 보냄</li>
<li>이유<ul>
<li>쿠키 모두 전달 시 성능 저하</li>
<li>쿠키 대부분은 서버에 특화된 이름/값 쌍을 포함하고 있기 때문에 사이트에서는 인식하지 않는 무의미한 값</li>
<li>특정 사이트에서 제공한 정보를 신뢰하지 않는 사이트에 가져갈 수 있어 잠재적인 개인정보 문제 발생<br/></li>
</ul>
</li>
<li><strong>쿠키 Domain 속성</strong><ul>
<li>서버는 쿠키를 생성할 때 Set-Cookie 응답 해더에 Domain 속성을 기술</li>
<li>어떤 사이트가 그 쿠키를 읽을 수 있는지 제어 가능</li>
</ul>
</li>
<li><strong>쿠키 Path 속성</strong><ul>
<li>웹 사이트 일부에만 쿠키 적용 가능</li>
<li>URL 경로의 앞부분을 가리키는 Path 속성을 기술해서 해당 경로에 속하는 페이지에만 키를 전달</li>
</ul>
</li>
</ul>
<h3 id="1165-쿠키-구성요소">11.6.5 쿠키 구성요소</h3>
<ul>
<li>(생략)</li>
</ul>
<h3 id="1166-version-0넷스케이프-쿠키">11.6.6 Version 0(넷스케이프) 쿠키</h3>
<ul>
<li>최초의 쿠키 명세는 넷스케이프가 정의</li>
<li>응답 헤더와 Cookie 요청 헤더와 쿠키를 조작하는 데 필요한 필드들을 정의<br/></li>
<li><strong>Version 0 Set-Cookie 헤더</strong><ul>
<li>Set-Cookie 헤더는 쿠키의 이름과 값을 가져야 함</li>
</ul>
</li>
<li><strong>Version 0 Cookie 헤더</strong><ul>
<li>클라이언트가 서버에 요청을 보낼 때는 Domain, Path, Secure 필터들이 현재 요청하려고 하는 사이트에 들어맞으면서 아직 파기되지 않은 쿠키들을 함께 보냄</li>
</ul>
</li>
</ul>
<h3 id="1167-version-1-rfc-2965-쿠키">11.6.7 Version 1 (RFC 2965) 쿠키</h3>
<ul>
<li>해당 표준은 원 버전인 넷스케이프 표준보다 좀 더 복잡하며, 아직 모든 브라우저나 서버가 완전히 지원하지는 않음</li>
</ul>
<h3 id="1168-쿠키와-세션-추적">11.6.8 쿠키와 세션 추적</h3>
<ul>
<li>쿠키는 웹 사이트에 수차례 트랜잭션을 만들어내는 사용자를 추적하는 데 사용</li>
<li>브라우저에 입력 
→ 일련의 리다이렉트, URL 리라이트, 쿠키설정 
→ 서버가 식별 정보를 첨부하기 위한 연속적은 트랜젝션 시작</li>
</ul>
<h3 id="1169-쿠키와-캐싱">11.6.9 쿠키와 캐싱</h3>
<ul>
<li>쿠키 트랜잭션과 관련된 문서를 캐싱하는 것은 주의</li>
<li>이전 사용자의 쿠키가 다른 사용자에게 할당돼버리거나, 누군가의 개인 정보가 다른 이에게 노출되는 상황 생길 수 있음</li>
</ul>
<blockquote>
</blockquote>
<p><strong>캐시를 다루는 기본 원칙에 대한 안내</strong></p>
<ul>
<li>캐시되지 말하야 할 문서가 있다면 표시하라</li>
<li>Set-Cookie 헤더를 캐시하는 것에 유의하라</li>
<li>Cookie 헤더를 가지고 있는 요청에 주의하라</li>
</ul>
<h3 id="11610-쿠키-보안-그리고-개인정보">11.6.10 쿠키, 보안 그리고 개인정보</h3>
<ul>
<li>쿠키를 사용하지 않도록 비활성화 시킬 수 있고, 로그 분석 같은 다른 방법으로 대체하는 것도 가능</li>
<li>→ 그 자체가 보안상으로 엄청나게 위험한 것은 아님</li>
<li>원격 DB에 개인 정보 저장, 해당 키 값을 쿠키에 저장하는 방식을 표준으로 사용하면 클라이언트와 서버 사이에 예민한 데이터가 오가는 것 최소화 가능<br/></li>
<li><strong>👉제공하는 개인정보를 누가 받는지 명확히 알고 사이트 개인정보 정책에만 유의한다면, 쿠키 관련 위험성보다 세션 조작이나 트랜잭션의 편리함이 더 큼</strong></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[10장] HTTP/2.0]]></title>
            <link>https://velog.io/@dayeon-choi/10%EC%9E%A5-HTTP2.0</link>
            <guid>https://velog.io/@dayeon-choi/10%EC%9E%A5-HTTP2.0</guid>
            <pubDate>Sun, 19 Sep 2021 09:05:31 GMT</pubDate>
            <description><![CDATA[<h2 id="101-http20의-등장-배경">10.1 HTTP/2.0의 등장 배경</h2>
<ul>
<li>HTTP/1.1의 메시지 포맷은 구현의 단순성, 접근성에 주안성을 두고 최적화</li>
<li>HTTP/1.1 특징 &amp; 문제<ul>
<li>커넥션 하나를 통해 요청 하나를 보내고 그에 대해 응답 하나만 받음</li>
<li>회전 지연(latency) 문제. 응답 받아야만 다음 요청을 보낼 수 있음</li>
<li>해결을 위한 병렬 커넥션, 파이프라인 커넥션이 도입으로는 부족</li>
</ul>
</li>
<li>해결 노력<ul>
<li>HTTP-NG 프로젝트</li>
<li>WAKA 프로토콜 제안</li>
<li>S+M(Speed+Mobility) 프로토콜 개발</li>
<li>SPDY(스피디)</li>
<li>→ SPDY의 초안을 가져와 HTTP/2.0 만들기 시작</li>
</ul>
</li>
</ul>
<hr>
<h2 id="102-개요">10.2 개요</h2>
<ul>
<li>HTTP/2.0은 서버와 클라이언트 사이의 TCP 커넥션 위에서 동작<ul>
<li>TCP 초기화는 클라이언트가 함</li>
<li>요청과 응답은 정의된(&lt;=16383byte) 한 개 이상의 프레임에 담김</li>
<li>HTTP 헤더는 압축되어 담김</li>
<li>프레임들에 담김 요청과 응답은 스트림을 통해 보내짐</li>
<li>하나의 커넥션 위에 여러 개의 스트림이 동시에 만들어질 수 있음
→ 여러 개의 요청/응답 동시 처리 가능</li>
</ul>
</li>
<li>기존 웹 애플리케이션들과 호환성을 최대한 유지하기 위해 요청/응답 메시지 의미를 HTTP/1.1과 같도록 유지</li>
</ul>
<hr>
<h2 id="http11과의-차이점">HTTP/1.1과의 차이점</h2>
<h3 id="1031-프레임">10.3.1 프레임</h3>
<ul>
<li>HTTP/2.0에서 모든 메시지는 프레임에 담겨 전송</li>
<li>모든 프레임은 <strong>8바이트</strong> 크기의 <strong>헤더</strong>로 시작</li>
<li>뒤이어 최대 <strong>16383바이트</strong> 크기의 <strong>페이로드</strong>가 옴</li>
</ul>
<blockquote>
</blockquote>
<p><strong>프레임 헤더의 각 필드</strong></p>
<ul>
<li>R<ul>
<li>예약된 2비트 필드. 값의 의미 정의 안됨. 반드시 0. 받는 쪽에서는 이 값 무시해야 함</li>
</ul>
</li>
<li>길이<ul>
<li>페이로드의 길이를 나타내는 14비트 무부호 정수. 프레임 헤더에 포함되지 않는 길이</li>
</ul>
</li>
<li>종류<ul>
<li>프레임의 종류</li>
</ul>
</li>
<li>플래그<ul>
<li>8비트 플래그. 값의 의미는 플래그 종류에 따름</li>
</ul>
</li>
<li>R<ul>
<li>예약된 1비트 비트. 첫 번째 R과 마찬가지로 의미 정의 안됨.</li>
</ul>
</li>
<li>스트림 식별자<ul>
<li>31비트 스트림 식별자. 특별히 0은 커넥션 전체와 연관된 프레임을 의미</li>
</ul>
</li>
</ul>
<h3 id="1032-스트림과-멀티플렉싱">10.3.2 스트림과 멀티플렉싱</h3>
<ul>
<li>스트림은 HTTP/2.0 커넥션을 통해 클라이언트와 서버 사이에서 교환되는 프레임들의 독립된 양방향 시퀀스</li>
<li>한 쌍의 HTTP 요청과 응답은 하나의 스트림을 통해 이루어짐</li>
<li>HTTP/2.0에서는 하나의 커넥션에 여러 개의 스트림이 동시에 열릴 수 있음</li>
<li>스트림은 우선순위도 가질 수 있음</li>
<li>HTTP/2.0 커넥션에서 한번 사용한 스트림 식별자는 다시 사용할 수 없음<ul>
<li>이때 스트림에 할당할 수 있는 식별자가 고갈될 수 있음</li>
<li>커넥션을 다시 맺으면 해결됨</li>
</ul>
</li>
</ul>
<h3 id="1033-헤더-압축">10.3.3 헤더 압축</h3>
<ul>
<li>HTTP/1.1에서 헤더는 아무런 압축 없이 전송</li>
<li>웹페이지 하나를 보기 위한 요청의 개수가 많아져 헤더의 크기가 문제가 됨</li>
<li>이를 개선하기 위해 HTTP/2.0에서는 HTTP 메시지의 헤더를 압축하여 전송</li>
<li>HPACK 명세에 정의된 헤더 압축 방법으로 압축된 뒤 <strong>헤더 블록 조각</strong>들로 쪼개 전송</li>
<li>받는 쪽에서는 이 조각들을 이은 뒤 압축을 풀어 원래의 헤더 집합으로 복원</li>
<li>압축, 해제 시 <strong>압축 콘텍스트</strong>를 사용</li>
</ul>
<h3 id="1034-서버-푸쉬">10.3.4 서버 푸쉬</h3>
<ul>
<li>HTTP/2.0은 서버가 하나의 요청에 대해 응답으로 여러 개의 리소스를 보낼 수 있도록 함</li>
<li>어떤 리소스를 요구할 것인지 미리 알 수 있는 상황에서 유용</li>
<li>리소스를 푸쉬하려는 서버는 먼저 클라이언트에게 자원을 푸쉬할 것임을 <code>PUSH_PROMISE</code> 프레임으로 미리 알림</li>
<li>해당 프레임 스트림은 예약됨(원격) 상태가 됨</li>
<li>이 상태에서 클라이언트는 <code>RST_STREAM</code> 프레임을 보내어 푸쉬 거절 가능</li>
</ul>
<hr>
<h2 id="104-알려진-보안-이슈">10.4 알려진 보안 이슈</h2>
<h3 id="1041-중재자-캡슐화-공격">10.4.1 중재자 캡슐화 공격</h3>
<ul>
<li>HTTP/2.0 메시지를 중간의 프락시가 HTTP/1.1 메시지로 변환할 때 메시지의 의미가 변질될 가능성 있음</li>
<li>HTTP/1.1과는 달리 HTTP/2.0은 헤더 필드의 이름과 값을 바이너리로 인코딩</li>
<li>다행히 HTTP/1.1 메시지를 번역하는 과정에서 번역 문제가 발생하지는 않음</li>
</ul>
<h3 id="1042-긴-커넥션-유지로-인한-개인정보-누출-우려">10.4.2 긴 커넥션 유지로 인한 개인정보 누출 우려</h3>
<ul>
<li>HTTP/2.0은 사용자가 요청을 보낼 때의 회전 지연을 줄이기 위해 클라이언트와 서버 사이의 커넥션을 오래 유지하는 것을 염두</li>
<li>개인 정보 유출에 악용될 가능성 있음</li>
<li>이는 HTTP가 현재 갖고 있는 문제이기도 하지만, 짧게 유지되는 커넥션에서는 위험이 적음</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[9장] 웹 로봇]]></title>
            <link>https://velog.io/@dayeon-choi/9%EC%9E%A5-%EC%9B%B9-%EB%A1%9C%EB%B4%87</link>
            <guid>https://velog.io/@dayeon-choi/9%EC%9E%A5-%EC%9B%B9-%EB%A1%9C%EB%B4%87</guid>
            <pubDate>Sun, 12 Sep 2021 14:39:03 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
</blockquote>
<p><strong>웹 로봇이란</strong>
사람과의 상호작용 없이 연속된 웹 트랜젝션들을 자동으로 수행하는 소프트웨어 프로그램이다.</p>
<hr>
<h2 id="91-크롤러와-크롤링">9.1 크롤러와 크롤링</h2>
<ul>
<li>웹 크롤러 (스파이더)<ul>
<li>크롤링을 반복하는 방식으로 웹을 순회하는 로봇</li>
<li>웹 페이지 한 개를 가져옴 → 그 페이지가 가리키는 모든 웹페이지를 가져옴 → 다시 그 페이지들이 가리키는 모든 웹 페이지를 가져옴 </li>
</ul>
</li>
</ul>
<h3 id="911-어디에서-시작하는가-루트-집합">9.1.1 어디에서 시작하는가: &#39;루트 집합&#39;</h3>
<ul>
<li>루트 집합(root set)<ul>
<li>크롤러가 방문을 시작하는 URL들의 초기 집합</li>
<li>일반적으로 좋은 루트 집합은 크고 인기 있는 웹 사이트, 새로 생성된 페이지들의 목록, 자주 링크되지 않은 잘 알려져있지 않은 페이지들의 목록으로 구성</li>
</ul>
</li>
</ul>
<h3 id="912-링크-추출과-상대-링크-정상화">9.1.2 링크 추출과 상대 링크 정상화</h3>
<ul>
<li>크롤러는 웹을 돌아다니면서 꾸준히 HTML 문서를 검색</li>
<li>검색한 각 페이지 안에 들어있는 URL 링크들을 파싱하여 크롤링할 페이지들의 목록에 추가 (새 링크 발견에 따라 급속한 목록 확장)</li>
<li>해결책 → 간단한 HTML 파싱으로 링크를 추출하여 절대 링크로 변환</li>
</ul>
<h3 id="913-순환-피하기">9.1.3 순환 피하기</h3>
<ul>
<li>로봇들은 순환을 피하기 위해 반드시 그들이 어디를 방문했는지 알아야 함</li>
</ul>
<h3 id="914-루프와-중복">9.1.4 루프와 중복</h3>
<ul>
<li>순환이 크롤러에게 해로운 3가지 이유</li>
</ul>
<ol>
<li>크롤러를 루프에 빠뜨려 꼼짝 못하게 함</li>
<li>웹 서버에 부담이 됨</li>
<li>크롤러의 애플리케이션에 쓸모없는 중복된 콘텐츠가 넘침</li>
</ol>
<h3 id="915-빵-부스러기의-흔적">9.1.5 빵 부스러기의 흔적</h3>
<ul>
<li>URL들은 굉장히 많기 때문에, 어떤 URL을 방문했는지 빠르게 판단하기 위해서는 <strong>속도와 메모리 사용 면에서 효과적인 자료 구조</strong>를 사용할 필요가 있음</li>
<li>웹 크롤러가 방문한 곳 관리를 위한 유용한 기법<ul>
<li>트리와 해시 테이블
: URL을 빨리 찾아볼 수 있게 해주는 소프트웨어 자료 구조</li>
<li>느슨한 존재 비트맵
: 공간 사용 최소화를 위한, 존재 비트 여부에 따른 크롤링 유무 판단</li>
<li>체크포인트
: 로봇 프로그램의 갑작스런 중단에 대비한 URL 방문 목록 저장을 확인</li>
<li>파티셔닝
: 농장을 이용한 커뮤니케이션이 가능한 개별 로봇들의 사용</li>
</ul>
</li>
</ul>
<h3 id="917-url-정규화">9.1.7 URL 정규화</h3>
<ul>
<li>URL 형식 변환 방식<ol>
<li>포트 번호가 명시되지 않았을 경우, 호스트 명에 :80 추가</li>
<li>모든 %xx 이스케이핑된 문자들을 대응되는 문자로 변환</li>
<li><code>#</code> 태그 제거</li>
</ol>
</li>
</ul>
<h3 id="918-파일-시스템-링크-순환">9.1.8 파일 시스템 링크 순환</h3>
<ul>
<li>파일 시스템의 심벌릭 링크는 의미 없는 디렉터리 층을 반복해서 만들어낼 수 있음</li>
<li>루프 발견이 중요</li>
</ul>
<h3 id="919-동적-가상-웹-공간">9.1.9 동적 가상 웹 공간</h3>
<ul>
<li>악의적인 웹 마스터들의 복잡한 크롤러 루프 생성은 있을 수 있는 일</li>
<li>URL과 HTML은 매번 전혀 달라보여 로봇이 감지하기 매우 어려움</li>
</ul>
<h3 id="9110-루프와-중복-피하기">9.1.10 루프와 중복 피하기</h3>
<ul>
<li>잘 설계된 로봇은 순환을 피하기 위해 휴리스틱의 집합을 필요로 함</li>
<li>약간의 손실을 유발할 가능성 있음</li>
<li>의심스러워 보이지만 유효한 콘텐츠를 걸러버릴 수 있는 일 발생</li>
<li>더 올바르게 동작하기 위해 사용되는 기법<ul>
<li>URL 정규화
: URL을 표준 형태로 변환</li>
<li>너비 우선 크롤링
: 깊이 우선이 아닌 너비 우선 크롤링으로 여전히 다른 웹 사이트에서 수십개의 페이지들을 받아올 수 있는 것</li>
<li>스로틀링
: 일정 시간동안 가져올 수 있는 페이지 숫자를 제한</li>
<li>URL 크기 제한
: 일정 길이를 넘는 URL의 크롤링을 거부</li>
<li>URL/사이트 블랙리스트
: 함정인 것으로 알려진 사이트와 URL 목록을 만들어 거부하고 피함</li>
<li>패턴 발견
: 둘, 셋 이상의 반복된 구성용소를 갖고 있는 URL 크롤링을 거절</li>
<li>콘텐츠 지문
: 페이지의 콘텐츠에서 몇 바이트를 얻어내어 체크섬을 계산, 이전에 확인한 체크섬을 가진 페이지라면 크롤링 거부</li>
<li>사람의 모니터링
: 사람이 쉽게 로봇의 진행 상황을 모니터링하여 즉각 인지할 수 있게끔 진단과 로깅을 포함하도록 설계하여 확인</li>
</ul>
</li>
</ul>
<hr>
<h2 id="92-로봇의-http">9.2 로봇의 HTTP</h2>
<ul>
<li>로봇 역시 HTTP 명세의 규칙을 지켜야 함</li>
<li>적절한 HTTP 요청 헤더를 사용해야 함</li>
</ul>
<h3 id="921-요청-헤더-식별하기">9.2.1 요청 헤더 식별하기</h3>
<ul>
<li>로봇의 능력, 신원, 출신을 알려주는 기본적인 몇 헤더들을 사이트에게 보내주는 것이 좋음</li>
<li>권장되는 기본 신원 식별 헤더<ul>
<li>User-Agent
: 서버에게 요청을 만든 로봇의 이름</li>
<li>From
: 로봇의 사용자/관리자의 이메일 주소 제공</li>
<li>Accept
: 서버에게 어떤 미디어 타입을 보내도 되는지 말해줌</li>
<li>Referer
: 현재 요청 URL을 포함한 문서의 URL을 제공</li>
</ul>
</li>
</ul>
<h3 id="922-가상-호스팅">9.2.2 가상 호스팅</h3>
<ul>
<li>Host 헤더를 지원할 필요가 있음</li>
<li>로봇들이 올바른 콘텐츠를 찾게 만들기 위함</li>
</ul>
<h3 id="923-조건부-요청">9.2.3 조건부 요청</h3>
<ul>
<li>로봇이 검색하는 콘텐츠의 양을 최소화하는 것은 의미 있는 일</li>
<li>ex) 인터넷 검색엔진 로봇<ul>
<li>오직 변경되었을 때만 콘텐츠 가져오기</li>
<li>엔터티 태그를 비교함으로, 그들이 받아간 마지막 버전 이후, 업테이트 된 것이 있는지 알아보는 조건부 HTTP 요청 구현</li>
</ul>
</li>
</ul>
<h3 id="924-응답-다루기">9.2.4 응답 다루기</h3>
<ul>
<li>웹 탐색, 서버와의 상호작용을 더 잘해보려는 로봇들은 여러 종류의 HTTP 응답을 다룰 줄 알 필요가 있음<ul>
<li>상태 코드
: 200, 404와 같은 HTTP 상태 코드에 해단 이해</li>
<li>엔터티
: HTTP 헤더에 임베딩된 정보를 따라 찾을 수 있으며, 유용한 정보들이 들어 있음</li>
</ul>
</li>
</ul>
<h3 id="925-user-agent-타기팅">9.2.5 User-Agent 타기팅</h3>
<ul>
<li>그들의 여러 기능을 지원할 수 있도록 브라우저의 종류를 감지하여 그에 맞게 콘텐츠를 최적화 함</li>
</ul>
<hr>
<h2 id="93-부적절하게-동작하는-로봇들">9.3 부적절하게 동작하는 로봇들</h2>
<ul>
<li>로봇들의 실수, 결과 몇가지<ul>
<li>폭주하는 로봇
: 극심한 부하를 안겨줌 → 폭주 보호 장치에 신경 써 설계하는 것으로 해결</li>
<li>오래된 URL
: 웹 사이트가 콘텐츠를 많이 바꿨을 경우 불필요한 요청을 많이 보냄, 웹 서버의 요청에 대한 수용 능력이 감소됨</li>
<li>길고 잘못된 URL
: 웹 서버의 처리 능력에 영향을 주고, 고장을 일으킬 위험도 있음</li>
<li>호기심이 지나친 로봇
: 최악의 경우엔 사생활 침해, 민감한 데이터 포함 경우 → 해당 콘텐츠를 무시하는 메커니즘</li>
<li>동적 게이트웨이 접근
: 처리 비용이 많이 듦</li>
</ul>
</li>
</ul>
<hr>
<h2 id="94-로봇-차단하기">9.4 로봇 차단하기</h2>
<ul>
<li>robots.txt라는 선택적 파일 제공</li>
<li>해당 파일은 어떤 로봇이 서버에 어떤 부분에 접근할 수 있는지에 대한 정보</li>
<li>이 자발적인 표준에 따른다면, 리소스 접근 전 그 사이트의 robots.txt를 요청</li>
<li>이 과정을 통해 로봇을 차단</li>
</ul>
<h3 id="941-로봇-찿단-표준">9.4.1 로봇 찿단 표준</h3>
<ul>
<li>임시방편으로 마련된 표준</li>
<li>불완전하지만 없는 것보다는 훨씬 낫고, 주류 업체, 검색엔진 크롤러들은 이 차단 표준 지원</li>
</ul>
<h3 id="942-웹-사이트와-robotstxt-파일들">9.4.2 웹 사이트와 robots.txt 파일들</h3>
<ul>
<li>robots.txt 가져오기<ul>
<li>HTTP GET 메서드를 이용해 robots.txt 리소스 가져옴</li>
<li>로봇은 사이트 관리자가 접근을 추적할 수 있도록 From이나 User-Agent 헤더를 통해 신원 넘김</li>
</ul>
</li>
<li>응답 코드<ul>
<li>로봇은 robots.txt를 많은 웹사이트가 가지고 있지 않다는 것을 모름</li>
<li>로봇은 robots.txt 검색 결과에 따라 다르게 동작함<ul>
<li>성공 응답 시 차단 규칙을 얻어 그 규칙을 따름</li>
<li>리소스가 존재하지 않다는 응답을 얻을 시 제약 없이 사이트에 접근</li>
<li>접근 제한으로 응답 얻을 시 접근은 완전히 제한되어 있다고 가정</li>
<li>요청 시도의 일시적인 실패일 경우 검색을 뒤로 미룸</li>
<li>리다이렉션을 의미하는 응답일 경우 리소스가 발견될 때까지 리다이렉션 따라감</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="943-robotstxt-파일-포맷">9.4.3 robots.txt 파일 포맷</h3>
<ul>
<li>robots.txt 파일은 매우 단순한 줄 기반 문법을 가짐</li>
<li>줄들은 레코드로 구분</li>
<li>줄 종류<ul>
<li>User-Agent 줄
: <code>User-Agent: &lt;robot-name&gt;</code>, 줄을 찾지 못했다면 접근 제한 없음</li>
<li>Disallow와 Allow 줄들
: User-Agent 줄들 바로 다음에 옴, 어떤 URL 경로가 명시적으로 허용, 금지 되어있는지 기술</li>
</ul>
</li>
</ul>
<h3 id="945-robotstxt의-캐싱과-만료">9.4.5 robots.txt의 캐싱과 만료</h3>
<ul>
<li>매 파일 접근마다 robots.txt 파일을 새로 가져오면 효율성 떨어짐, 웹 서버 부하도 두 배</li>
<li>→ robots.txt의 캐시된 사본은 robots.txt가 만료될 때까지 로봇에 의해 사용</li>
</ul>
<h3 id="946-로봇-차단-펄-코드">9.4.6 로봇 차단 펄 코드</h3>
<ul>
<li>robots.txt 파일과 상호작용하는 공개된 펄 라이브러리</li>
<li>RobotRules 객체 만들기
: <code>$rules = WWW::RobotRules-&gt;new($robot_name);</code></li>
<li>robots.txt 파일 로드하기
: <code>$rules-&gt;parse($url,$content,$fresh_until);</code></li>
<li>사이트 URL을 가져올 수 있는지 검사
: <code>$can_fetch = $rules-&gt;allowed($url);</code></li>
</ul>
<h3 id="947-html-로봇-제어-meta-태그">9.4.7 HTML 로봇 제어 META 태그</h3>
<ul>
<li>robots.txt 파일을 사이트 관리자가 로봇들을 웹 사이트 일부, 전체에 접근할 수 없게 함</li>
<li>로봇 차단 태그 형식<ul>
<li>로봇 META 지시자<ul>
<li>NOINDEX
: 해당 페이지를 처리하지 말고 무시하라</li>
<li>NOFOLLOW
: 해당 페이지가 링크한 페이지를 크롤링하지 마라</li>
<li>INDEX
: 해당 페이지의 콘텐츠를 인덱싱해도 됨</li>
<li>NOARCHIVE
: 해당 페이지를 위한 로컬 사본 생성은 안됨</li>
<li>ALL
: = INDEX, FOLLOW</li>
<li>NONE
: = NOINDEX, NOFOLLOW</li>
</ul>
</li>
<li>검색엔진 META 태그</li>
</ul>
</li>
</ul>
<hr>
<h2 id="95-로봇-에티켓">9.5 로봇 에티켓</h2>
<ul>
<li>가이드 라인</li>
<li>출처 : <a href="https://www.robotstxt.org/guidelines.html">https://www.robotstxt.org/guidelines.html</a></li>
</ul>
<hr>
<h2 id="96-검색엔진">9.6 검색엔진</h2>
<ul>
<li>웹 로봇을 가장 광범위하게 사용하는 것은 검색엔진</li>
<li>먹이 주듯 문서들을 가져다 주어, 검색엔진이 어떤 문서에 어떤 단어들이 존재하는지에 대한 색인을 생성할 수 있게 함</li>
</ul>
<h3 id="961-넓게-생각하라">9.6.1 넓게 생각하라</h3>
<ul>
<li>대규모 크롤러가 자신의 작업을 완료하려면 많은 장비들을 똑똑하게 사용하여 요청을 병렬로 수행할 수 있어야 할 것</li>
<li>그러나, 그 규모로 인해 웹 전체 크롤링은 쉽지 않은 도전</li>
</ul>
<h3 id="962-현대적인-검색엔진의-아키텍처">9.6.2 현대적인 검색엔진의 아키텍처</h3>
<ul>
<li>오늘날 검색엔진, 전 세계 웹페이지들에 대한 <strong>풀 텍스트 색인</strong>이라고 하는 복잡한 로컬 데이터베이스 생성</li>
<li>웹의 모든 웹 문서에 대한 일종의 카드 카탈로그처럼 동작</li>
<li>과정
: 크롤러들이 웹 페이지들을 수집하여 집으로 가져옴 → 풀 텍스트 색인에 추가 + 웹 검색 게이트웨이를 통해 질의 보냄</li>
</ul>
<h3 id="963-풀-텍스트-색인">9.6.3 풀 텍스트 색인</h3>
<ul>
<li>단어 하나를 입력받아 그 단어를 포함하고 있는 문서를 즉각 알려줄 수 있는 데이터베이스</li>
<li>해당 문서들은 색인 생성 후, 검색할 필요가 없음</li>
<li>풀 텍스트 색인은 각 단어를 포함한 문서를 열거<ul>
<li>단어 &#39;a&#39;는 문서 A와 B에 들어 있음</li>
<li>단어 &#39;best&#39;는 문서 A와 C에 들어 있음</li>
<li>단어 the는 세 문서 A,B,C에 들어 있음</li>
</ul>
</li>
</ul>
<h3 id="964-질의-모내기">9.6.4 질의 모내기</h3>
<ul>
<li>사용자가 질의를 웹 검색엔진 게이트웨이로 보내는 방법, 이후<ul>
<li>HTML 폼을 사용자가 채워 넣고 브라우저가 그 폼을 HTTP GET, POST 요청을 통해 게이트웨이에 보내는 식</li>
<li>게이트웨이 프로그램은 검색 질의를 추출하고 웹 UI 풀 텍스트 색인을 검색할 때 사용되는 표현식으로 변환</li>
<li>게이트웨이는 웹 서버에게 문서의 목록을 결과로 알려줌</li>
<li>웹 서버는 결과를 사용자를 위한 HTML 페이지로 변환</li>
</ul>
</li>
</ul>
<h3 id="965-검색-결과를-정렬하고-보여주기">9.6.5 검색 결과를 정렬하고 보여주기</h3>
<ul>
<li>검색엔진이 색인을 한번 사용했다면, 게이트웨이 애플리케이션은 그 결과를 이용해 결과 페이지를 즉석으로 생성 가능</li>
<li>검색엔진은 결과에 순위를 매기기 위한 똑똑한 알고리즘 사용</li>
<li>주어진 단어와 관련이 많은 순서대로 결과 문서에 나타낼 수 있도록 <strong>관련도 랭킹</strong></li>
</ul>
<h3 id="966-스푸핑">9.6.6 스푸핑</h3>
<ul>
<li>웹 사이트를 찾을 때 검색 결과의 순서는 중요</li>
<li>속임수를 잡아내기 위한 끊임없는 관련도 알고리즘 수정이 필요</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[5장] 웹 서버]]></title>
            <link>https://velog.io/@dayeon-choi/5%EC%9E%A5-%EC%9B%B9-%EC%84%9C%EB%B2%84</link>
            <guid>https://velog.io/@dayeon-choi/5%EC%9E%A5-%EC%9B%B9-%EC%84%9C%EB%B2%84</guid>
            <pubDate>Sun, 22 Aug 2021 14:17:40 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="51-다채로운-웹-서버">5.1 다채로운 웹 서버</h2>
<ul>
<li>웹 서버 : 웹 서버 소프트웨어와 웹페이지 제공에 특화된 장비 양쪽을 모두 가리킴</li>
</ul>
<br/>

<h3 id="511-웹-서버-구현">5.1.1 웹 서버 구현</h3>
<ul>
<li>웹 서버는 HTTP 및 그와 관련된 TCP 처리를 구현한 것</li>
<li>자신이 제공하는 리소스를 관리하고 웹 서버를 설정, 통제, 확장하기 위한 관리 기능 제공</li>
</ul>
<blockquote>
</blockquote>
<p><strong>웹 서버의 형태</strong></p>
<ul>
<li>다목적 소프트웨어 웹 서버를 표준 컴퓨터 시스템에 설치하고 실행할 수 있음</li>
<li>마이크로프로세서의 기적으로, 어떤 회사들은 사용자에게 판매할 전자기기 안에 몇 개의 컴퓨터 칩만으로 구현된 웹 서버를 내장시켜서 완전한 관리 콘솔로 제공</li>
</ul>
<br/>

<h3 id="512-다목적-소프트웨어-웹-서버">5.1.2 다목적 소프트웨어 웹 서버</h3>
<ul>
<li>다목적 소프트웨어 웹 서버는 네트워크에 연결된 표준 컴퓨터 시스템에서 동작</li>
<li>EX) 오픈 소스 소프트웨어(아파치, W3C의 직소), 상용 소프트웨어(마이크로 소프트, 아이플래닛 웹 서버)</li>
</ul>
<br/>

<h3 id="513-임베디드-웹-서버">5.1.3 임베디드 웹 서버</h3>
<ul>
<li>일반 소비자용 제품에 내장될 목적으로 만들어진 작은 웹 서버</li>
<li>그들의 일반 소비자용 기기를 간편한 웹 브라우저 인터페이스로 관리할 수 있게 해줌</li>
</ul>
<br/>

<hr>
<h2 id="52-간단한-펄-웹-서버">5.2 간단한 펄 웹 서버</h2>
<ul>
<li>(생략) </li>
</ul>
<br/>

<hr>
<h1 id="53-진짜-웹-서버가-하는-일">5.3 진짜 웹 서버가 하는 일</h1>
<blockquote>
</blockquote>
<p><strong>하는 일</strong></p>
<ul>
<li>커넥션을 맺음</li>
<li>요청을 받음</li>
<li>요청을 처리</li>
<li>리소스에 접근</li>
<li>응답 생성</li>
<li>응답 보냄</li>
<li>트랜잭션을 로그로 남김</li>
</ul>
<img src="https://images.velog.io/images/dayeon-choi/post/6555c830-37c8-4781-8d56-9b58ca479d60/image.png" width="80%" />

<br/>

<hr>
<h2 id="54-단계-1-클라이언트-커넥션-수락">5.4 단계 1: 클라이언트 커넥션 수락</h2>
<ul>
<li>클라이언트가 이미 서버에 열려있는 지속적 커넥션을 가지고 있다면 해당 커넥션 사용 가능</li>
<li>그렇지 않다면 새 커넥션 필요</li>
</ul>
<br/>

<h3 id="541-새-커넥션-다루기">5.4.1 새 커넥션 다루기</h3>
<ul>
<li>웹 서버는 새 커넥션을 맺고 TCP 커넥션에서 IP 주소를 추출하여 커넥션 맞은편에 어떤 클라이언트가 있는지 확인</li>
<li>웹 서버는 어떤 커넥션이든 마음대로 거절하거나 즉시 닫기 가능</li>
<li>신원 식별 기법 사용 가능 (IP 주소, 호스트 명 인가 확인)</li>
</ul>
<br/>

<h3 id="542-클라이언트-호스트-명-식별">5.4.2 클라이언트 호스트 명 식별</h3>
<ul>
<li>대부분의 웹 서버들은 DNS (역방향)을 사용해 클라이언트의 IP 주소를 클라이언트의 호스트 명으로 변환하도록 설정</li>
<li>클라이언트 호스트 명을 구체적인 접근 제어와 로깅을 위해 사용 가능</li>
</ul>
<br/>

<h3 id="543-ident를-통해-클라이언트-사용자-알아내기">5.4.3 ident를 통해 클라이언트 사용자 알아내기</h3>
<ul>
<li>몇몇 웹 서버는 IETF ident 프로토콜 지원</li>
<li>ident 프로토콜 :
서버에게 어떤 사용자 이름이 HTTP 커넥션을 초기화했는지 찾아낼 수 있게 해줌</li>
<li>ident는 조직 내부에서는 잘 사용할 수 있지만 공공 인터넷에서는 여러 이유로 잘 동작하지 않음</li>
</ul>
<blockquote>
</blockquote>
<p><strong>ident가 공공 인터넷에서 동작하지 않는 이유</strong></p>
<ul>
<li>많은 클라이언트 PC는 identd 신원확인 프로토콜 데몬 소프트웨어를 실행하지 않음</li>
<li>HTTP 트랜잭션을 유의미하게 지연시킴</li>
<li>방화벽이 이 트래픽을 들어오는 것을 막는 경우가 많음</li>
<li>안전하지 않고 조작하기 쉬움</li>
<li>가상 IP 주소를 잘 지원하지 않음</li>
<li>클라이언트 사용자 이름의 노출로 인한 프라이버시 침해의 우려가 있음</li>
</ul>
<br/>

<hr>
<h2 id="55-단계-2-요청-메시지-수신">5.5 단계 2: 요청 메시지 수신</h2>
<ul>
<li>커넥션에 데이터 도착 시, 웹 서버는 네트워크 커넥션에서 그 데이터를 읽어들이고 파싱하여 요청 메시지 구성<img src="https://images.velog.io/images/dayeon-choi/post/87e2a217-6ad2-4ee7-8bc4-2f28f641af42/image.png" width="80%" />
<span style="color:gray">출처 : https://velog.io/@wltjs10645/HTTP-%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C-5%EC%9E%A5</span>

</li>
</ul>
<blockquote>
</blockquote>
<p><strong>요청 메시지 파싱 시, 웹 서버가 하는 일</strong></p>
<ul>
<li>요청줄을 파싱하여 요청 메서드, 지정된 리소스의 식별자(URI), 버전 번호를 찾음</li>
<li>메시지 헤더들을 읽음 (각 메시지 헤더는 CRLF로 끝남)</li>
<li>헤더의 끝을 의미하는 CRLF로 끝나는 빈 줄을 찾아냄</li>
<li>요청 본문이 있다면 읽어들임 (길이는 Content-Length 헤더로 정의)</li>
</ul>
<br/>

<h3 id="551-메시지의-내부-표현">5.5.1 메시지의 내부 표현</h3>
<ul>
<li>몇몇 웹 서버는 요청 메시지를 쉽게 다룰 수 있도록 내부의 자료 구조에 저장</li>
</ul>
<br/>

<h3 id="552-커넥션-입력출력-처리-아키텍처">5.5.2 커넥션 입력/출력 처리 아키텍처</h3>
<ul>
<li>고성능 웹 서버는 수천 개의 커넥션을 동시에 열 수 있도록 지원</li>
<li>웹 서버는 항상 새 요청을 주시</li>
<li>웹 서버 아키텍처의 차이에 따라 요청을 처리하는 방식도 달라짐</li>
</ul>
<blockquote>
</blockquote>
<p><strong>요청 처리 방식</strong></p>
<ul>
<li>단일 스레드 웹 서버 그림<ul>
<li>한번에 하나씩 요청 처리</li>
<li>트랜잭션 완료 시, 다음 커넥션이 처리</li>
</ul>
</li>
<li>멀티프로세스와 멀티스레드 웹 서버<ul>
<li>여러 요청을 동시에 처리하기 위해 여러개의 프로세스 혹은 고효율 스레드를 할당</li>
</ul>
</li>
<li>다중 I/O 서버<ul>
<li>모든 커넥션은 동시에 해당 활동을 감시</li>
<li>커넥션의 상태가 바뀌면 그 커넥션에 대한 작은 양의 처리 수행</li>
</ul>
</li>
<li>다중 멀치스레드 웹 서버<ul>
<li>여러 개의 스레드는 각각 열려있는 커넥션을 감시하고 각 커넥션에 대해 조금씩 작업을 수행</li>
</ul>
</li>
</ul>
<br/>

<hr>
<h2 id="56-단계-3-요청-처리">5.6 단계 3: 요청 처리</h2>
<ul>
<li>웹 서버가 요청을 받으면 서버는 요청으로부터 메서드,리소스,헤더,본문을 얻어내어 처리</li>
</ul>
<br/>

<hr>
<h2 id="57-단계-4-리소스의-맵핑과-접근">5.7 단계 4: 리소스의 맵핑과 접근</h2>
<ul>
<li>웹 서버는 리소스 서버</li>
<li>웹 서버가 클라이언트에 콘텐츠를 전달하려면, 그 전에 요청 메시지의 URI에 대응하는 알맞은 콘텐츠나 콘텐츠 생성기를 웹 서버에서 찾아 그 콘텐츠의 원천을 식별해야 함</li>
</ul>
<br/>

<h3 id="571-docroot">5.7.1 Docroot</h3>
<ul>
<li>리소스 매핑의 가장 단순한 형태는 요청 URI를 웹 서버의 파일 시스템 안에 있는 파일 이름으로 사용하는 것</li>
<li>docroot : 
웹 서버 파일 시스템의 특별한 폴더를 웹 콘텐츠를 위해 예약해 둔 것</li>
<li>웹 서버는 요청 메시지에서 URI를 가져와서 문서 루트 뒤에 붙임</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>가상 호스팅된 docroot<ul>
<li>각 사이트에서 그들만의 분리된 문서 루트를 주는 방법으로 한 웹 서버에서 여러 개의 웹 사이트를 호스팅 함</li>
<li>하나의 웹 서버 위에서 두 개의 사이트가 완전히 분리된 콘텐츠를 호스팅 되도록 할 수 있음</li>
</ul>
</li>
<li>사용자 홈 디렉터리 docroots</li>
<li>사용자들이 한 대의 웹 서버에서 각자의 개인 웹 사이트를 만들도록 해주는 것</li>
<li><code>/~사용자이름</code>으로 시작하는 URI는 그 사용자의 개인 문서 루트를 가리킴</li>
</ul>
<br/>

<h3 id="572-디렉터리-목록">5.7.2 디렉터리 목록</h3>
<ul>
<li>웹 서버는 경로가 파일이 아닌 디렉터리를 가리키는, 디렉터리 URL에 대한 요청을 받을 수 있음</li>
</ul>
<blockquote>
</blockquote>
<p><strong>디렉터리 URL 요청 시 설정할 수 있는 행동</strong></p>
<ul>
<li>에러 반환</li>
<li>디렉터리 대신 특별한 색인 파일 반환</li>
<li>디렉터리 탐색 후 그 내용을 담은 HTML 페이지 반환</li>
</ul>
<br/>

<h3 id="573-동적-콘텐츠-리소스-매핑">5.7.3 동적 콘텐츠 리소스 매핑</h3>
<ul>
<li>웹 서버는 URI를 동적 리소스에 매핑할 수도 있음
= 요청에 맞게 콘텐츠를 생성하는 프로그램에 URI 매핑</li>
<li>동적 리소스 사용 시, 애플리케이션 서버는 그에 대한 동적 콘텐츠 생성 프로그램이 어디에/어떻게 실행하는지 알려줄 수 있어야 함</li>
<li>→ 대부분의 웹 서버는 동적 리소스를 식별하고 매핑할 수 있는 매커니즘 보유</li>
</ul>
<br/>

<h3 id="574-서버사이드-인클루드server-side-includes-ssi">5.7.4 서버사이드 인클루드(Server-Side Includes, SSI)</h3>
<ul>
<li>어떤 리소스가 서버사이드 인클루드를 포함하고 있는 것으로 설정되면, 서버는 그 리소스의 콘텐츠를 클라이언트에 보내기 전에 처리</li>
<li>서버는 콘텐츠에 변수 이름, 내장된 스크립트가 될 수 있는 특별한 패턴이 있는지 검사 → 변수 값/스크립트 출력 값으로 치환</li>
</ul>
<br/>

<h3 id="575-접근-제어">5.7.5 접근 제어</h3>
<ul>
<li>웹 서버는 각각의 리소스에 접근 제어를 할당할 수 있음</li>
<li>리소스에 접근하기 위한 비밀번호를 물어볼 수도 있음</li>
</ul>
<br/>

<hr>
<h2 id="58-단계-5-응답-만들기">5.8 단계 5: 응답 만들기</h2>
<ul>
<li>서버가 리소스를 식별하면, 서버는 요청 메서드로 서술되는 동작을 수행한 뒤 응답 메시지를 반환</li>
<li>응답 메시지는 응답 상태 코드, 응답 헤더, 응답 본문을 포함</li>
</ul>
<br/>

<h3 id="581-응답-엔터티">5.8.1 응답 엔터티</h3>
<blockquote>
</blockquote>
<p><strong>트랜잭션이 응답 본문 생성 시, 응답 메시지가 포함하는 내용</strong></p>
<ul>
<li>응답 본문의 MIME 타입을 서술하는 Content-Type 헤더</li>
<li>응답 본문의 길이를 서술하는 Content-Length 헤더</li>
<li>실제 응답 본문의 내용</li>
</ul>
<br/>

<h3 id="582-mime-타입-결정하기">5.8.2 MIME 타입 결정하기</h3>
<blockquote>
</blockquote>
<p><strong>MIME 타입과 리소스를 연결하는 여러 가지 방법</strong></p>
<ul>
<li>mime.types<ul>
<li>MIME 타입을 나타내기 위해 파일 이름의 확장자를 사용할 수 있음.</li>
<li>각 리소스의 MIME 타입을 계산하기 위해 확장자별 MIME 타입이 담겨있는 파일 탐색</li>
</ul>
</li>
<li>매직 타이핑<ul>
<li>각 파일의 MIME 타입을 알아내기 위해 파일의 내용을 검사해 잘 알려진 패턴에 대한 테이블(매직 파일)에 해당하는 패턴이 있는지 찾음</li>
</ul>
</li>
<li>유형 명시<ul>
<li>특정 파일이나 디렉터리 안 파일들이 파일 확장자나 내용에 상관 없이 어떤 MIME 타입을 갖도록 웹 서버 설정 가능</li>
</ul>
</li>
<li>유형 협상<ul>
<li>한 리소스가 여러 종류의 문서 형식에 속하도록 설정 가능</li>
<li>특정 파일이 특정 MIME 타입을 갖게끔 설정 가능</li>
</ul>
</li>
</ul>
<br/>

<h3 id="583-리다이렉션">5.8.3 리다이렉션</h3>
<ul>
<li>웹 서버는 요청을 수행하기 위해 브라우저가 다른 곳으로 가도록 리다이렉트 할 수 있음</li>
</ul>
<blockquote>
</blockquote>
<p><strong>리다이렉트가 유용한 경우</strong></p>
<ul>
<li>영구히 리소스가 옮겨진 경우<ul>
<li>웹 서버는 클라이언트에게 리소스 이름이 바뀌었으므로, 클라이언트는 북마크를 갱신하거나 할 수 있다고 말해줄 수 있음</li>
</ul>
</li>
<li>임시로 리소스가 옮겨진 경우<ul>
<li>서버는 클라이언트가 갱신하지 않기를 바람</li>
<li>303 See Other와 307 Temporary Redirect 상태 코드 사용</li>
</ul>
</li>
<li>URL 증강<ul>
<li>클라이언트는 리다이렉트를 따라가서 상태정보가 추가된 완전한 URL을 포함한 요청을 다시 보냄</li>
</ul>
</li>
<li>부하 균형<ul>
<li>303 See Other와 307 Temporary Redirect 상태 코드 사용</li>
</ul>
</li>
<li>친밀한 다른 서버가 있을 때<ul>
<li>303 See Other와 307 Temporary Redirect 상태 코드 사용</li>
</ul>
</li>
<li>디렉터리 이름 정규화<ul>
<li>상대 경로가 정상적으로 동작할 수 있는 클라이언트를 정규화하여 URI로 리다이렉트</li>
</ul>
</li>
</ul>
<br/>

<hr>
<h2 id="59-단계-6-응답-보내기">5.9 단계 6: 응답 보내기</h2>
<ul>
<li>서버는 커넥션 상태를 추적해야 하며, 지속적인 커넥션은 특별히 주의해서 다룰 필요</li>
</ul>
<br/>

<hr>
<h2 id="510-단계-7-로깅">5.10 단계 7: 로깅</h2>
<ul>
<li>마지막으로, 트랜잭션이 완료되었을 시, 웹 서버는 트랜잭션이 어떻게 수행되었는지에 대한 로그를 로그 파일에 기록</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[4장] 커넥션 관리]]></title>
            <link>https://velog.io/@dayeon-choi/4%EC%9E%A5-%EC%BB%A4%EB%84%A5%EC%85%98-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@dayeon-choi/4%EC%9E%A5-%EC%BB%A4%EB%84%A5%EC%85%98-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Thu, 19 Aug 2021 10:56:56 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="41-tcp-커넥션">4.1 TCP 커넥션</h1>
<ul>
<li>전 세계 모든 HTTP 통신은 TCP/IP를 통해 이루어짐</li>
<li>세계 어디서든 클라이언트 애플리케이션은 서버 애플리케이션으로 TCP/IP와 커넥션을 맺을 수 있음.</li>
<li>커넥션이 맺어지면 클라이언트와 서버 컴퓨터 간에 주고받은 메시지들은 보전되며 안전하게 전달됨.</li>
</ul>
<blockquote>
</blockquote>
<p>** 커넥션 7단계 **
(1) 브라우저가 호스트 명 추출
(2) 브라우저가 호스트 명에 대한 IP 주소 찾음
(3) 브라우저가 포트 번호를 얻음
(4) 브라우저가 해당 포트로 TCP 커넥션을 생성
(5) 브라우저가 서버로 HTTP GET 요청 메시지를 보냄
(6) 브라우저가 서버에서 온 HTTP 응답 메시지를 읽음
(7) 브라우저가 커넥션을 끊음</p>
<br/>

<h3 id="411-신뢰할-수-있는-데이터-전송-통로인-tcp">4.1.1 신뢰할 수 있는 데이터 전송 통로인 TCP</h3>
<ul>
<li>TCP 커넥션의 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 정확히 전달됨<br/>

</li>
</ul>
<h3 id="412-스트림은-나뉘어-ip-패킷을-통해-전송된다">4.1.2 스트림은 나뉘어 IP 패킷을 통해 전송된다.</h3>
<ul>
<li>TCP는 IP 패킷(IP 데이터그램)이라는 작은 조각을 통해 데이터를 전송</li>
<li>HTTP는 프로토콜 스택에서 최상위 계층</li>
<li>HTTP + 보안 기능 = HTTPS = TLS = SSL = HTTP와 TCP 사이에 있는 암호화 계층</li>
<li>TCP는 세그먼트 단위로 스트림을 잘게 나눠 IP 패킷으로 감싸 보냄</li>
<li>TCP/IP 소프트웨어 의해 처리됨</li>
</ul>
<blockquote>
</blockquote>
<p><strong>IP 패킷 구성 요소</strong></p>
<ul>
<li>IP 패킷 헤더</li>
<li>TCP 세그먼트 헤더</li>
<li>TCP 데이터 조각</li>
</ul>
<br/>

<h3 id="413-tcp-커넥션-유지하기">4.1.3 TCP 커넥션 유지하기</h3>
<ul>
<li>컴퓨터는 항상 TCP를 여러 개 가지고 있음</li>
<li>TCP는 포트 번호를 통해 이런 여러 개의 커넥션을 유지</li>
<li>발신지 IP 주소, 발신지 포트, 수신지 IP 주소, 수신지 포트 값으로 <strong>유일한 커넥션</strong>을 생성<br/>

</li>
</ul>
<h3 id="414-tcp-소켓-프로그래밍">4.1.4 TCP 소켓 프로그래밍</h3>
<ul>
<li>운영체제는 TCP 커넥션의 생성과 관련된 여러 기능 제공</li>
<li>소켓 API 사용 시, TCP 종단 데이터 구조를 생성하고, 원격 서버의 TCP 종단에 그 종단 데이터 구조를 연결하여 테이터 스트림 읽기/쓰기 가능</li>
<li>TCP IP는 핸드셰이킹, 데이터 스트림과 IP 패킷 간의 분할 및 재조립에 대한 모든 세부사항을 외부로부터 숨김</li>
<li><img src="https://images.velog.io/images/dayeon-choi/post/e64f9fc2-8578-473d-81dc-8f24ed7ca6dc/image.png" width="50%" />

</li>
</ul>
<p>🔎 <strong>핸드셰이킹 :</strong> 주고받기. 두 개의 통신 채널의 변수를 동적으로 설정하는 자동화된 협상 과정</p>
<br/>

<hr>
<h1 id="42-tcp의-성능에-대한-고려">4.2 TCP의 성능에 대한 고려</h1>
<ul>
<li>HTTP는 TCP 바로 위에 있는 개층이기 때문에 HTTP 성능은 그 아래 계층, TCP 성능에 영향을 받음<br/>

</li>
</ul>
<h3 id="421-http-트랜잭션-지연">4.2.1 HTTP 트랜잭션 지연</h3>
<ul>
<li><img src="https://images.velog.io/images/dayeon-choi/post/77b62cd8-a9ea-4e97-9dd6-2fea3a31f5fe/image.png" height="80%" /></li>
<li>HTTP 지연은 대부분 TCP 네트워크 지연때문에 발생</li>
<li>HTTP 트랜잭션 지연 원인<ul>
<li>URI에 기술되어 있는 호스트에 방문한 적이 최근에 없는 경우 (호스트명 → IP 주소)</li>
<li>수백 개의 HTTP 트랜잭션이 존재할 때, 새로운 TCP 커넥션으로 인해 커넥션 설정 시간이 발생하는 경우</li>
<li>요청 메시지가 인터넷을 통해 전달되고 서버에 의해서 처리되어 시간이 소요되는 경우</li>
<li>웹 서버가 HTTP 응답을 보내는 경우</li>
</ul>
</li>
</ul>
<br/>

<h3 id="422-성능-관련-중요-요소">4.2.2 성능 관련 중요 요소</h3>
<p>(생략)
<br/></p>
<h3 id="423-tcp-커넥션-핸드셰이크-지연">4.2.3 TCP 커넥션 핸드셰이크 지연</h3>
<blockquote>
</blockquote>
<p><strong>TCP 커넥션이 핸드셰이크를 하는 순서</strong></p>
<ul>
<li><p>(1) 커넥션 생성 요청 :
클라이언트가 새로운 TCP 커넥션을 생성하기 위해 작은 TCP를 서버에 보냄. 해당 패킷은 &#39;SYN&#39;라는 특별한 플래그 가짐.</p>
</li>
<li><p>(2) 커넥션 받음 :
몇 가지 커넥션 매개변수를 산출, &#39;SYN&#39;과 &#39;ACK&#39; 플래그를 포함한 TCP 패킷을 클라이언트에 보냄</p>
</li>
<li><p>(3) 확인응답 신호 :
클라이언트는 커넥션이 잘 맺어졌음을 알리기 위해 서버에게 다시 확인응답 신호를 보냄. 오늘날 TCP는 데이터까지 함께 보낼 수 있음.</p>
</li>
<li><p>패킷들은 보이지 않게 TCP 소프트웨어가 관리 (HTTP 프로그래머들은 확인 불가능)</p>
</li>
<li><p>HTTP 트랜잭션은 TCP를 구성하는 것에 많은 시간을 할애. </p>
</li>
<li><p>→ 이러한 지연을 제거하기 위해 이미 존재하는 커넥션을 재활용</p>
</li>
</ul>
<br/>

<h3 id="424-확인응답-지연">4.2.4 확인응답 지연</h3>
<ul>
<li>인터넷 라우터는 과부화가 걸렸을 시, 패킷을 마음대로 파기할 수 있음 (패킷 전송 보장 ✕)</li>
<li>TCP는 성공적인 데이터 전송을 보장하기 위해 자체적인 확인 체계를 가짐 → &quot;확인 응답&quot;<ul>
<li>*<em>무결성 체크섬 : *</em>
각 세그먼트의 수신자는 세그먼트를 온전히 받으면 작은 확인응답 패킷을 송신자에게 반환</li>
<li>*<em>편승(piggyback) : *</em>
TCP는 송출 데이터 패킷과 확인응답을 하나로 묶음으로써 네트워크를 좀 더 효율적으로 사용</li>
</ul>
</li>
</ul>
<br/>

<h3 id="425-tcp-느린-시작slow-start">4.2.5 TCP 느린 시작(slow start)</h3>
<ul>
<li>TCP 커넥션은 시간이 지나면서 자체적으로 튜닝됨</li>
<li>처음에는 커넥션의 최대 속도를 제한하고 데이터가 성공적으로 전송됨에 따라 속도 제한 높임</li>
<li>급작스러운 부하와 혼잡을 방지</li>
<li>TCP가 한 번에 전송할 수 있는 패킷의 수 제한</li>
<li>혼잡 윈도를 연다 (확인 응답을 받으면 2개의 패킷을 보낼 수 있는 방식)</li>
</ul>
<br/>

<h3 id="426-네이글nagle-알고리즘과-tcp-_nodelay">4.2.6 네이글(Nagle) 알고리즘과 TCP _NODELAY</h3>
<ul>
<li>TCP는 데이터 스트림 인터페이스를 제공</li>
<li>세그먼트가 최대 크기가 되지 않으면 전송을 하지 않음</li>
<li>모든 패킷이 확인응답을 받았을 경우 최대 크기보다 작은 패킷의 전송을 허락</li>
<li>HTTP 성능에 관련해 여러 문제를 발생시킴<ul>
<li>크기가 작은 HTTP 메시지는 패킷을 채우지 못하기 때문에, 예측 불가능한 추가적인 데이터를 기다리며 지연됨</li>
<li>확인응답 지연과 함께 쓰일 경우 형편없이 동작 (지연에 지연을 더함)</li>
</ul>
</li>
<li>HTTP 애플리케이션은 성능 향상을 위해 HTTP 스택에 TCP _NODELAY 파라미터 값을 설정하여 네이글 알고리즘을 비활성화하기도 함 → 큰 크기의 데이터 덩어리 만들어야 함</li>
</ul>
<br/>

<h3 id="427-time_wait의-누적과-포트-고갈">4.2.7 TIME_WAIT의 누적과 포트 고갈</h3>
<ul>
<li>TIME_WAIT 포트 고갈은 성능 측정 시에 심각한 성능 저하를 발생시키지만 보통 실제 상황에서는 문제를 발생시키지 않음</li>
<li>TCP 커넥션의 종단에서 TCP를 끊으면, 종단에서는 커넥션의 IP 주소와 포트 번호를 메모리의 작은 제어영역에 기록 (일정 시간동안 TCP 커넥션 생성 방지 목적)</li>
<li>현대의 빠른 라우터들 적용 이후, 커넥션 닫힌 후 패킷이 중복되는 경우 거의 사라짐</li>
<li>주의할 점<ul>
<li>이전 커넥션의 패킷이 그 해당 커넥션과 같은 연결 값으로 생성되면 패킷이 중복되며 TCP 데이터 충돌</li>
<li>포트 고갈 문제를 겪지 않더라도, 많은 커넥션 맺기나 많은 대기 상태의 제어 블록이 있는 상황은 주의</li>
</ul>
</li>
</ul>
<br/>

<hr>
<h2 id="43-http-커넥션-관리">4.3 HTTP 커넥션 관리</h2>
<ul>
<li>(생략)<br/>

</li>
</ul>
<h3 id="431-흔히-잘못-이해하는-connection-헤더">4.3.1 흔히 잘못 이해하는 Connection 헤더</h3>
<ul>
<li>HTTP는 중개 서버가 놓이는 것을 허락</li>
<li>HTTP 메시지는 중개 서버들을 하나하나 거치면서 전달됨</li>
</ul>
<blockquote>
</blockquote>
<p><strong>Connection 헤더에 전달될 수 있는 3가지 종류의 토큰</strong></p>
<ul>
<li><p>HTTP 헤더 필드명은 해당 커넥션에만 해당되는 헤더들 나열</p>
</li>
<li><p>임시적인 토큰 값 = 커넥션에 대한 비표준 옵션</p>
</li>
<li><p>close 값 = 커넥션 작업 완료 시 종료되어야 함을 의미</p>
</li>
<li><p>Connection 헤더에 있는 모든 헤더 필드들은 메시지를 다른 곳을 전달하는 시점에 삭제</p>
</li>
<li><p>헤더 보호하기 (Connection 헤더에서 흡별 헤더 명을 기술하는 것)</p>
</li>
</ul>
<br/>

<h3 id="432-순차적인-트랜잭션-처리에-의한-지연">4.3.2 순차적인 트랜잭션 처리에 의한 지연</h3>
<ul>
<li>커넥션 관리는 TCP 성능에 영향을 미침</li>
<li>순차적 처리의 단점<ul>
<li>순차적인 처리로 인한 지연에는 물리적인 지연뿐이 아닌, 심리적인 지연 또한 존재</li>
<li>특정 브라우저의 경우, 모든 객체를 내려받기 전까지 빈화면 보여줌 (크기 측정 때문)</li>
</ul>
</li>
<li>HTTP 커넥션 성능 향상을 위한 최신 기술<ul>
<li>병렬 커넥션 : 여러 개의 TCP 커넥션을 통한 동시 HTTP 요청</li>
<li>지속 커넥션 : 커넥션을 맺고 끊는 데서 발생하는 지연을 제거하기 위한 TCP 커넥션 재활용</li>
<li>파이프라인 커넥션 : 공유 TCP 커넥션을 통한 병렬 HTTP 요청</li>
<li>다중 커넥션 : 요청과 응답들에 대한 중재(실험적인 기술)</li>
</ul>
</li>
</ul>
<br/>

<hr>
<h2 id="44-병렬-커넥션">4.4 병렬 커넥션</h2>
<ul>
<li>(생략)<br/>

</li>
</ul>
<h3 id="441-병렬-커넥션은-페이지를-더-빠르게-내려받는다">4.4.1 병렬 커넥션은 페이지를 더 빠르게 내려받는다</h3>
<ul>
<li>하나의 커넥션으로 단일 커넥션의 대역폭 제한과 커넥션이 동작하지 않고 있는 시간 활용</li>
<li>각 커넥션의 지연 시간을 겹치게 함</li>
<li>나머지 객체를 내려받는 데에 남은 대역폭 사용</li>
</ul>
<br/>

<h3 id="442-병렬-커넥션이-항상-빠르지는-않다">4.4.2 병렬 커넥션이 항상 빠르지는 않다</h3>
<ul>
<li>클라이언트의 네트워크 대역폭이 좁을 때, 각 객체를 전송받는 것은 느리기 때문에 성능상의 장점은 거의 없어짐</li>
<li>다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제를 발생</li>
<li>브라우저는 실제로 병렬 커넥션을 사용하긴 하지만 적은 수의 병렬 커넥션만을 허용</li>
</ul>
<br/>

<h3 id="443-병렬-커넥션은-더-빠르게-느껴질-수-있다">4.4.3 병렬 커넥션은 더 빠르게 &#39;느껴질 수&#39; 있다</h3>
<ul>
<li>화면 여러 개의 객체가 동시에 보이면서 내려받고 있는 상황을 볼 수 있기 때문에 사용자는 빠르다고 느낄 수 있음</li>
</ul>
<br/>

<hr>
<h2 id="45-지속-커넥션">4.5 지속 커넥션</h2>
<ul>
<li>처리가 완료된 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용 가능</li>
<li>TCP의 느린 시작으로 인한 지연 피함<br/>

</li>
</ul>
<h3 id="451-지속-커넥션-vs-병렬-커넥션">4.5.1 지속 커넥션 vs 병렬 커넥션</h3>
<ul>
<li>병렬 커넥션의 단점<ul>
<li>각 트랜잭션마다 새로운 커넥션을 맺고 끊음으로 인해 시간과 대역폭이 소요</li>
<li>각각의 새로운 커넥션은 TCP 느린 시작으로 인해 성능 저하</li>
<li>실제로 연결할 수 있는 병렬 커넥션의 수 제한</li>
</ul>
</li>
<li>지속 커넥션의 장점<ul>
<li>커넥션 맺기 전 사전 작업과 지연 줄이며 튜닝된 커넥션 유지</li>
<li>커넥션의 수 줄임</li>
</ul>
</li>
<li>지속 커넥션의 단점<ul>
<li>지속 커넥션을 잘못 관리할 경우 연결 상태의 수많은 커넥션이 누적 (불필요한 소모 발생)</li>
</ul>
</li>
<li><strong>→ 지속 커넥션은 병렬 커넥션과 함께 사용할 때 가장 효과적</strong></li>
</ul>
<br/>

<h3 id="452-http10의-keep-alive-커넥션">4.5.2 HTTP/1.0+의 Keep-Alive 커넥션</h3>
<ul>
<li>연속적으로 각 커넥션을 생성하여 처리하는 방식과 비교했을 때, 지속 커넥션으로만 처리하는 방식은 시간을 단축시킴</li>
</ul>
<br/>

<h3 id="453-keep-alive-동작">4.5.3 Keep-Alive 동작</h3>
<ul>
<li>사용하지 않기로 결정되어 HTTP/1.1 명세에서 제외</li>
<li>하지만 아직 널리 사용. </li>
<li><img src="https://images.velog.io/images/dayeon-choi/post/37baa88a-75c8-4c4b-884b-9a074c69fe67/image.png" width="70%"/>

</li>
</ul>
<br/>

<h3 id="454-keep-alive-옵션">4.5.4 Keep-Alive 옵션</h3>
<ul>
<li>Keep-Alive 헤더를 커넥션을 유지하기 바라는 요청일 뿐 (무조건 따를 필요 없음)</li>
<li>동작은 헤더의 쉼표로 구분된 옵션들로 제어 가능</li>
</ul>
<blockquote>
</blockquote>
<p><strong>제어 가능한 옵션들</strong></p>
<ul>
<li>timeout</li>
<li>max 파라미터</li>
<li>헤더에서 임의의 속성 (문법 → 이름[=값])</li>
</ul>
<br/>

<h3 id="455-keep-alive-커넥션-제한과-규칙">4.5.5 Keep-Alive 커넥션 제한과 규칙</h3>
<ul>
<li>클라이언트는 Keep-Alive 커넥션을 사용하기 위해 Connection:Keep-Alive 요청 헤더를 보내야 함</li>
<li>커넥션이 끊어지기 전, 엔터티 본문의 길이를 알아햐 유지 가능</li>
<li>프락시와 게이트웨이는 메시지를 전달하거나 캐시에 넣기 전 Connection 헤더에 명시된 모든 헤더 필드와 Connection 헤더를 제거해야 함</li>
<li>Keep-Alive 커넥션은 Connection 헤더를 인식하지 못하는 프락시 서버와는 맺어지면 안됨</li>
<li>클라이언트는 응답 전체를 모두 받기 전 커넥션이 끊어졌을 경우, 요청을 다시 보낼 수 있도록 준비해야 함</li>
</ul>
<br/>

<h3 id="456-keep-alive와-멍청한dumb-프락시">4.5.6 Keep-Alive와 멍청한(dumb) 프락시</h3>
<ul>
<li>Connection 헤더의 무조건 전달<ul>
<li>프락시는 Connection 헤더를 이해하지 못해서 해당 헤더들을 삭제하지 않고 요청 그대로를 다음 프락시에 전달</li>
<li>통신에 혼선이 생겨 브라우저는 자신이나 서버가 타임아웃이 나서 커넥션이 끊길 때까지 기다림</li>
</ul>
</li>
<li>프락시와 흡별 헤더<ul>
<li>프락시는 Connection 헤더와 헤더에 명시된 헤더들을 절대 전달하면 안됨</li>
<li>헤더 뿐만 아니라 Keep-Alive 이름의 헤더도 전달하면 안됨</li>
</ul>
</li>
</ul>
<br/>

<h3 id="457-proxy-connection-살펴보기">4.5.7 Proxy-Connection 살펴보기</h3>
<ul>
<li>클라이언트의 요청이 중개서버를 통해 이어지는 경우 모든 헤더를 무조건 전달하는 문제를 해결하기 위해 사용</li>
<li>Connection 헤더 대신 비표준인 Proxy-Connection 확장 헤더를 프락시에게 전달</li>
<li>영리한 프락시는 Proxy-Connection 헤더가 Keep-Alive를 요청하는 것임을 인식하여 자체적으로 Connection:Keep-Alive 헤더를 웹 서버에 전송</li>
<li>이 방식은 클라이언트와 서버 사이 한 개의 프락시만 있는 경우에서만 동작</li>
</ul>
<br/>

<h3 id="458-http11-지속-커넥션">4.5.8 HTTP/1.1 지속 커넥션</h3>
<ul>
<li>애플리케이션은 트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection:close 헤더를 명시해야 함</li>
<li>위를 보내지 않는다고 하여 서버가 커넥션을 영원히 유지하겠다는 것을 뜻하진 않음</li>
</ul>
<br/>

<h3 id="459-지속-커넥션의-제한과-규칙">4.5.9 지속 커넥션의 제한과 규칙</h3>
<blockquote>
</blockquote>
<p><strong>상세 내용</strong></p>
<ul>
<li>클라이언트가 요청에 Connection:close 헤더를 포함했으면, 클라이언트는 해당 커넥션으로 추가적인 요청 보낼 수 없음</li>
<li>클라이언트가 해당 커넥션으로 추가 요청을 보내지 않을 것이라면, Connectoin:close 명시</li>
<li>커넥션에 있는 모든 메시지가 자신의 길이 정보를 가지고 있을 때만 커넥션 지속 가능</li>
<li>하나의 사용자 클라이언트는 서버의 과부하 방지를 위해 넉넉잡아 두 개의 지속 커넥션만을 유지 (N명 → 2N개 커넥션 유지)</li>
</ul>
<br/>

<hr>
<h2 id="46-파이프라인-커넥션">4.6 파이프라인 커넥션</h2>
<blockquote>
</blockquote>
<p><strong>파이프라인 제약 사항</strong></p>
<ul>
<li>HTTP 클라이언트는 커넥션이 지속 커넥션인지 확인하기 전까지는 파이프라인을 이어서는 안됨</li>
<li>HTTP 응답은 요청 순서와 같게 와야 함</li>
<li>HTTP 클라이언트는 커넥션이 언제 끊어지더라도, 완료되지 않은 요청이 파이프라인에 있으면 언제든 다시 요청을 보낼 준비가 되어 있어야 함</li>
<li>HTTP 클라이언트는 POST 요청같이 반복해서 보낼 경우 문제가 생기는 요청은 파이프라인을 통해 보내면 안됨</li>
</ul>
<br/>

<hr>
<h2 id="47-커넥션-끊기에-대한-미스터리">4.7 커넥션 끊기에 대한 미스터리</h2>
<ul>
<li>커넥션 관리에는 명확한 기준이 없음. <br/>

</li>
</ul>
<h3 id="471-마음대로-커넥션-끊기">4.7.1 &#39;마음대로&#39; 커넥션 끊기</h3>
<ul>
<li>어떠한 HTTP 클라이언트, 서버, 혹은 프락시든 언제든지 TCP 전송 커넥션을 끊을 수 있음</li>
<li>단, 그 끊는 시점에 데이터를 전송하지 않을 것이라고 확신하지 못하기 때문에, 클라이언트는 요청 메시지를 보내는 도중에 문제가 생길 수 있음</li>
</ul>
<br/>

<h3 id="472-content-length와-truncation">4.7.2 Content-Length와 Truncation</h3>
<ul>
<li>각 HTTP 응답은 본문의 정확한 크기 값을 가지는 Content-Length 헤더를 가지고 있어야 함</li>
<li>클라이언트나 프락시가 커넥션이 끊어졌다는 HTTP 응답을 받은 후, 이에 대한 값이 일치하지 않거나 존재하지 않는다면 수신자는 정확한 길이를 서버에 물어야 함</li>
</ul>
<br/>

<h3 id="473-커넥션-끊기의-허용-재시도-멱등성">4.7.3 커넥션 끊기의 허용, 재시도, 멱등성</h3>
<ul>
<li>커넥션은 에러가 없더라도 언제든 끊기 가능</li>
<li>이로 인한 부작용 주의</li>
<li>멱등(실행 횟수에 상관 없이 같은 결과를 반환함)이 아닌 요청은 파이프라인을 통해 요청하면 안됨</li>
<li>멱등 메서드들 <code>GET, HEAD, PUT, DELETE, TRACE</code></li>
<li>비멱등인 메서드나 순서에 대해 에이전트가 요청을 다시 보낼 수 있도록 기능을 제공한다 하더라도, 자동을 재시도하면 안됨</li>
</ul>
<br/>

<h3 id="474-우아한-커넥션-끊기">4.7.4 우아한 커넥션 끊기</h3>
<ul>
<li>전체 끊기와 절반 끊기<ul>
<li><code>close()</code>를 호출하면 커넥션의 입력/출력 채널 커넥션 모두 끊음</li>
<li><code>shutdown()</code>을 호출하면 입력/출력 채널 중 하나를 개별적으로 끊음</li>
</ul>
</li>
<li>TCP 끊기와 리셋 에러<ul>
<li>애플리케이션이 각기 다른 HTTP 클라이언트, 서버, 프락시와 통신할 때, 파이프 라인 지속 커넥션을 사용할 땐 절반 끊기를 사용해야 함</li>
<li>보통은 커넥션의 출력 채널을 끊는 것이 안전</li>
</ul>
</li>
<li>우아하게 커넥션 끊기</li>
<li>애플리케이션 자신의 출력 채널을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널이 끊기는 것을 기다리는 것</li>
<li>끊기 후에도 주기적으로 상태 검사를 해야 함</li>
</ul>
<br/>

<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[1장] HTTP 개관]]></title>
            <link>https://velog.io/@dayeon-choi/1.1-HTTP-%EC%9B%B9%EC%9D%98-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@dayeon-choi/1.1-HTTP-%EC%9B%B9%EC%9D%98-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Wed, 04 Aug 2021 04:51:36 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>HTTP 프로토콜에 대해 알아보자</p>
</blockquote>
<hr>
<h3 id="11-http-인터넷의-멀티미디어-배달부">1.1 HTTP: 인터넷의 멀티미디어 배달부</h3>
<ul>
<li>HTTP는 전 세계의 웹 서버로부터 자원들을 클라이언트들의 웹 브라우저로 옮겨줌</li>
<li>신뢰성 있는 데이터 전송 프로토콜을 사용하기 때문에, 데이터 손상의 염려는 ✕</li>
</ul>
<hr>
<h3 id="12-웹-클라이언트와-서버">1.2 웹 클라이언트와 서버</h3>
<ul>
<li>월드 와이드 웹의 <strong>기본 요소</strong></li>
<li><img src="https://images.velog.io/images/dayeon-choi/post/f0a6692b-d40c-4101-865f-36c9268d5068/image.png" width="60%" /></li>
<li>웹 서버는<ul>
<li>HTTP 프로토콜로 의사소통하기 때문에 보통 HTTP 서버라고 불림</li>
<li>인터넷의 데이터를 저장, HTTP 클라이언트가 요청한 데이터 제공</li>
</ul>
</li>
<li>순서<ul>
<li>1) 클라이언트가 서버에게 HTTP 요청을 보냄</li>
<li>2) 서버는 요청된 데이터를 HTTP 응답으로 돌려줌</li>
</ul>
</li>
</ul>
<p><span style='color:gray'>이미지 출처: 생활코딩</span></p>
<hr>
<h3 id="13-리소스">1.3 리소스</h3>
<ul>
<li><strong>웹 콘텐츠의 원천</strong>. 웹 서버가 이를 관리하고 제공함</li>
<li>정적 파일, 요청에 따라 콘텐츠를 생산하는 프로그램 모두 리소스</li>
<li><span style='color:gray'>정적 파일 EX) 텍스트/HTML/마이크로소프트 워드/어도비 아크로뱃/JPEG 이미지/AVI 동영상 파일</span></li>
</ul>
<p><strong>1) 미디어 타입</strong></p>
<ul>
<li>HTTP는 웹에서 전송되는 객체 각각에 <strong>MIME 타입</strong>이라는 데이터 포맷 라벨을 붙임</li>
<li>🔎 MIME<ul>
<li>전자메일 시스템에서 유래. 멀티미디어 콘텐츠를 기술하고 라벨을 붙이기 위해 채택</li>
<li>사선(/)으로 구분된 주 타입과 부 타입으로 이루어진 문자열 라벨</li>
<li><code>HTML</code>→<code>text/html</code> 
<code>plain ASCII</code>→<code>text/plain</code> 
<code>JPEG</code>→<code>image/jpeg</code>
<code>apple quick time player</code>→<code>video/quicktime</code></li>
</ul>
</li>
</ul>
<p><strong>2) URI</strong></p>
<ul>
<li>서버 리소스, 통합 자원 식별자(<strong>u</strong>niform <strong>r</strong>esource <strong>i</strong>dentifier)</li>
<li>인터넷의 우편물 주소, 정보 리소스를 고유하게 식별하고 위치를 지정함</li>
<li>HTTP는 주어진 URI로 객체를 찾아옴</li>
<li>종류는 URL과 URN</li>
</ul>
<p><strong>3) URL</strong></p>
<ul>
<li>통합 자원 지시자(<strong>u</strong>niform <strong>r</strong>esouce <strong>l</strong>ocator)</li>
<li>오늘날 대부분의 URI, 통상적인 관례로 URI와 URL은 같은 의미로 사용함</li>
<li>특정 서버의 한 리소스에 대한 구체적인 위치를 서술 (리소스의 위치, 접근방법 표현)</li>
<li>URL의 세 부분<ul>
<li>스킴(scheme), 첫 번째 부분, 리소스 접근을 위해 사용되는 프로토콜 서술</li>
<li>두 번째 부분, 인터넷 주소 제공</li>
<li>마지막 부분, 웹 서버의 리소스 가리킴</li>
</ul>
</li>
</ul>
<p><strong>4) URN</strong></p>
<ul>
<li>유니폼 리소스 이름(<strong>u</strong>niform <strong>r</strong>esource <strong>n</strong>ame), 위치 독립적인 유일한 이름, 실험 중인 상태</li>
<li>콘텐츠를 이루는 한 리소스에 대해, 그 리소스 위치에 영향 받지 않는 유일한 이름</li>
<li>리소스가 이름 유지만 한다면, 여러 종류의 네트워크 접속 프로토콜로 접근해도 문제 ✕</li>
</ul>
<hr>
<h3 id="14-트랜잭션">1.4 트랜잭션</h3>
<ul>
<li>HTTP 트랜젝션은 요청 명령(클라이언트→서버)과 응답 결과(서버→클라이언트)로 구성</li>
<li>위 상호작용은 HTTP 메시지라고 불리는 정형화된 데이터 덩어리를 이용</li>
<li><img src="https://images.velog.io/images/dayeon-choi/post/8172e063-5eef-4e2e-b7d8-52872b7f4e49/image.png" width="70%"/>

</li>
</ul>
<p><strong>1) 메서드</strong></p>
<ul>
<li>HTTP 메서드, HTTP가 지원하는 여러 가지 종류의 요청 명령</li>
<li>모든 HTTP 요청 메시지는 한 개의 메서드를 가짐</li>
<li>메서드는 서버에게 어떤 동작이 취해져야 하는지 말해줌</li>
<li><img src="https://images.velog.io/images/dayeon-choi/post/5792fd3f-da5d-4db6-b9bd-a322067ebbef/image.png" width="70%"/>

</li>
</ul>
<p><strong>2) 상태코드</strong></p>
<ul>
<li><img src="https://images.velog.io/images/dayeon-choi/post/f1496f66-bdc9-49b3-a29d-36b5cea34965/image.png" width="70%" />

</li>
</ul>
<p><strong>3) 웹페이지는 여러 객체로 이루어질 수 있다</strong></p>
<ul>
<li>애플리케이션은 보통 하나의 작업을 수행하기 위해 여러 HTTP 트랜젝션을 수행함</li>
<li>순서<ul>
<li>1) 페이지 레이아웃을 서술하는 HTML 뼈대를 한 번의 트랜잭션으로 가져옴</li>
<li>2) 이미지, 그래픽 조각, 자바 애플릿 등 가져오기 위한 추가 트랜잭션 수행</li>
</ul>
</li>
</ul>
<hr>
<h3 id="15-메시지">1.5 메시지</h3>
<ul>
<li>HTTP 메시지, 단순한 줄 단위의 문자열, 일반 텍스트 형식</li>
<li>요청 메시지와 응답 메시지 두 가지 뿐</li>
<li>HTTP 메시지의 세 부분<ul>
<li><strong>시작줄</strong>: 메시지의 첫 줄, 요청이라면 해야 할 일, 응답이라면 일어난 일 나타냄</li>
<li><strong>헤더</strong>: 시작줄 다음 줄, 0개 이상의 헤더 필드가 이어짐, 쌍점(:)으로 구분되어 있는 하나 값과 이름으로 구성, 빈 줄로 끝남</li>
<li><strong>본문</strong>: 어떤 종류의 데이터든 들어갈 수 있음, 웹 서버로 데이터 실어 보냄</li>
</ul>
</li>
</ul>
<hr>
<h3 id="16-tcp-커넥션">1.6 TCP 커넥션</h3>
<ul>
<li>전송 제어 프로토콜(<strong>T</strong>ransmission <strong>C</strong>ontrol <strong>P</strong>rotocol)</li>
<li>인터넷 프로토콜(<strong>I</strong>nternet <strong>P</strong>rotocol)</li>
</ul>
<p><strong>1) TCP/IP</strong></p>
<ul>
<li>HTTP가 신경쓰지 않는 네트워크 통신의 핵심 세부사항에 대해 관리</li>
<li>TCP/IP는 TCP와 IP가 층을 이루는, 패킷 교환 네트워크 프로토콜의 집합, TCP가 더 위의 계층</li>
<li>각 네트워크와 하드웨어의 특성을 숨기고 종류에 상관 없이 신뢰성 있는 의사소통을 가능하게 함</li>
<li>TCP가 제공하는 것<ul>
<li>오류 없는 데이터 전송</li>
<li>순서에 맞는 전달 (데이터는 언제나 보낸 순서대로 도착)</li>
<li>조각나지 않는 데이터 스트림 (언제든 어떤 크기로든 보내기 가능)  </li>
</ul>
</li>
</ul>
<p><strong>2) 접속, IP 주소 그리고 포트 번호</strong></p>
<ul>
<li>전송을 위해서는 인터넷 프로토콜 주소와 포트번호를 사용해 클라이언트와 서버 사이 TCP/IP 커넥션이 필요</li>
<li>URL은 그 리소스를 가지고 있는 장비에 대한 IP 주소를 알려줄 수 있음</li>
<li>순서<ul>
<li>1) 웹브라우저는 서버의 URL에서 호스트 명 추출</li>
<li>2) 웹브라우저는 서버의 호스트 명을 IP로 변환</li>
<li>3) 웹브라우저는 URL에서 포트번호가 존재한다면 추출</li>
<li>4) 웹브라우저는 웹 서버와 TCP 커넥션을 맺음</li>
<li>5) 웹브라우저는 서버에 HTTP 요청을 보냄</li>
<li>6) 서버는 웹 브라우저에 HTTP 응답을 돌려줌</li>
<li>7) 커넥션이 닫히면, 웹브라우저는 문서를 보여줌</li>
</ul>
</li>
</ul>
<hr>
<h3 id="17-프로토콜-버전">1.7 프로토콜 버전</h3>
<ul>
<li><p><strong>HTTP/0.9</strong></p>
<ul>
<li>간단한 HTML 객체를 받아오기 위해 만들어짐</li>
<li>심각한 디자인 결함이 다수 있음</li>
<li>구식 클라이언트와만 사용 가능</li>
<li><code>GET</code>메서드만 지원</li>
</ul>
</li>
<li><p><strong>HTTP/1.0</strong></p>
<ul>
<li>처음으로 널리 쓰이기 시작한 버전</li>
<li>추가된 지원 (버전 번호, 헤더, 추가 메서드, 멀티미디어 객체 처리)</li>
<li>잘 정의된 명세는 아님</li>
</ul>
</li>
<li><p><strong>HTTP/1.0+</strong></p>
<ul>
<li>상업적 성공에 따른 요구를 만족시키기 위해 기능을 추가한 버전</li>
</ul>
</li>
<li><p><strong>HTTP/1.1</strong></p>
<ul>
<li>HTTP 설계의 구조적 결함 교정, 성능 최적화, 잘못된 기능 제거에 집중된 버전</li>
</ul>
</li>
<li><p><strong>HTTP/2.0</strong></p>
<ul>
<li>성능 문제 개선을 위해 구글의 SPDY 프로토콜을 기반으로 설계가 진행중인 프로토콜</li>
</ul>
</li>
</ul>
<hr>
<h3 id="18-웹의-구성요소">1.8 웹의 구성요소</h3>
<p><strong>1) 프락시</strong></p>
<ul>
<li>클라이언트와 서버 사이에 위치한 신뢰할 만한 HTTP 중개자</li>
<li>주로 보안을 위해 사용</li>
<li>요청과 응답을 필터링</li>
</ul>
<p><strong>2) 캐시</strong></p>
<ul>
<li>많이 찾는 웹페이지를 클라이언트 가까이에 보관하는 HTTP 창고</li>
<li>특별한 종류의 HTTP 프락시 서버</li>
<li>다음번에 클라이언트가 같은 문서를 요청하면 그 캐시가 갖고 있는 사본을 받을 수 있음</li>
</ul>
<p><strong>3) 게이트웨이</strong></p>
<ul>
<li>다른 애플리케이션과 연결된 특별한 웹 서버</li>
<li>주로 HTTP 트래픽을 다른 프로토콜로 변환하기 위해 사용</li>
<li>언제나 스스로가 리소스를 갖고 있는 진짜 서버인 것처럼 요청을 다뤄 클라이언트가 눈치채지 못함</li>
</ul>
<p><strong>4) 터널</strong></p>
<ul>
<li>단순히 HTTP 통신을 전달하기만 하는 특별한 프락시</li>
<li>주로 비 HTTP 데이터를 하나 이상의 HTTP 연결을 통해 그대로 전송해주기 위해 사용</li>
</ul>
<p><strong>5) 에이전트</strong></p>
<ul>
<li>자동화된 HTTP 요청을 만드는 준지능적(semi-intelligent) 클라이언트 프로그램</li>
<li>웹 요청을 만드는 애플리케이션은 뭐든 HTTP 에이전트</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[16장] SerialDate 리팩터링]]></title>
            <link>https://velog.io/@dayeon-choi/16%EC%9E%A5-SerialDate-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@dayeon-choi/16%EC%9E%A5-SerialDate-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81</guid>
            <pubDate>Sun, 01 Aug 2021 11:14:11 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>SerialDate : 날짜를 표현하는 자바 클래스</p>
</blockquote>
<hr>
<h2 id="첫째-돌려보자">첫째, 돌려보자</h2>
<ul>
<li>단위 테스트 케이스 몇 개를 포함하는 SerialDateTests 클래스</li>
<li>실패하는 테스트 케이스는 없지만 모든 경우를 점검하지는 않음</li>
<li>주석처리 된, 실패한 테스트 케이스가 있음</li>
<li>경계 조건 오류 발견</li>
</ul>
<hr>
<h2 id="둘째-고쳐보자-과정">둘째, 고쳐보자 (과정)</h2>
<ul>
<li>소스 코드 제어 도구를 사용하므로 필요성이 없어진 <strong>변경 이력 제거</strong></li>
<li>통합할 수 있는 import 문, <code>java.util.*</code>으로 수정</li>
<li>클래스 이름 수정 <code>SerialDate</code> → <code>DayDate</code></li>
<li>불필요한 주석 제거</li>
<li>상수 모음인 <code>MonthConstants</code>는 <code>enum</code>으로 정의</li>
<li>ABSTRACT FACTORY 패턴 적용하여 DayDateFactory 생성</li>
<li>메서드 이름 변경 <code>createInstance</code> → <code>makeDate</code></li>
<li>사용하지 않는 변수, 메서드, 생성자 제거</li>
<li>연관이 높은 코드들은 위치 또한 가깝게 배치</li>
<li>연산자를 사용해 하나의 <code>if</code> 문으로 수정</li>
<li>단순화 할 수 있는 메서드는 합쳐 단순화</li>
<li>이름을 좀 더 서술적으로 변경</li>
<li>정적 메서드를 인스턴스 메서드로 변경</li>
<li>중복 인스턴스 메서드 제거</li>
</ul>
<br/>

<ul>
<li>🔎 <strong>ABSTRACT FACTORY</strong><ul>
<li><a href="https://beomseok95.tistory.com/243">https://beomseok95.tistory.com/243</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="둘째-고쳐보자-정리">둘째, 고쳐보자 (정리)</h2>
<ul>
<li>1) 주석은 간단하게 개선</li>
<li>2) <code>enum</code>을 모두 독자적인 소스 파일로 옮김</li>
<li>3) 정적 변수와 정적 메서드를 하나의 새 클래스로 옮김</li>
<li>4) 일부 추상 메서드를 수준에 따라 특정 클래스로 끌어올림</li>
<li>5) 메서드 이름을 변경하고, 목적과 의미에 따라 접근자와 접근제어자 재설정</li>
<li>6) 중복 메서드를 제거하여 명확하게 함</li>
<li>7) 이름에서 의미없이 남발하던 특정 키워드를 없애고 적절하게 변경</li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<ul>
<li>보이스카우트 규칙을 따라 더 깨끗하게 코드 체크인 진행</li>
<li>결과 → 테스트 커버리지 증가, 버그 해결, 코드 크기 감소, 코드 명확</li>
</ul>
<hr>
<h2 id="인상-깊었던">인상 깊었던...</h2>
<blockquote>
<p><em>누구나 사용하고 누구나 비판하라고 코드를 만천하에 공개했다. 참으로 훌륭한 행동이다!</em></p>
</blockquote>
<blockquote>
<p><em>시간은 걸렸지만 가치 있는 작업이었다.</em></p>
</blockquote>
<blockquote>
<p><em>다음 사람은 우리보다 코드를 좀 더 쉽게 이해하리라. 그래서 우리보다 코드를 좀 더 쉽게 개선하리라</em></p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[15장] JUnit 들여다보기]]></title>
            <link>https://velog.io/@dayeon-choi/15%EC%9E%A5-JUnit-%EB%93%A4%EC%97%AC%EB%8B%A4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dayeon-choi/15%EC%9E%A5-JUnit-%EB%93%A4%EC%97%AC%EB%8B%A4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 01 Aug 2021 09:37:09 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>JUnit 프레임워크에서 가져온 코드를 평가해보자.</p>
</blockquote>
<hr>
<h2 id="junit-프레임워크">JUnit 프레임워크</h2>
<ul>
<li>저자가 많다.</li>
<li>알아볼 모듈은 문자열 비교 오류를 파악할 때 유용한 코드.</li>
<li>전반적으로 상당히 훌륭한 모듈.</li>
</ul>
<hr>
<h2 id="원본-코드">원본 코드</h2>
<pre><code class="language-java">package junit.framework;

public class ComparisonCompactor {

    private static final String ELLIPSIS = &quot;...&quot;;
    private static final String DELTA_END = &quot;]&quot;;
    private static final String DELTA_START = &quot;[&quot;;

    private int fContextLength;
    private String fExpected;
    private String fActual;
    private int fPrefix;
    private int fSuffix;

    public ComparisonCompactor(int contextLength, String expected, String actual) {
        fContextLength = contextLength;
        fExpected = expected;
        fActual = actual;
    }

    public String compact(String message) {
        if (fExpected == null || fActual == null || areStringsEqual()) {
            return Assert.format(message, fExpected, fActual);
        }

        findCommonPrefix();
        findCommonSuffix();
        String expected = compactString(fExpected);
        String actual = compactString(fActual);
        return Assert.format(message, expected, actual);
    }

    private String compactString(String source) {
        String result = DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
        if (fPrefix &gt; 0) {
            result = computeCommonPrefix() + result;
        }
        if (fSuffix &gt; 0) {
            result = result + computeCommonSuffix();
        }
        return result;
    }

    private void findCommonPrefix() {
        fPrefix = 0;
        int end = Math.min(fExpected.length(), fActual.length());
        for (; fPrefix &lt; end; fPrefix++) {
            if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix)) {
                break;
            }
        }
    }

    private void findCommonSuffix() {
        int expectedSuffix = fExpected.length() - 1;
        int actualSuffix = fActual.length() - 1;
        for (; actualSuffix &gt;= fPrefix &amp;&amp; expectedSuffix &gt;= fPrefix; actualSuffix--, expectedSuffix--) {
            if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix)) {
                break;
            }
        }
        fSuffix = fExpected.length() - expectedSuffix;
    }

    private String computeCommonPrefix() {
        return (fPrefix &gt; fContextLength ? ELLIPSIS : &quot;&quot;) + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
    }

    private String computeCommonSuffix() {
        int end = Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
        return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 &lt; fExpected.length() - fContextLength ? ELLIPSIS : &quot;&quot;);
    }

    private boolean areStringsEqual() {
        return fExpected.equals(fActual);
    }
}</code></pre>
<hr>
<h2 id="개선-과정">개선 과정</h2>
<p>👉 보이스카우트 규칙에 따라, 우린 처음 왔을 때보다 더 깨끗하게 해놓고 떠나야 한다.</p>
<ul>
<li><strong>접두어 f 모두 제거</strong>
오늘날 사용하는 개발 환경에서는 변수 이름에 범위를 명시할 필요가 없다. </li>
<li><strong>캡슐화</strong>
의도를 명확히 표현하려면 조건문을 캡슐화 해야한다. 메서드로 뽑아내 적절한 이름 붙이기.</li>
<li><strong>이름 수정</strong>
의미를 파악해 명확한 이름으로 수정한다. 변수, 함수, 멤버 변수...</li>
<li><strong>조건문 반전</strong>
부정문은 긍정문보다 이해하기 어려우므로 첫 문장 if문을 긍정으로 바꿔 반전시키기.</li>
<li><strong>함수 사용 방식 일관화</strong>
함수 사용 방식이 일관되도록 반환 여부를 맞춰주기.</li>
<li><strong>숨겨진 시간적 결합 파악</strong>
숨겨진 시간적 결합이 있다면 이를 외부에 노출하기.</li>
<li><strong>원래대로 돌려놓을 부분 돌려놓기</strong>
리팩터링에서의 흔한 경우. 수많은 시행착오를 반복하는 작업.</li>
</ul>
<hr>
<h2 id="최종-코드">최종 코드</h2>
<pre><code class="language-java">package junit.framework;

public class ComparisonCompactor {

    private static final String ELLIPSIS = &quot;...&quot;;
    private static final String DELTA_END = &quot;]&quot;;
    private static final String DELTA_START = &quot;[&quot;;

    private int contextLength;
    private String expected;
    private String actual;
    private int prefixLength;
    private int suffixLength;

    public ComparisonCompactor(int contextLength, String expected, String actual) {
        this.contextLength = contextLength;
        this.expected = expected;
        this.actual = actual;
    }

    public String formatCompactedComparison(String message) {
        String compactExpected = expected;
        String compactactual = actual;
        if (shouldBeCompacted()) {
            findCommonPrefixAndSuffix();
            compactExpected = comapct(expected);
            compactActual = comapct(actual);
        }         
        return Assert.format(message, compactExpected, compactActual);      
    }

    private boolean shouldBeCompacted() {
        return !shouldNotBeCompacted();
    }

    private boolean shouldNotBeCompacted() {
        return expected == null &amp;&amp; actual == null &amp;&amp; expected.equals(actual);
    }

    private void findCommonPrefixAndSuffix() {
        findCommonPrefix();
        suffixLength = 0;
        for (; suffixOverlapsPrefix(suffixLength); suffixLength++) {
            if (charFromEnd(expected, suffixLength) != charFromEnd(actual, suffixLength)) {
                break;
            }
        }
    }

    private boolean suffixOverlapsPrefix(int suffixLength) {
        return actual.length() = suffixLength &lt;= prefixLength || expected.length() - suffixLength &lt;= prefixLength;
    }

    private void findCommonPrefix() {
        int prefixIndex = 0;
        int end = Math.min(expected.length(), actual.length());
        for (; prefixLength &lt; end; prefixLength++) {
            if (expected.charAt(prefixLength) != actual.charAt(prefixLength)) {
                break;
            }
        }
    }

    private String compact(String s) {
        return new StringBuilder()
            .append(startingEllipsis())
            .append(startingContext())
            .append(DELTA_START)
            .append(delta(s))
            .append(DELTA_END)
            .append(endingContext())
            .append(endingEllipsis())
            .toString();
    }

    private String startingEllipsis() {
        prefixIndex &gt; contextLength ? ELLIPSIS : &quot;&quot;
    }

    private String startingContext() {
        int contextStart = Math.max(0, prefixLength = contextLength);
        int contextEnd = prefixLength;
        return expected.substring(contextStart, contextEnd);
    }

    private String delta(String s) {
        int deltaStart = prefixLength;
        int deltaend = s.length() = suffixLength;
        return s.substring(deltaStart, deltaEnd);
    }

    private String endingContext() {
        int contextStart = expected.length() = suffixLength;
        int contextEnd = Math.min(contextStart + contextLength, expected.length());
        return expected.substring(contextStart, contextEnd);
    }

    private String endingEllipsis() {
        return (suffixLength &gt; contextLength ? ELLIPSIS : &quot;&quot;);
    }
}</code></pre>
<hr>
<h2 id="인상-깊었던">인상 깊었던...</h2>
<blockquote>
<p><em>코드를 리팩터링 하다 보면 원래 했던 변경을 되돌리는 경우가 흔하다. 리팩터링은 코드가 어느 수준에 이를 때까지 수많은 시행착오를 반복하는 작업이기 때문이다.</em></p>
</blockquote>
<blockquote>
<p><em>하지만 세상에 개선이 불필요한 모듈은 없다. 코드를 처음보다 조금 더 깨끗하게 만드는 책임은 우리 모두에게 있다.</em></p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[14장] 점진적인 개선]]></title>
            <link>https://velog.io/@dayeon-choi/14%EC%9E%A5-%EC%A0%90%EC%A7%84%EC%A0%81%EC%9D%B8-%EA%B0%9C%EC%84%A0</link>
            <guid>https://velog.io/@dayeon-choi/14%EC%9E%A5-%EC%A0%90%EC%A7%84%EC%A0%81%EC%9D%B8-%EA%B0%9C%EC%84%A0</guid>
            <pubDate>Sun, 01 Aug 2021 08:42:11 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다는 의미이다.</p>
</blockquote>
<hr>
<h2 id="args-구현-코드-정리">Args 구현 (코드 정리)</h2>
<ul>
<li>Args.java<pre><code class="language-java">import java.util.*;
</code></pre>
</li>
</ul>
<p>public class Args {
    private String schema;
    private Map&lt;Character, ArgumentMarshaler&gt; marshalers =
                                             new HashMap&lt;Character, ArgumentMarshaler&gt;();
    private Set<Character> argsFound = new HashSet<Character>();
    private Iterator<String> currentArgument;
    private List<String> argsList;</p>
<pre><code>public Args(String schema, String[] args) throws ArgsException {
    this.schema = schema;
    argsList = Arrays.asList(args);
    parse();
}

private void parse() throws ArgsException {
    parseSchema();
    parseArguments();
}

private boolean parseSchema() throws ArgsException {
    for (String element : schema.split(&quot;,&quot;)) {
        if (element.length() &gt; 0) {
            parseSchemaElement(element.trim());
        }
    }
    return true;
}

private void parseSchemaElement(String element) throws ArgsException {
    char elementId = element.charAt(0);
    String elementTail = element.substring(1);
    validateSchemaElementId(elementId);
    if (elementTail.length() == 0)
        marshalers.put(elementId, new BooleanArgumentMarshaler());
    else if (elementTail.equals(&quot;*&quot;))
        marshalers.put(elementId, new StringArgumentMarshaler());
    else if (elementTail.equals(&quot;#&quot;))
        marshalers.put(elementId, new IntegerArgumentMarshaler());
    else if (elementTail.equals(&quot;##&quot;))
        marshalers.put(elementId, new DoubleArgumentMarshaler());
    else
        throw new ArgsException(ArgsException.ErrorCode.INVALID_FORMAT,
            elementId, elementTail);
}

private void validateSchemaElementId(char elementId) throws ArgsException {
    if (!Character.isLetter(elementId)) {
        throw new ArgsException(ArgsException.ErrorCode.INVALID_ARGUMENT_NAME,
            elementId, null);
    }
}

private void parseArguments() throws ArgsException {
    for (currentArgument = argsList.iterator(); currentArgument.hasNext();) {
        String arg = currentArgument.next();
        parseArgument(arg);
    }
}

private void parseArgument(String arg) throws ArgsException {
    if (arg.startsWith(&quot;-&quot;))
        parseElements(arg);
}

private void parseElements(String arg) throws ArgsException {
    for (int i = 1; i &lt; arg.length(); i++)
        parseElement(arg.charAt(i));
}

private void parseElement(char argChar) throws ArgsException {
    if (setArgument(argChar))
        argsFound.add(argChar);
    else {
        throw new ArgsException(ArgsException.ErrorCode.UNEXPECTED_ARGUMENT,
            argChar, null);
    }
}

private boolean setArgument(char argChar) throws ArgsException {
    ArgumentMarshaler m = marshalers.get(argChar);
    if (m == null)
        return false;
    try {
        m.set(currentArgument);
        return true;
    } catch (ArgsException e) {
        e.setErrorArgumentId(argChar);
        throw e;
    }
}
public int cardinality() {
    return argsFound.size();
}

public String usage() {
    if (schema.length() &gt; 0)
        return &quot;-[&quot; + schema + &quot;]&quot;;
    else
        return &quot;&quot;;
}

public boolean getBoolean(char arg) {
    ArgumentMarshaler am = marshalers.get(arg);
    boolean b = false;
    try {
        b = am != null &amp;&amp; (Boolean) am.get();
    } catch (ClassCastException e) {
        b = false;
    }
    return b;
}

public String getString(char arg) {
    ArgumentMarshaler am = marshalers.get(arg);
    try {
        return am == null ? &quot;&quot; : (String) am.get();
    } catch (ClassCastException e) {
        return &quot;&quot;;
    }
}

public int getInt(char arg) {
    ArgumentMarshaler am = marshalers.get(arg);
    try {
        return am == null ? 0 : (Integer) am.get();
    } catch (Exception e) {
        return 0;
    }
}


public double getDouble(char arg) {
    ArgumentMarshaler am = marshalers.get(arg);
    try {
        return am == null ? 0 : (Double) am.get();
    } catch (Exception e) {
        return 0.0;
    }
}

public boolean has(char arg) {
    return argsFound.contains(arg);
}</code></pre><p>}</p>
<pre><code>
* ArgsException.java
```java
public class ArgsException extends Exception {
    private char errorArgumentId = &#39;\0&#39;;
    private String errorParameter = &quot;TILT&quot;;
    private ErrorCode errorCode = ErrorCode.OK;

    public ArgsException() {}

    public ArgsException(String message) {super(message);}

    public ArgsException(ErrorCode errorCode) {
        this.errorCode = errorCode;
    }
    public ArgsException(ErrorCode errorCode, String errorParameter) {
        this.errorCode = errorCode;
        this.errorParameter = errorParameter;
    }

    public ArgsException(ErrorCode errorCode, char errorArgumentId,
        String errorParameter) {
        this.errorCode = errorCode;
        this.errorParameter = errorParameter;
        this.errorArgumentId = errorArgumentId;
    }

    public char getErrorArgumentId() {
        return errorArgumentId;
    }

    public void setErrorArgumentId(char errorArgumentId) {
        this.errorArgumentId = errorArgumentId;
    }

    public String getErrorParameter() {
        return errorParameter;
    }

    public void setErrorParameter(String errorParameter) {
        this.errorParameter = errorParameter;
    }

    public ErrorCode getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(ErrorCode errorCode) {
        this.errorCode = errorCode;
    }

    public String errorMessage() throws Exception {
        switch (errorCode) {
            case OK:
            throw new Exception(&quot;TILT: Should not get here.&quot;);
            case UNEXPECTED_ARGUMENT:
            return String.format(&quot;Argument -%c unexpected.&quot;, errorArgumentId);
            case MISSING_STRING:
            return String.format(&quot;Could not find string parameter for -%c.&quot;,
                errorArgumentId);
            case INVALID_INTEGER:
            return String.format(&quot;Argument -%c expects an integer but was &#39;%s&#39;.&quot;,
                errorArgumentId, errorParameter);
            case MISSING_INTEGER:
            return String.format(&quot;Could not find integer parameter for -%c.&quot;,
                errorArgumentId);
            case INVALID_DOUBLE:
            return String.format(&quot;Argument -%c expects a double but was &#39;%s&#39;.&quot;,
                errorArgumentId, errorParameter);
            case MISSING_DOUBLE:
            return String.format(&quot;Could not find double parameter for -%c.&quot;,
                errorArgumentId);
        }
        return &quot;&quot;;
    }

    public enum ErrorCode {
        OK, INVALID_FORMAT, UNEXPECTED_ARGUMENT, INVALID_ARGUMENT_NAME,
        MISSING_STRING,
        MISSING_INTEGER, INVALID_INTEGER,
        MISSING_DOUBLE, MISSING_BOOLEAN, INVALID_BOOLEAN, INVALID_DOUBLE}
    }</code></pre><ul>
<li>ArgumentMarshaler.java<pre><code class="language-java">import java.util.Iterator;
</code></pre>
</li>
</ul>
<p>public interface ArgumentMarshaler {
    void set(Iterator<String> currentArgument) throws ArgsException;
    Object get();
}</p>
<pre><code>
* BooleanArgumentMarshaler.java
```java
import java.util.Iterator;
import java.util.NoSuchElementException;

import static clean.code.chapter14.refactored.second.ArgsException.ErrorCode.INVALID_BOOLEAN;
import static clean.code.chapter14.refactored.second.ArgsException.ErrorCode.MISSING_BOOLEAN;

public class BooleanArgumentMarshaler implements ArgumentMarshaler {
  private boolean booleanValue = false;

  public void set(Iterator&lt;String&gt; currentArgument) throws ArgsException {
    String parameter = null;
    try {
      parameter = currentArgument.next();
      booleanValue = Boolean.parseBoolean(parameter);
    } catch (NoSuchElementException e) {
      throw new ArgsException(MISSING_BOOLEAN);
    } catch (NumberFormatException e) {
      throw new ArgsException(INVALID_BOOLEAN, parameter);
    }
  }

  public Object get() {
    return booleanValue;
  }
}</code></pre><ul>
<li>DoubleArgumentMarshaler.java<pre><code class="language-java">import java.util.Iterator;
import java.util.NoSuchElementException;
</code></pre>
</li>
</ul>
<p>import static clean.code.chapter14.refactored.second.ArgsException.ErrorCode.INVALID_DOUBLE;
import static clean.code.chapter14.refactored.second.ArgsException.ErrorCode.MISSING_DOUBLE;</p>
<p>public class DoubleArgumentMarshaler implements ArgumentMarshaler {</p>
<p>  private double doubleValue = 0;</p>
<p>  public void set(Iterator<String> currentArgument) throws ArgsException {
    String parameter = null;
    try {
      parameter = currentArgument.next();
      doubleValue = Double.parseDouble(parameter);
    } catch (NoSuchElementException e) {
      throw new ArgsException(MISSING_DOUBLE);
    } catch (NumberFormatException e) {
      throw new ArgsException(INVALID_DOUBLE, parameter);
    }
  }</p>
<p>  public Object get() {
    return doubleValue;
  }
}</p>
<pre><code>
* IntegerArgumentMarshaler.java
```java
import java.util.Iterator;
import java.util.NoSuchElementException;

import static clean.code.chapter14.refactored.second.ArgsException.ErrorCode.INVALID_INTEGER;
import static clean.code.chapter14.refactored.second.ArgsException.ErrorCode.MISSING_INTEGER;

public class IntegerArgumentMarshaler implements ArgumentMarshaler {
  private int intValue = 0;

  public void set(Iterator&lt;String&gt; currentArgument) throws ArgsException {
    String parameter = null;
    try {
      parameter = currentArgument.next();
      intValue = Integer.parseInt(parameter);
    } catch (NoSuchElementException e) {
      throw new ArgsException(MISSING_INTEGER);
    } catch (NumberFormatException e) {
      throw new ArgsException(INVALID_INTEGER, parameter);
    }
  }

  public Object get() {
    return intValue;
  }
}</code></pre><ul>
<li>StringArgumentMarshaler.java<pre><code class="language-java">import java.util.Iterator;
import java.util.NoSuchElementException;
</code></pre>
</li>
</ul>
<p>import static clean.code.chapter14.refactored.second.ArgsException.ErrorCode.MISSING_STRING;</p>
<p>public class StringArgumentMarshaler implements ArgumentMarshaler {
  private String stringValue = &quot;&quot;;</p>
<p>  public void set(Iterator<String> currentArgument) throws ArgsException {
    try {
      stringValue = currentArgument.next();
    } catch (NoSuchElementException e) {
      throw new ArgsException(MISSING_STRING);
    }
  }</p>
<p>  public Object get() {
    return stringValue;
  }
}</p>
<p>```</p>
<hr>
<h2 id="어떻게-짰느냐고">어떻게 짰느냐고?</h2>
<ul>
<li>1차 초안을 시작으로 계속 고쳐 최종안을 만들어라!</li>
<li><strong>&quot;점진적인 개선&quot;</strong></li>
</ul>
<hr>
<h2 id="그래서-멈췄다">그래서 멈췄다</h2>
<ul>
<li>이대로 진행하다간, 코드가 훨씬 더 나빠질 것이라는 확신이 있다면 멈춰라</li>
<li>유지보수하기 좋은 상태로 만들 적기를 놓치지 마라!</li>
<li>멈췄다면 기능 추가를 잠시 그만하고, <strong>리팩터링</strong>을 시작하기</li>
</ul>
<hr>
<h2 id="점진적으로-개선하다">점진적으로 개선하다</h2>
<ul>
<li>개선이라는 이름 아래 구조를 크게 뒤집는 것은 프로그램을 망치는 행위</li>
<li><strong>테스트 주도 개발 기법</strong>을 적극적으로 활용!</li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<ul>
<li>나쁜 코드는 지금 당장 정리하자. 썩기 전에!</li>
</ul>
<hr>
<h2 id="인상-깊었던">인상 깊었던...</h2>
<blockquote>
<p><em>더욱 중요하게는 여러분이 깨끗하고 우아한 프로그램을 한 방에 뜩딱 내놓으리라 기대하지 않는다.</em></p>
</blockquote>
<blockquote>
<p><em>그저 돌아가는 코드만으로는 부족하다.</em></p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[13장] 동시성]]></title>
            <link>https://velog.io/@dayeon-choi/13%EC%9E%A5-%EB%8F%99%EC%8B%9C%EC%84%B1</link>
            <guid>https://velog.io/@dayeon-choi/13%EC%9E%A5-%EB%8F%99%EC%8B%9C%EC%84%B1</guid>
            <pubDate>Sun, 01 Aug 2021 01:48:31 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
</blockquote>
<p><em>객체는 처리의 추상화다. 스레드는 일정의 추상화다.</em>
동시성과 깔끔한 코드는 양립하기 어렵다. 여러 스레드를 동시에 돌리는 이유와 그 어려움, 해결 방법에 대해 알아보자.</p>
<hr>
<h2 id="동시성이-필요한-이유">동시성이 필요한 이유?</h2>
<p>👉 동시성은 무엇과 언제로 분리하는 전략이다. <strong>애플리케이션의 구조와 효율이 극적으로 나아진다.</strong></p>
<ul>
<li><strong>동시성에 대한 미신</strong><ul>
<li>동시성은 항상 성능을 높여준다 (✕)
→ 여러 스레드가 프로세스를 공유할 수 있거나, 동시에 처리할 독립적인 계산이 많은 경우에만!</li>
<li>동시성을 구현해도 설계는 변하지 않는다 (✕)
→ 일반적으로 무엇과 언제를 분리하면 구조가 크게 달라짐!</li>
<li>웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다 (✕)
→ 동작 방식, 여러 문제 해결 방식에 대한 이해가 필요!<br/></li>
</ul>
</li>
<li><strong>동시성에 대한 타당한 생각</strong><ul>
<li>동시성은 부하를 유발한다</li>
<li>동시성은 복잡하다</li>
<li>일반적으로 동시성 버그는 재현하기 어렵다</li>
<li>동시성을 구현하려면 근본적인 설계 전략을 재고해야 한다</li>
</ul>
</li>
</ul>
<hr>
<h2 id="난관">난관</h2>
<p>👉 동시성을 구현하기 어려운 이유에 대해 알아보자.</p>
<ul>
<li>경우의 수가 많아지며, 일부 경우의 수가 <strong>잘못된 결과</strong>를 내놓기 때문</li>
</ul>
<hr>
<h2 id="동시성-방어-원칙">동시성 방어 원칙</h2>
<p>👉 동시성 코드의 문제로부터 시스템을 방어하는 원칙과 규칙을 알아보자.</p>
<ul>
<li><strong>단일 책임 원칙</strong> (SRP)<ul>
<li>변경할 이유가 하나여야 한다는 원칙</li>
<li>복잡성이라는 이유 하나 → <strong>동시성 관련 코드는 다른 코드와 분리</strong>해야 함</li>
<li>동시성 구현시, 동시성 코드에 대한 고려 사항
1) 개발/변경/조율 주기가 있다는 점
2) 독자적인 난관이 존재한다는 점
3) 동시성 하나만으로도 충분히 어렵다는 점<br/>    </li>
</ul>
</li>
<li><strong>따름 정리: 자료 범위를 제한하라</strong><ul>
<li>공유 객체를 사용하는 코드 내 임계영역을 <code>synchronized</code> 키워드로 보호</li>
<li>자료를 캡슐화하여 <strong>공유 자료를 최대한 줄이기</strong><br/></li>
</ul>
</li>
<li><strong>따름 정리: 사본을 사용하라</strong><ul>
<li>처음부터 공유하지 않는 방법<ul>
<li>객체를 복사해 읽기 전용으로 사용</li>
<li>객체를 복사해 사용한 후 한 스레드가 해당 사본의 결과를 가져옴<br/></li>
</ul>
</li>
</ul>
</li>
<li><strong>따름 정리: 스레드는 가능한 독립적으로 구현하라</strong><ul>
<li>자료를 독립적인 단위로 분할 → 다른 스레드와 동기화 할 필요✕</li>
<li>독자적인 스레드로, 가능하면 다른 프로세서에서!</li>
</ul>
</li>
</ul>
<hr>
<h2 id="라이브러리를-이해하라">라이브러리를 이해하라</h2>
<p>👉 언어, 버전에 따라 스레드 코드 구현시 고려사항을 알아보자.</p>
<ul>
<li>사용할 라이브러리의 규칙과 주의사항, 고려사항을 이해하자.</li>
<li>언어가 제공하는 주요 클래스를 검토하자.</li>
</ul>
<hr>
<h2 id="실행-모델을-이해하라">실행 모델을 이해하라</h2>
<p>👉 다중 스레드 애플리케이션의 분류 방식, 기본 용어를 알아보자. 알고리즘과 각 해법 이해하기!</p>
<ul>
<li><strong>기본 용어</strong><ul>
<li>한정된 자원 : 다중 스레드 환경에서 사용하는 크기나 숫자가 제한적인 자원</li>
<li>상호 배제 : 한 번에 한 스레드만 공유 자료/자원을 사용할 수 있는 경우</li>
<li>기아 : 한 스레드나 여러 스레드가 오랫동안 또는 영원히 자원을 기다리는 경우</li>
<li>데드락 : 여러 스레드가 서로가 끝나기를 기다려 진행이 불가한 경우</li>
<li>라이브락 : 락을 거는 단계에서 각 스레드가 서로를 방해해 진행이 불가한 경우<br/></li>
</ul>
</li>
<li><strong>실행 모델</strong><ul>
<li>생산자-소비자</li>
<li>읽기-쓰기</li>
<li>식사하는 철학자들</li>
</ul>
</li>
</ul>
<hr>
<h2 id="동기화하는-메서드-사이에-존재하는-의존성을-이해하라">동기화하는 메서드 사이에 존재하는 의존성을 이해하라</h2>
<p>👉 공유 클래스 하나에 동기화된 메서드가 여럿이라면 구현이 올바른지 확인하라. 공유 객체 하나에는 메서드 하나만 사용하라.</p>
<ul>
<li>공유 객체 하나에 여러 메서드가 필요한 상황, 고려할 방법 3가지<ul>
<li>클라이언트에서 잠금 : 첫 메서드 호출 전 -🔒- 마지막 메서드 호출</li>
<li>서버에서 잠금 : 서버를 잠금 - 모든 메서드 호출 - 잠금 해제</li>
<li>연결 서버 : 잠금을 수행하는 중간 단계 생성. 원래 서버 변경 ✕</li>
</ul>
</li>
</ul>
<hr>
<h2 id="동기화하는-부분을-작게-만들어라">동기화하는 부분을 작게 만들어라</h2>
<p>👉 동기화 하는 부분은 최대한 작게!!</p>
<ul>
<li>임계영역 수를 최대한 줄이자!</li>
<li>필요 이상 임계영역 크기를 키우는 것은 금물!</li>
</ul>
<hr>
<h2 id="올바른-종료-코드는-구현하기-어렵다">올바른 종료 코드는 구현하기 어렵다</h2>
<p>👉 영구적인 실행 프로그램과 종료되는 프로그램을 구현하는 방법은 다르다. 종료 프로그램이 더 어려움!!</p>
<ul>
<li>종료 코드를 올바르게 구현하기는 어렵다. 시간을 더 투자할 것!</li>
<li>개발 초기부터 고민하고 구현하기 </li>
<li>이미 나온 알고리즘을 검토하기</li>
</ul>
<hr>
<h2 id="스레드-코드-테스트하기">스레드 코드 테스트하기</h2>
<p>👉 문제(시스템 설정, 부하, 플랫폼, 많은 환경)를 노출하는 테스트 케이스 작성해보자. 다시 돌렸더니 통과한다는 이유로 넘어가서는 안된다!</p>
<ul>
<li><strong>말이 안되는 실패는 잠정적인 스레드 문제로 취급</strong><ul>
<li>시스템 실패를 일회성이라 치부하지 않기<br/></li>
</ul>
</li>
<li><strong>다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들기</strong><ul>
<li>먼저 스레드 환경 밖에서 코드를 올바로 돌리기<br/></li>
</ul>
</li>
<li><strong>다중 스레드 코드 부분을 다른 환경에 쉽게 끼워넣을 수 있도록 구현하기</strong><br/></li>
<li><strong>다중 스레드 코드 부분을 상황에 맞게 조율할 수 있도록 작성하기</strong><br/></li>
<li><strong>프로세서 수 보다 많은 스레드 돌려보기</strong><ul>
<li>처음부터, 자주 모든 목표 플랫폼에서 코드 돌리기<br/></li>
</ul>
</li>
<li><strong>코드에 보조 코드를 넣어 돌려 강제로 실패를 일으키게 해보기</strong><ul>
<li>직접 구현하거나 자동화하는 두가지 방법이 존재</li>
<li>흔들기 기법을 사용해 오류 찾아내기</li>
</ul>
</li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<ul>
<li>SRP 준수, POJO를 사용해 분리(스레드를 아는 코드, 모르는 코드)</li>
<li>동시성 오류를 일으키는 잠정적 원인 철저히 이해</li>
<li>사용하는 라이브러리와 기본 알고리즘 이해</li>
<li>보호할 코드 영역을 찾는/잠그는 방법 이해</li>
<li>TDD 3대 규칙을 따라 테스트 용이성 지키기 </li>
</ul>
<hr>
<h2 id="인상-깊었던">인상 깊었던...</h2>
<blockquote>
<p><em>깨끗한 동시성은 책 하나를 할당할 정도로 복잡한 주제다.</em></p>
</blockquote>
<blockquote>
<p><em>스레드 코드를 출시하기 전까지 최대한 오랫동안 돌려봐야 한다.</em></p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[12장] 창발성]]></title>
            <link>https://velog.io/@dayeon-choi/12%EC%9E%A5-%EC%B0%BD%EB%B0%9C%EC%84%B1</link>
            <guid>https://velog.io/@dayeon-choi/12%EC%9E%A5-%EC%B0%BD%EB%B0%9C%EC%84%B1</guid>
            <pubDate>Mon, 26 Jul 2021 01:44:58 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>창발성 : 떠오름 현상. 하위 계층(구성 요소)에는 없는 특성이나 행동이 상위 계층(전체 구조)에서 자발적으로 돌연히 출현하는 현상이다. 또한, 불시에 솟아나는 특성을 창발성이라고 한다.</p>
</blockquote>
<hr>
<h2 id="창발적-설계로-깔끔한-코드를-구현하자">창발적 설계로 깔끔한 코드를 구현하자</h2>
<p>👉 우수한(단순한) 설계가 나오는 간단한 규칙 네 가지를 중요도 순으로 알아보자!</p>
<ul>
<li>모든 테스트를 실행한다.</li>
<li>중복을 없앤다.</li>
<li>프로그래머 의도를 표현한다.</li>
<li>클래스와 메서드 수를 최소로 줄인다.</li>
</ul>
<hr>
<h2 id="단순한-설계-규칙-1-모든-테스트를-실행하라">단순한 설계 규칙 1: 모든 테스트를 실행하라</h2>
<p>👉 테스트가 불가능한 시스템은 검증도 불가능하다.</p>
<ul>
<li>검증이 불가능한 시스템은 절대 출시하면 안된다.</li>
<li>철저한 테스트가 가능한 시스템을 만들면 더 나은 설계가 얻어진다.</li>
<li>더 나은 설계 → 낮은 결합도와 높은 응집력 <span style="color:gray">객체 지향 방법론이 지향하는 목표</span></li>
</ul>
<hr>
<h2 id="단순한-설계-규칙-24-리팩터링">단순한 설계 규칙 2~4: 리팩터링</h2>
<p>👉 테스트 케이스와 함께 코드를 점진적으로 리팩터링 해나간다. 리팩터링 단계에 대해!</p>
<ul>
<li>코드 추가 후 테스트 케이스를 통해 확인한다.</li>
<li>리팩터링 단계에서는 소프트웨어 설계 품질을 높이는 기법이라면 무엇이든 적용 OK</li>
<li>단순한 설계 규칙 중 나머지 3개를 적용한다.</li>
</ul>
<hr>
<h2 id="중복을-없앤다">중복을 없앤다.</h2>
<p>👉 우수한 설계에서 중복은 커다란 적이다.</p>
<ul>
<li>비슷한 코드는 더 비슷하게 고쳐주면 리팩터링이 쉬워진다.</li>
<li><code>TEMPLATE METHOD</code> 패턴을 적용해 중복을 제거하자. <a href="https://jusungpark.tistory.com/24">https://jusungpark.tistory.com/24</a></li>
</ul>
<hr>
<h2 id="표현하라">표현하라</h2>
<p>👉 장기적인 유지보수를 위해 다른 사람이 이해할 수 있는 코드를 작성하자. 의도를 분명히 표현하자!</p>
<ul>
<li>기능과 매칭되는 좋은 이름을 선택한다.</li>
<li>함수와 크기를 가능한 줄인다. 작을 수록 모두 쉬워진다!</li>
<li>표준 명칭을 사용한다. <span style='color:gray'>EX) 클래스에 표준 패턴(<code>COMMAND</code>나 <code>VISITOR</code>)을 사용시 패턴 이름 넣기</span></li>
<li>단위 테스트 케이스 꼼꼼히 작성하자. <span style='color:gray'>테스트 테이스는 예제로 보여주는 문서!</span></li>
</ul>
<hr>
<h2 id="클래스와-메서드-수를-최소로-줄여라">클래스와 메서드 수를 최소로 줄여라</h2>
<p>👉 무조건 작게? 많은 것은 금물, 가능하면 수도 줄여라!</p>
<ul>
<li>무의미하고 독단적인 정책으로 클래스와 메서드 수가 늘어나는 것을 주의하자.</li>
<li>함수와 클래스 크기를 작게 유지하면서 시스템 크기도 작게 유지하는 것이 목표이다!</li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<p>👉 단순한 설계 규칙은 오랜 경험을 통해 나왔다. 이 규칙을 따른다면 우수한 기법과 원칙을 단번에 활용할 수 있다!</p>
<hr>
<h2 id="인상-깊었던">인상 깊었던...</h2>
<blockquote>
<p><em>자신의 작품에 조금만 더 주의를 기울이자. 주의는 대단한 재능이다.</em></p>
</blockquote>
<blockquote>
<p><em>가능한 독단적인 개념은 멀리하고 실용적인 방법을 택한다.</em></p>
</blockquote>
<blockquote>
<p><em>경험을 대신할 단순한 개발 기법이 있을까? 당연히 없다.</em></p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[11장] 시스템]]></title>
            <link>https://velog.io/@dayeon-choi/11%EC%9E%A5-%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
            <guid>https://velog.io/@dayeon-choi/11%EC%9E%A5-%EC%8B%9C%EC%8A%A4%ED%85%9C</guid>
            <pubDate>Sun, 25 Jul 2021 14:13:42 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>복잡성은 죽음이다.</p>
</blockquote>
<hr>
<h2 id="도시를-세운다면">도시를 세운다면</h2>
<p>👉 소프트웨어 팀도 도시처럼! 시스템 수준에서도 깨끗함을 유지하는 방법을 알아보자.</p>
<ul>
<li>도시가 돌아가는 또 다른 이유는 <strong>추상화</strong>와 <strong>모듈화</strong> 때문이다.</li>
<li>큰 그림을 이해하지 못할지라도 개인과 개인이 관리하는 구성요소는 효율적으로 돌아간다.</li>
</ul>
<hr>
<h2 id="시스템-제작과-시스템-사용을-분리하라">시스템 제작과 시스템 사용을 분리하라</h2>
<p>👉 소프트웨어 시스템은 애플리케니션 객체를 제작하고 의존성을 서로 &#39;연결&#39;하는 <strong>준비과정과 이후 런타임 로직을 분리</strong>해야 한다. 분리 방법에 대해 알아보자.</p>
<ul>
<li>제작은 사용과 다르다.</li>
<li><strong>관심사 분리</strong>를 진행하라!</li>
</ul>
<br/>

<ul>
<li><h4 id="main-분리">Main 분리</h4>
<ul>
<li><img src="https://images.velog.io/images/dayeon-choi/post/467076ab-2abf-47ae-b0a2-73616245ef4d/image.png" width="50%" /></li>
<li>생성과 관련한 코드는 모두 main이나 main이 호출하는 모듈로 옮기고 모든 의존성이 연결된 상태이다.</li>
<li>애플리케이션은 main이나 객체가 생성되는 과정을 모른다.
<span style='color:gray'>모든 화살표가 main → 애플리케이션</span></li>
</ul>
</li>
<li><h4 id="팩토리">팩토리</h4>
<ul>
<li><img src="https://images.velog.io/images/dayeon-choi/post/972ea36a-4a8b-4794-98e4-ad2704d37954/image.png" width="50%" /></li>
<li>때론 객체가 생성되는 시점을 애플리케이션이 결정할 필요도 있다.</li>
<li>OrderProcessing 애플리케이션은 LineItem이 생성되는 구체적인 방법을 모른다.
<span style='color:gray'>main → OrderProcessing</span></li>
</ul>
</li>
<li><h4 id="의존성-주입">의존성 주입</h4>
<ul>
<li><strong>제어 역전 기법</strong>(IoC:Inversion of Control)을 의존성 관리에 적용한 메커니즘이다.</li>
<li>참고하기 <a href="https://pks424.tistory.com/entry/IoC-DI%EB%9E%80">https://pks424.tistory.com/entry/IoC-DI%EB%9E%80</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="확장">확장</h2>
<p>👉 내일은 새로운 스토리에 맞춰 시스템을 조정하고 확장하면 된다.</p>
<ul>
<li><strong>애자일 방식</strong>의 핵심, 반복적 + 점진적 <ul>
<li>참고하기 <a href="https://www.redhat.com/ko/devops/what-is-agile-methodology">https://www.redhat.com/ko/devops/what-is-agile-methodology</a></li>
<li>시스템 조정과 확장을 용이하게 한다.   </li>
</ul>
</li>
<li><strong>횡단(cross-cutting) 관심사</strong><ul>
<li><code>EJB2</code> 아키텍쳐는 일부 영역에서 관심사를 거의 완벽하게 분리해냈다. 이는 AOP (관점 지향 프로그래밍)을 예견했다.</li>
<li>영속성 프레임워크와 도메인 논리를 독자적을 모듈화할 수 있는데, 이 두 영역이 세밀하게 겹치는 부분이 생긴다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="자바-프록시">자바 프록시</h2>
<p>👉 프록시 사용, 주의하자!!</p>
<ul>
<li><strong>단순한 상황</strong>에 적합하다. <span style='color:gray'>EX) 개별 객체 / 클래스에서 메서드 호출을 감싸는 경우</span></li>
<li>프록시를 사용하면 깨끗한 코드를 작성하기 어렵다.
<span style='color:gray'>(시스템 단위로 실행 지점을 명시하는 메커니즘 제공 안함)</span></li>
</ul>
<hr>
<h2 id="순수-자바-aop-프레임워크">순수 자바 AOP 프레임워크</h2>
<p>👉 프록시 코드는 대부분 도구로 자동화가 가능하다. 스프링 AOP, JBoss AOP 등은 내부적으로 프록시를 사용한다.</p>
<ul>
<li><img src="https://images.velog.io/images/dayeon-choi/post/2ace2d0b-01b8-4e12-856f-0349cc993f60/image.png" width="60%" /></li>
<li>위는 스프링 V2.5 설정 파일 app.html 일부</li>
<li>애너테이션에 들어있는 영속성 정보는 필요하다면 XML 배치 기술자로 옮겨도 괜찮다. <span style='color:gray'>→ 순수한 POJO만 남음</span></li>
<li>비교했을 땐<code>EJB3</code> 사용 추천</li>
</ul>
<hr>
<h2 id="aspectj-관점">AspectJ 관점</h2>
<p>👉 관심사를 관점으로 분리하는 가장 강력한 도구, AspectJ 언어</p>
<ul>
<li>AspectJ 란) 언어 차원에서 관점을 모듈화 구성으로 지원하는 자바 언어 확장</li>
<li>장점) 관점을 분리하는 강력하고 풍부한 도구 집합 제공</li>
<li>단점) 새 언어 문법과 사용법을 익혀야 함</li>
<li>→ <strong>애너테이션 폼</strong> 추천</li>
</ul>
<hr>
<h2 id="테스트-주도-시스템-아키텍처-구축">테스트 주도 시스템 아키텍처 구축</h2>
<p>👉 코드 수준에서 아키텍쳐 관심사를 분리할 수 있다면, 진정한 테스트 주도 아키텍처 구축이 가능해진다.</p>
<ul>
<li>그때 그때 새로운 기술을 채택해 키워나가는 것이 가능 <span style='color:gray'>단순한 아키텍처 → 복잡한 아키텍처</span></li>
<li>BDUF(Big Design Up Font) 추구할 필요 ✕ 해로움!</li>
<li>단, 일반적인 범위, 목표, 일정, 결과를 내놓을 시스템의 <strong>일반적인 구조를 생각하는 것</strong>은 필수! <span style='color:gray'>방향성을 찾지 못하는 것은 ✕</span></li>
</ul>
<hr>
<h2 id="의사-결정을-최적화하라">의사 결정을 최적화하라</h2>
<p>👉 책임은 가장 적합한 사람에게, 최대한 정보를 모아 최선의 결정으로!</p>
<ul>
<li>고객 피드백을 더 모으고, 프로젝트를 더 고민하고, 구현 방안을 더 탐험하라!!</li>
</ul>
<hr>
<h2 id="명백한-가치가-있을-때-표준을-현명하게-사용하라">명백한 가치가 있을 때 표준을 현명하게 사용하라</h2>
<p>👉 표준이라는 이유만으로 이에 집착하는 것은 바람직하지 ✕</p>
<ul>
<li>표준의 장점) 아이디어/컴포넌트 재사용 용이, 경험 충분, 캡슐화 용이, 컴포넌트 엮기에 용이</li>
<li>표준의 단점) 표준 제작 시간 딜레이, 제정 목적을 잃은 표준의 존재</li>
</ul>
<hr>
<h2 id="시스템은-도메인-특화-언어가-필요하다">시스템은 도메인 특화 언어가 필요하다</h2>
<p>👉 최근들어 조명받기 시작한 <strong>DSL</strong>(Domain-Specific Language)을 사용하라! 장점은?</p>
<ul>
<li>도메인 전문가가 작성한 구조적인 산문처럼 읽힌다.</li>
<li>도메인을 잘못 구현할 가능성이 줄어든다.</li>
<li>추상화 수준을 코드 관용구,디자인 패턴 이상으로 끌어 올린다.</li>
<li>→ <strong>적절한 추상화 수준에서 코드 의도 표현 가능</strong></li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<p>👉 시스템 역시 깨끗해야 한다.</p>
<ul>
<li>생산성과 TDD의 장점과 연결되는 <strong>기민성</strong>을 떨어지지 않게 하라!</li>
<li>모든 추상화 단계에서 의도는 명확하게!</li>
<li>관점과 유사한 메커니즘으로 구현 관심사를 분리하라!</li>
<li><strong>실제로 돌아가는 가장 단순한 수단을 사용하라!</strong></li>
</ul>
<hr>
<h2 id="인상-깊었던">인상 깊었던...</h2>
<blockquote>
<p><em>복잡성은 죽음이다. 개발자에게서 생기를 앗아가며, 제품을 계획하고 제작하고 테스트하기 어렵게 만든다.</em></p>
</blockquote>
<blockquote>
<p><em>나는 업계에서 여러 형태로 아주 과장되게 포장된 표준에 집착하는 바람에 고객 가치가 뒷전으로 밀려난 사례를 많이 봤다.</em></p>
</blockquote>
<blockquote>
<p><em>깨끗하지 못한 아키텍처는 도메인 논리를 흐리며 기민성을 떨어뜨린다.</em></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[10장] 클래스 ]]></title>
            <link>https://velog.io/@dayeon-choi/10%EC%9E%A5-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@dayeon-choi/10%EC%9E%A5-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Sun, 25 Jul 2021 10:17:40 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>코드의 표현력과 그 코드로 이루어진 함수에 아무리 신경 쓸지라도 좀 더 차원 높은 단계까지 신경 쓰지 않으면 깨끗한 코드를 얻기는 어렵다.</p>
</blockquote>
<hr>
<h2 id="클래스-체계">클래스 체계</h2>
<p>👉 프로그램은 신문 기사처럼 읽히도록, 추상화 단계가 순차적으로 내려가도록 작성한다.</p>
<ul>
<li><strong>캡슐화</strong><ul>
<li>변수와 유틸리티 함수를 반드시 숨겨야하는 것은 ✕</li>
<li>하지만 비공개 상태를 유지할 방법을 강구</li>
<li>최후는 <code>protected</code>로 선언해 테스트 코드에 접근</li>
</ul>
</li>
</ul>
<hr>
<h2 id="클래스는-작아야-한다">클래스는 작아야 한다!</h2>
<p>👉 클래스를 만들 때 기본 규칙은 <strong>작은 크기</strong>! 얼마나 작아야 하는지 알아보자.</p>
<ul>
<li>클래스 이름은 해당 <strong>클래스 책임을 기술</strong>해야 한다. <span style='color:gray'>작명은 클래스 크기를 줄이는 첫 번째 관문!</span><br/></li>
<li><h4 id="단일-책임-원칙-srp">단일 책임 원칙 (SRP)</h4>
<ul>
<li>🔎 클래스나 모듈을 <strong>변경할 이유</strong>와 <strong>맡은 책임</strong>이 단 <strong>하나</strong>뿐이여야 한다는 원칙
<span style='color:gray'>변경할 이유를 파악하다 보면 코드를 추상화하기 쉬워진다.</span></li>
<li>작은 클래스는 다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행하며 이것이 바람직하다.</li>
</ul>
</li>
<li><h4 id="응집도-cohesion">응집도 (Cohesion)</h4>
<ul>
<li>변수 사용량↑ = 응집도↑ 
= 클래스에 속한 메서드와 변수의 의존성↑, 논리적인 단위로 묶임</li>
<li><strong>함수를 작게, 매개변수 목록을 짧게!</strong></li>
<li>몇몇 메서드만이 사용하는 인스턴트 변수가 많아지게 되면 새로운 클래스로 쪼개라.  </li>
</ul>
</li>
<li><h4 id="응집도를-유지하면-작은-클래스-여럿이-나온다">응집도를 유지하면 작은 클래스 여럿이 나온다.</h4>
<ul>
<li>함수로 쪼개면 응집력을 잃는다. 그렇다면 작은 클래스로 쪼개라!</li>
</ul>
</li>
<li><h4 id="저자가-추천하는-리팩터링-방법">저자가 추천하는 리팩터링 방법</h4>
<ul>
<li>1) 원래 프로그램의 정확한 동작을 검증하는 테스트 슈트를 작성</li>
<li>2-1) 한 번에 하나씩 수 차례에 걸쳐 조금씩 코드를 변경</li>
<li>2-2) 코드를 변경할 때마다 테스트 수행</li>
<li>3) 최종 프로그램!!!!</li>
</ul>
</li>
</ul>
<hr>
<h2 id="변경하기-쉬운-클래스">변경하기 쉬운 클래스</h2>
<p>👉 깨끗한 시스템은 클래스를 체계적으로 정리해 변경에 수반하는 위험을 낮춘다. 체계적으로 정리 = <strong>서로 분리된 클래스</strong></p>
<ul>
<li>개선에 뛰어드는 계기는 <strong>시스템이 변해서</strong>여야 한다.</li>
<li>구조적인 관점에서 <strong>SRP(단일 책임 원칙)과 OCP(개방 폐쇄 원칙)을 위반하는지</strong> 체크하여 개선한다.</li>
<li><strong>변경으로부터 격리</strong>한다.<ul>
<li>변경은 필연적인 것. 시스템의 <strong>결합도</strong>를 낮춰 유연성과 재사용성을 높이자!</li>
<li>결합도↓ = 각 시스템 요소가 서로 잘 격리된 것</li>
<li>결합도를 최소화하면 <strong>DIP</strong>를 따르는 클래스가 나옴</li>
</ul>
</li>
</ul>
<p>🔎 <strong>DIP</strong>(Dependency Inversion Principle) : 의존관계 역전 원칙. 소프트웨어 모듈을 분리하는 특정 형식.</p>
<hr>
<h2 id="인상-깊었던">인상 깊었던..</h2>
<blockquote>
<p><em>우리들 대다수는 두뇌 용량에 한계가 있어 &#39;깨끗하고 체계적인 소프트웨어&#39;보다 &#39;돌아가는 소프트웨어&#39;에 초점을 맞춘다.</em></p>
</blockquote>
<blockquote>
<p><em>문제는 우리들 대다수가 프로그램이 돌아가면 일이 끝났다고 여기는 데 있다.</em></p>
</blockquote>
<blockquote>
<p><em>규모가 어느 수준에 이르는 시스템은 논리가 많고도 복잡하다. 이런 복잡성을 다루려면 체계적인 정리가 필수다. 그래야 개발자가 무엇이 어디에 있는지 쉽게 찾는다.</em></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[9장] 단위 테스트]]></title>
            <link>https://velog.io/@dayeon-choi/9%EC%9E%A5-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@dayeon-choi/9%EC%9E%A5-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Sun, 25 Jul 2021 08:37:54 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>하지만 우리 분야에 테스트를 추가하려고 급하게 서두르는 와중에 많은 프로그래머들이 제대로 된 테스트 케이스를 작성해야 한다는 좀 더 미묘한 (그리고 더욱 중요한) 사실을 놓쳐버렸다.</p>
</blockquote>
<hr>
<h2 id="tdd-법칙-세-가지">TDD 법칙 세 가지</h2>
<p>👉 실제 코드를 짜기 전, 단위 테스트 부터 짜라고 요구하는 사실! 하지만 이것은 빙산의 일각에 불과하다.</p>
<ul>
<li>세 가지의 TDD 법칙
<span style='color:gray'>이 규칙을 따르면 30초 주기로 개발과 테스트가 묶임</span><ul>
<li><strong>첫째 법칙 :</strong> 실패하는 단위 테스트를 작헝할 때까지 실제 코드 작성 ✕</li>
<li><strong>둘째 법칙 :</strong> 컴파일은 실패하지 않으면서 실행이 실패하는 정도만 단위 테스트 작성</li>
<li><strong>셋째 법칙 :</strong> 현재 실패하는 테스트를 통과할 정도로만 실제 코드 작성</li>
</ul>
</li>
<li>방대한 테스트 코드는 심각한 관리 문제를 유발!!</li>
</ul>
<hr>
<h2 id="깨끗한-코드-유지하기">깨끗한 코드 유지하기</h2>
<p>👉 테스트를 안 하는 것보다 <strong>지저분한 테스트 코드</strong>를 내 놓는 것이 더 나쁘다! <strong>테스트 코드는 실제 코드 못지 않게 중요</strong>하다.</p>
<ul>
<li><strong>테스트는 유연성, 유지보수성 재사용성을 제공한다.</strong>    <ul>
<li>이를 제공하는 버팀목은 <strong>단위 테스트</strong></li>
<li>지저분한 테스트 코드는 변경/개선 능력↓</li>
<li>지저분한 테스트 코드 → 지저분한 실제 코드</li>
</ul>
</li>
</ul>
<hr>
<h2 id="깨끗한-테스트-코드">깨끗한 테스트 코드</h2>
<p>👉 포인트는 <strong>가독성</strong></p>
<ul>
<li><p>테스트 코드에서 가독성은 더더욱 중요!</p>
</li>
<li><p>명료성, 단순성, 풍부한 표현력, 잡음 제거</p>
<br/></li>
<li><p><strong>도메인에 특화된 테스트 언어 (DSL)</strong></p>
<ul>
<li>🔎 특정 영역의 문제 해결에는 그 영역에 맞는 특화된 도구를 사용하자는 취지</li>
<li>API 위에 함수와 유틸리티를 구현한 후 이를 사용 → 테스트 코드 작성 용이</li>
<li>API를 끊임없이 리팩터링하는 것이 필요</li>
</ul>
</li>
<li><p><strong>이중 표준</strong> </p>
<ul>
<li>🔎 실제 환경에서는 절대로 안 되지만 테스트 환경에서는 전혀 문제가 없다는, 말 그대로 &#39;이중 표준&#39;</li>
<li>테스트 코드는 깨끗해야 하지만 실제 코드만큼 효율적일 필요 ✕</li>
</ul>
</li>
</ul>
<hr>
<h2 id="테스트-당-assert-하나">테스트 당 assert 하나</h2>
<p>👉 함수마다 assert 문은 <strong>단 하나만</strong> 사용하자</p>
<ul>
<li>병합하기 불합리하다면 쪼개어 각자가 assert 문을 수행하도록 하자.</li>
<li>테스트를 분리하면 중복되는 코드가 많아지는데, 이는 <strong>TEMPLATE METHOD 패턴</strong>으로 해결하자.<ul>
<li><image src="https://images.velog.io/images/dayeon-choi/post/803ad518-cf90-47a4-9270-d254925a6979/image.png" width='50%'/></li>
<li>참고 : <a href="https://kimseunghyun-bg.github.io/programming/oop/template_method_pattern/">https://kimseunghyun-bg.github.io/programming/oop/template_method_pattern/</a></li>
<li>배보다 배꼽이 더 큰 코드가 되기도 하지만, 이득이 훨씬 더 크다!<br/></li>
</ul>
</li>
<li><strong>테스트 당 개념 하나</strong><ul>
<li><strong>테스트 함수마다 한 개념</strong>만 테스트</li>
<li>사실, assert 문이 하나씩만 있는 것보다 <strong>더 신경써야하는 부분</strong>이다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="first">F.I.R.S.T</h2>
<p>👉 깨끗한 테스트의 5가지 규칙</p>
<ul>
<li><strong>F</strong>ast : 테스트는 자주 돌릴 수 있도록 <strong>빨라</strong>야 한다.</li>
<li><strong>I</strong>ndependent : 각 테스트는 어떤 순서로 테스트 해도 상관 없도록 <strong>독립적</strong>이여야 한다.</li>
<li><strong>R</strong>epeatable : 테스트는 변명할 수 없도록 어떤 환경에서도 <strong>반복이 가능</strong>해야 한다.</li>
<li><strong>S</strong>elf-Validating : 테스트는 부울 값으로 결과를 내어 <strong>성공/실패</strong>로 나타내야 한다.</li>
<li><strong>T</strong>imely : 테스트는 <strong>적시에</strong> 작성하여 테스트를 염두해두며 코드를 설계할 수 있도록 한다.</li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<p>👉 깨끗한 테스트 코드는 이 내용이 전부가 아니다. 그만큼 중요하며 많은 공부가 필요하다!!</p>
<ul>
<li>어쩌면 테스트 코드는 실제 코드보다 중요하다.</li>
<li>지속적으로 테스트 코드를 깨끗하게 관리하자.</li>
</ul>
<hr>
<h2 id="인상-깊었던">인상 깊었던...</h2>
<blockquote>
<p><em>&#39;지저분해도 빨리&#39;가 주제였다. 변수 이름은 싱경 쓸 필요가 없었고, 테스트 함수는 간결하거나 서술적일 필요가 없었고, 테스트 코드는 잘 설계하거나 주의해서 분리할 필요가 없었다.</em></p>
</blockquote>
<blockquote>
<p><em>테스트 코드가 방치되어 망가지면 실제 코드도 망가진다.</em></p>
</blockquote>
<hr>
]]></description>
        </item>
    </channel>
</rss>