## 7.1 불필요한 데이터 전송

- 복수의 클라이언트가 자주 쓰이는 페이지에 접근시, 같은 바이트들이 네트워크를 통해 계속 반복해서 이동
- 캐시를 이용하여, 캐시된 사본이 뒤이은 요청에 대한 응답으로 사용하여 트래픽을 주고 받는 낭비를 줄임

## 7.2 대역폭 병목

- 캐시는 네트워크 병목 줄여줌. 빠른 LAN에 있는 캐시로부터 사본을 가져온다면, 캐싱은 성능 대폭 개선 가능.
    - 많은 네트워크가 원격 서버보다 로컬 네트워크 클라이언트에 더 넓은 대역폭 제공.
    - 클라이언트들이 서버에 접근할 때의 속도는 그 경로에 있는 가장 느린 네트워크의 속도.

## 7.3 갑작스런 요청 쇄도(Flash Crowds)

- 많은 사람이 동시에 웹 문서 접근시 발생하며, 캐시를 통해 갑작스런 쇄도 요청에 대처 가능.
    - 이로 초래된 불필요한 트래픽 급증은 네트워크와 웹 서버에 심각한 장애 야기.

## 7.4 거리로 인한 지연

- 거리로 인한 지연 요소
    - 모든 네트워크 라우터는 제각각 인터넷 트래픽을 지연시킴
    - 라우터가 많지 않더라도, 빛의 속도 그자체로 유의미한 지연 발생
- 복잡한 페이지는 빛의 속도로 인한 지연이 수 초에 달함. 캐시를 통해 전송 거리를 수천 킬로 미터에서 수십 미터로 줄일 수 있음

## 7.5 적중과 부적중

- 캐시는 유용하지만, 세상 모든 문서의 사본을 저장하지 않음.
- 캐시에 요청 도착시,
    - 캐시 적중(cache hit): 사본이 있음. → 이를 이용해 요청 처리
    - 캐시 부적중(cache miss): 사본이 없음. → 원 서버로 전달

### 7.5.1 재검사(Revalidation)

- HTTP 재검사: 원 컨텐츠가 변경될 수 있으므로, 캐시는 반드시 갖고 있는 사본이 최신인지 점검이 필요.
- 캐시는 대부분 사본을 요청받고, 그 것이 검사가 필요할 정도로 오래된 경우에만 재검사.
    - 언제나 재검사가 가능하지만, 보유 문서양 대비 네트워크 대역폭이 부족하기 때문
- 캐시는 재검사 필요시, 원 서버에게 재검사 요청을 보냄.
- HTTP는 재검사 요청에 사용되는 도구 몇 가지를 제공하는데, 그 중 `If-Modified-Since`  제공
    - 서버에 보내는 `GET` 요청에 위 헤더를 추가시, 캐시된 시간 이후에 변경된 경우에만 사본을 보내달라는 의미

***재검사 적중***

- 원 서버에 재검사 요청을 보냈을 때 콘텐츠가 미변경된 경우.
    - 서버가 `304 Not Modified` 응답 전송 → 캐시는 사본 제공
- 순수 캐시 적중보다 느림: 원서버 검사가 필요하기 때문
- 캐시 부적중보다 빠름: 서버로부터 객체 데이터를 받아올 필요 없음

***재검사 부적중***

- 원 서버에 재검사 요청을 보냈을 때 콘텐츠가 변경된 경우.
    - 서버는 콘텐츠와 함께 `200 OK` 응답 전송

***객체 삭제***

- 원 서버에 재검사 요청을 보냈을 때 객체가 삭제된 경우.
    - 서버는 `404 Not Found` 응답 전송 → 캐시는 사본 삭제

### 7.5.2 적중률

- **캐시 적중률**(=캐시 적중비), **문서 적중률**(=문서 적중비): 캐시가 요청을 처리하는 비율
    - 적중률은 0~1, 혹은 퍼센트로 표현
    - 0%: 모든 요청이 캐시 부적중, 100%: 모든 요청이 캐시 적중
- 캐시 관리자는 적중율이 100%에 가깝게 되길 원하지만, 실제로는 40%정도라면 웹 캐시로서 괜찮은 편임.
    - 실제 적중률은, 캐시 크기, 캐시 사용자의 관심사의 비슷한 정도, 캐시된 데이터가 얼마나 자주 변경되거나 개인화 되는지, 캐시가 어떻게 설정되어있는 지에 따라 다름.

### 7.5.3 바이트 적중율

- 문서들이 다 같은 크기가 아니므로, 문서 적중률이 모든 것을 말해주지 않음.
    - 몇 큰 객체들은 덜 접근 되지만, 그 크기때문에 전체 트래픽에는 더 크게 기여함
- 이런 이유로 몇 사람들(특히 트래픽 바이트에 요금을 매기는 사람)은 바이트 단위 적중률 측정값을 선호
- **바이트 적중률**: 캐시를 통해 제공된 모든 바이트의 비율
    - 트래픽이 절감된 정도를 포착. 100%라면 모든 바이트가 캐시에서 와서, 어떤 트래픽도 인터넷으로 나가지 않았음을 의미.
- 문서 적중률과 바이트 단위 적중률 모두 캐시 성능에 대한 유용한 지표
    - 문서 적중률: 얼마나 많은 웹 트랜잭션을 외부로 보내지 않았는지 → 대기시간(지연) 최소화
    - 바이트 단위 적중률: 얼마나 많은 바이트가 인터넷으로 나가지 않았는지 → 대역폭 절약 최적화

### 7.5.4 적중과 부적중의 구별

- HTTP는 클라이언트에게 응답이 캐시 적중/원서버 접근인지 전달 불가능. 모두 `200 OK` 로 전달.
- 클라이언트가 응답이 캐시에서 왔는지는 알아낼 수 있는 방법
    1. `Date` 헤더를 통해 알 수 있음.
        - 응답의 값과 현재 시각을 비교하여 응답 생성일이 더 오래되면 응답이 캐시된 것
    2. `Age` 헤더를 통해 알 수 있음.
    3. 어떤 상용 프락시 캐시는 `Via` 헤더를 통해 캐시에 일어난 일 설명

## 7.6 캐시 토폴로지

- 개인 전용캐시(private cache): 한 명에게만 할당된 캐시로. 그 사용자가 자주 찾는 페이지를 담음
- 공용 캐시(public cache): 수천 명의 사용자에게 공유된 캐시로, 사용자 집단에 자주 쓰이는 페이지를 담음

### 7.6.1 개인 전용 캐시

- 많은 에너지/저장공간 불필요 → 작고 저렴함
- 웹 브라우저는 개인 전용 캐시를 내장.
    - 대부분의 브라우저는 자주 쓰이는 문서를 개인용 컴퓨터의 디스크와 메모리에 캐시.
    - 사용자가 캐시 사이즈/설정 수정 및 캐시 내부 확인 가능.

### 7.6.2 공용 프락시 캐시

- 캐시 프락시 서버(=프락시 캐시)라고 불리는 특별한 종류의 공유된 프락시 서버
- 로컬 캐시에서 문서를 제공하거나 혹은 사용자 입장에서 서버에 접근 → 불필요한 트래픽을 줄일 기회가 많음
    - 자주 찾는 객체를 단 한번만 가져와 모든 요청에 대해 공유된 사본을 제공함으로써 트래픽 줄임
- 프락시 캐시는 프락시를 위한 규칙(6장 참조)을 따름
- 브라우저는 수동 프락시를 지정 혹은 프락시 자동 설정파일을 설정해 프락시 캐시를 사용할 수 있음.
- 인터셉트 프락시를 사용함으로써 브라우저 설정없이 HTTP 요청이 캐시를 통하도록 강제할 수 있음.

### 7.6.3 프락시 캐시 계층들

- 캐시 계층을 만드는 방식이 합리적인 경우가 많음
    - 작은 캐시에서 캐시 부적중 발생시, 더 큰 부모 캐시가 그 걸러진 트래픽을 처리함
- 클라이언트 주위에는 자고 저렴한 캐시 사용. 계층 상단에는 많은 사용자들에 의해 공유되는 문서를 유지하기위해 더 크고 강력한 캐시 사용
- 캐시 계층이 깊다면 요청은 캐시의 긴 연쇄를 따라가므로, 길어질 수록 중간 프락시는 현저한 성능 저하 발생.
    - 실제로 네트워크 아키텍트들은 2~3개의 프락시만 거치도록 스스로 제한.

### 7.6.4 캐시망, 콘텐츠 라우팅, 피어링

- 몇 네트워크 아키텍처는 단순한 캐시 계층 대신 복잡한 캐시망을 만듬
- 캐시망의 프락시 캐시는  캐시 커뮤니케이션 결정을 동적으로 내림
    - URL에 근거하여, 부모 캐시와 원 서버 중 하나를 동적으로 선택
    - URL에 근거하여, 특정 부모 캐시를 동적으로 선택
    - 부모 캐시에 가기전에 캐시된 사본을 로컬에서 찾아보기
    - 다른 캐시들이 그들의 캐시된 콘텐츠에 부분적으로 접근 허용하되, 그들의 캐시를 통한 인터넷 트랜짓(Internet transit, 트래픽이 다른 네트워크로 건너가는 것)을 비허용
- 이렇게 더 복잡한 캐시 사이에는, 서로 다른 조직들이 상호 이득을 위해 캐시끼리 연결하여 서로를 찾아볼 수 있도록 해줌.
    - 이렇게 선택적인 피어링을 지원하는 캐시를 형제 캐시라고 함
    - HTTP는 형제 캐시를 지원하지 않으므로, ICP(인터넷 캐시 프로토콜) 이나 HTCP(하이퍼텍스트 캐시 프로토콜)같은 프로토콜을 이용하여 확장.

## 7.7 캐시 처리 단계

- 상용 프락시 캐시는 매우 고성능이며, HTTP와 그 외 기술의 고급기능을 지원하도록 만들어져 복잡함.
- 웹 캐시의 기본적인 동작은 단순하며, HTTP GET 메시지 하나를 처리하는 캐시 처리절차는 다음과 같다:
    1. 요청받기
    2. 파싱
    3. 검색
    4. 신선도 검사
    5. 응답 생성
    6. 발송
    7. 로깅

### 7.7.1 단계 1: 요청받기

- 캐시는 네트워크 커넥션의 활동을 감지하고, 들어오는 데이터를 읽어들임
- 고성능이면 여러 개의 커넥션들로부터 동시에 읽어들이고 메세지 전체가 도착전에 트랜잭션 처리를 시작

### 7.7.2 단계 2: 파싱

- 캐시는 요청 메시지를 여러 부분으로 파싱하여 헤더 부분을 조작하기 쉬운 자료구조에 담음
    - 캐싱 소프트웨어가 헤더 필드를 처리, 조작하기 쉽게 만들어줌

### 7.7.3 단계 3: 검색

- 캐시는 URL을 알아내고, 그에 해당하는 로컬 사본이 있는지 검사
- 로컬 복사본은 메모리/디스크/근처 다른 컴퓨터에 있을 수 있음.
    - 전문적인 수준의 캐시는 객체를 로컬 캐시에서 가져올 수 있는지 여부 판단을 위해 빠른 알고리즘 사용
- 로컬에서 가져올 수 없다면 캐시는 상황/설정에 따라 원 서버 혹은 부모 프락시에서 가져오거나 실패 반환
- 캐시된 객체는 서버 응답 본문과 원 서버 응답 헤더를 포함하고 있으므로, 캐시 적중 동안 올바른 서버 헤더가 반환될 수 있음
- 캐시된 객체는 얼마나 오랫동안 캐시에 머물렀는지, 얼마나 자주 사용되었는지 등에 대한 메타데이터를 포함

### 7.7.4 단계 4: 신선도 검사

- HTTP는 캐시가 일정 기간동안 서버 문서의 사본을 보유하도록 하여, 캐시는 서버 접촉 없이 해당 문서 제공
- 일정 기간을 넘기면 신선하지 않은 것으로 간주하여, 캐시는 문서 제공전에 서버와 재검사
- 신선도 검사 규칙은 매우 복잡하며, 캐시 제품들의 설정 옵션과 비 HTTP 신선도 표준과의 상호작용때문에 더욱 복잡

### 7.7.5 단계 5: 응답 생성

- 캐시된 응답을 원 서버에서 온것처럼 하기 위해, 캐시는 캐시된 서버 응답 헤더를 토대로 응답 헤더 생성
    - 이 기저 헤더들은 캐시에 의해 수정됨
- 캐시는 클라이언트에 맞게 헤더를 조정할 책임이 있음
    - 예: HTTP/1.1 응답을 긱대하는 상황에서 서버가 HTTP/1.0이라면 적절하게 번역해야함
- 캐시 신선도 정보(`Cache-Control`, `Age`, `Expires` 헤더)를 삽입하고, 프락시 캐시를 거쳐갔음을 알려주기위해 `Via` 헤더를 포함하기도 함
- `Date` 헤더(그 객체가 원 서버에서 최초로 생겨난 일시)를 조정하면 안됨.

### 7.7.6 단계 6: 전송

- 응답 헤더가 준비되면, 응답 돌려주기.
- 모든 프락시 서버와 동일하게 클라이언트와의 커넥션을 유지할 필요가 있음
    - 고성능 캐시는 종종 로컬 저장 장치와 네트워크 I/O 버퍼 사이에서 문서 콘텐츠 복사를 피해, 데이터를 효과적으로 전송하려고 노력함

### 7.7.7 단계 7: 로깅

- 로그 파일과 캐시 사용에 대한 통계를 유지.
- 각 캐시 트랜잭션 완료후, 캐시 적중과 부적중 횟수 등에 대한 통계를 갱신하고 로그 파일에 요청 종류, URL, 발생한 일에 대해 알려주는 항목 추가
- 가장 많이 쓰이는 캐시 로그 포맷은 스퀴드 로그 포맷(Squid log format), 넷스케이프 확장 공용 로그 포맷(Netscape extended common log format). 많은 캐시 제품은 커스텀 로그 파일 허용

## 7.8 사본을 신선하게 유지하기

- 캐시의 사본 모두가 서버 문서와 일치하지 않으며, 캐시된 데이터가 서버의 데이터와 일치하도록 관리해야 함
- 캐시된 사본이 서버와 일치하게 유지할 수있는 문서 만료와 서버 재검사라고하는 메커니즘을 갖고 있음

### 7.8.1 문서 만료

- 서버는 `Cache-Control` 과 `Expires` 라는 유효기간을 명시하는 특별한 헤더를 사용.
- 캐시된 문서가 만료되면, 캐시는 반드시 서버에 변경된 것이 있는지 확인하고, 그렇다면 새 유효기간과 함께 새 사본을 얻어와야 함.

### 7.8.2 유효기간과 나이

- `Expirers` 헤더: HTTP/1.0+,
    - 절대 유효기간을 명시.

    ```
    Expires: Fri, 05 Jul 2002, 05:00:00 GMT
    ```

- `Cache-Control: max-age` 응답헤더:
    - HTTP/1.1, 최대 나이 정의. 최대아니는 처음 문서가 생성된 이후부터 신선하다고 간주되지 않을 때까지의 최댓값(초 단위)

    ```
    Cache-Control: max-age=494200
    ```

### 7.8.3 서버 재검사

- 서버 재검사: 캐시가 원 서버에 문서가 변경되었는지 여부를 물어볼 필요가 있음을 의미
- 재검사 결과
    - 컨텐츠가 변경되었다면? → 새 사본을 가져와 오래된 데이터 대신 저장
    - 컨텐츠가 변경되지 않았다면? → 새 만료일을 포함한 새 헤더들만 가져와 캐시 안의 헤더 갱신
- HTTP 프로토콜은 캐시가 다음중 하나를 반환할 것을 요구:
    - 신선한 캐시된 사본
    - 원 서버와 재검사 되어서, 충분히 신선하다고 확실할 수 있는 캐시된 사본
    - 에러 메시지(재검사해야하는 원 서버각 다운된 경우)
    - 경고 메시지가 부착된 캐시의 사본(부정확한 경우)

### 7.8.4 조건부 메서드와의 재검사

- 조건부 메서드는 재검사를 효율적으로 만들어줌
    - 캐시가 서버에게 조건부 GET이라는 요청을 보내, 서버 내 문서가 캐시와 다른 경우에만 본문을 보냄
- 조건부 GET은 특별한 조건부 헤더를 추가해야 하고, 서버는 해당 조건이 참일 때 객체 반환
- HTTP에는 조건부 요청 헤더를 5개 정의했고, 각각 `If-` 접두어로 시작
    - 그 중 `If-Modified-Since`, `IF-None-Match` 각 캐시 재검사에 유용

### 7.8.5 If-Modified-Since: 날짜 재검사

- `If-Modified-Since: <date>` (IMS 요청이라고 줄여 부름)
- 주어진 날짜 이후로 수정되었다면 요청 메서드를 처리.
    - 참: 새 문서와 새 만료날짜와 정보들이 담긴 헤더와 함께 반환
    - 거짓: `304 Not Modified` 에 본문과 불필요한 헤더(예: `Content-Type` 처럼 잘 안변하는 정보)를 제외하고 갱신에 필요한 정보(예: 새 만료날짜)만 제공
- `Last-Modified` 서버 응답 헤더와 함께 사용
    - 원 서버에서 제공하는 문서의 최근 변경 일시 포함
- 몇 웹서버는 실제 날짜 비교로 구현하지 않고, 문자열 비교로 수행함.

### 7.8.6 If-None-Match: 엔터티 태그 재검사

- 날짜 재검사 하기 어려운 케이스가 있음
    - 어떤 문서는 일정 간격으로 다시쓰이지만(예: 백그라운드 프로세스로 동작) 실제 데이터가 같은 경우
    - 어떤 문서의 변경사항이 캐시가 다시 읽어들이기엔 사소한 경우(예: 철자나 주석 변경)
    - 어떤 서버는 그들의 페이지에 대한 최근 변경 일시를 정확히 판별할 수 없음
    - 어떤 서버는 1초보다 작은 간격으로 문서를 갱신
- `IF-None-Match: <tags>` :
- 마지막 변경 날짜 대신, 문서에 대한 일련번호와 같은 태그를 제공
- 캐시된 태그가 서버에 있는 문서 태그와 다를 때 요청 처리
    - 참: 새 문서와 새 ETag 반환
    - 거짓: `304 Not Modified` 응답 반환
- 엔터티 태그가 변경되었다면, 새
- 캐시가 여러 개의 사본을 갖고 있는 경우, 그것을 서버에게 알리기 위해 `If-None-Match` 헤더에 여러 개의 엔터티 태그 포함 가능

    ```
    If-None-Match: "v2.6"
    If-None-Match: "v2.4", "v2.5", "v2.6"
    If-None-Match: "foobar", "A34FAC0095", "Profiles in Courage"
    ```

### 7.8.7 약한 검사기와 강한 검사기

- 엔터티 태그로 최신인지 확인하는 경우, 엔터티 태그와 변경일시는 둘 다 "캐시 검사기(cache validator)"임.
- 약한 검사기(weak validator): 어느 정도의 컨텐츠 변경은 같은 문서로 취급 허용, 중요한 의미가 변경되면 함께 변경됨. HTTP/1.1 에서  지원.
- 강한 검사기(strong validator): 콘텐츠가 바뀔 때마다 바뀜.
- 조건부 특정 범위 가져오기 같은 몇몇 동작은 약한 검사기로는 불가능하기 떄문에, 서버는 `W/` 접두사로 약한 검사기를 구분

    ```
    ETag: W/"v2.6"
    If-None-Match: W/"v2.6"
    ```

- 서버는 서로 다른 두 엔터티에 대해 태그 값을 재활용해서는 안됨.
    - 강한 엔터티 태그: 엔터티 값이 어떻게 바뀌든지 매번 같이 바뀜
    - 약한 엔터티 태그: 대응하는 엔터티에 유의미한 변경이 있을 때마다 같이 변경

### 7.8.8 언제 엔터티 태그를 사용하고 언제 Last-Modified 일시를 사용하는가

- HTTP/1.1 클라이언트는
    - 서버가 엔터티 그 반환시 엔터티 태그 검사기를 반드시 사용.
    - Last-Modified 값만 반환했다면, `If-Modified-Since` 검사 사용
    - 둘다 가능하다면,  HTTP/1.0, 1.1 캐시 모두에 적절히 응답할 수 있도록 두 가지 정책을 모두 사용
- HTTP/1.1 서버는
    - 가능하다면 엔터티 태그 검사기를 보내야하며, 이점이 있다면 강한 엔터티 태그 대신 약한 엔터티 태그를 보낼 수 있음
    - `Last-Modified` 값을 같이 보내는 것도 선호
- HTTP/1.1 캐시나 서버가 `If-Modified-Since` 엔터티 태그 조건부 헤더를 모두 받았다면, 요청의 모든 조건부 헤더 필드의 조건에 부합되지 않는 이상 `304 Not Modified` 응답을 반환해서는 안됨

## 7.9 캐시 제어

- 서버는 캐시 만료 기간을 여러가지로 정의할 수 있으며, 우선순위는 다음과 같다.
    1. `Cache-Control: no-store` 헤더를 응답에 첨부
    2. `Cache-Control: no-cache` 헤더를 응답에 첨부
    3. `Cache-Control: must-revalidate` 헤더를 응답에 첨부
    4. `Cache-Control: max-age` 헤더를 응답에 첨부
    5. `Expires` 날짜 헤더를 응답에 첨부
    6. 아무 만료 정보를 주지않고, 캐시가 스스로 휴리스틱한 방법으로 결정

### 7.9.1 no-cache와 no-store 응답 헤더

- 캐시가 검증되지 않은 캐시 객체로 응답하는 것을 막음

```
Cache-Control: no-store
Cache-Control: no-cache
Pragma: no-cache
```

- `no-store` : 캐시가 응답의 사본을 만드는 것을 금지
    - 캐시는 보통 캐시가 아닌 프락시서버와 동일하게 클라이언트에게 `no-store` 응답 전달 후 객체 삭제
- `no-cache` : 캐시가 응답의 사본을 만들기는 하나, 서버와 재검사 없이는 클라이언트로 제공할 수 없음
- `Pragma: no-cache` :
    - HTTP/1.0+와의 하위호환성을 위해 HTTP/1.1에 포함.
    - HTTP 1.1 애플리케이션은 `Pragma: no-cache` 만 이해할 수 있는 HTTP/1.0을 대응해야 하는 경우가 아니라면 `Cache-Control: no-cache` 를 사용해야 함

### 7.9.2 Max-Age 응답 헤더

- `Cache-Control: max-age` 는 문서가 서버로부터 온 이후로 흐른 시간을 초로 나타냄
- `Cache-Control: s-maxage` 헤더는 (max와 age사이에 - 없음) `max-age` 와 동일하지만, 공유된 캐시에만 적용

```
Cache-Control: max-age=3600
Cache-Control: s-maxage=3600
```

- 서버는 0으로 설정함으로써, 캐시가 매 접근마다 문서를 캐시하거나 리프레시하지 않도록 요청할 수 있음

### 7.9.3 Expires 응답 헤더

- deprecated 된 `Expires` 헤더는 실제 만료 날짜 명시.
    - 많은 서버가 동기화되지 않거나, 부정확한 시계를 갖고 있어서 경과시간으로 표현하는 것이 낫다고 판단
- 몇 서버는 문서를 항상 만료되도록 하기 위해 `Expires: 0` 응답헤더를 돌려보내기도 하지만, 문법 위반이며 문제를 일으킬 수 있음.
    - 받아들일 수는 있지만, 생성해서는 안됨.

### 7.9.4 Must-Revalidate 응답 헤더

- 캐시는 성능 개선을 위해, 만료된 객체를 제공하도록 설정될 수 있음.
    - 엄격하게 만료 정보를 따르게 하고싶다면 `Cache-Control: must-revalidate` 사용
- 이 객체의 신선하지 않은 사본을 원 서버와의 재검사 없이 제공해서는 안됨을 의미
    - 캐시는 `must-revalidate` 신선도 검사를 시도했을 때, 원 서버 사용 불가라면 `504 Gateway Timeout error` 를 반환해야함

### 7.9.5 휴리스틱 만료

- 응답에 어떤 것도 포함하지 않는다면, 캐시는 휴리스틱한 방법으로 최대 나이 계산
- 계산 결과로 얻은 값이 24시간 보다 크다면 Heuristic Expiration 경고 헤더가 응답에 추가되어야 함
- 휴리스틱 만료 알고리즘이든 어떤 것이 사용될 수 있지만, LM 알고리즘이 유명
    - 문서가 최근 변경 일시를 포함하고 있다면 사용 가능. 얼마나 자주 바뀌는지 추정에 사용
    - 캐시된 문서의 마지막 변경일이 옛날이라면, 안정적인 문서이므로 오래보관
    - 캐시된 문서가 최근에 변경되었다면, 자주 변경될 것이고 따라서 짧은 시간만 캐시

    ```perl
    $마지막_수정_이후로_경과한_시간 = max(0, $서버의_Date - $서버의_Last_Modified);
    $서버_신선도_한계 = int($마지막_수정_이후로_경과한_시간 * $lm_인자);
    ```

- 최근 변경일 조차 없다면 캐시는 판단 근거를 갖지 못하므로, 기본 신선도 유지기간(보통 한시간~하루) 설정
    - 더 보수적인 캐시면 수명이 없기도함.

### 7.9.6 클라이언트 신선도 제약

- 웹 브라우저는 브라우저/프락시 캐시의 콘텐츠를 강제로 갱신 시켜주는 리프레시/리로드 버튼이 있음
    - `Cache-Control` 요청 헤더가 추가된 GET 요청을 발생시켜, 강제로 재검사하거나 서버로부터 콘텐츠를 가져옴
    - 정확한 동작은 브라우저, 중간 캐시 설정따라 다름
- 클라이언트는 `Cache-Control` 요청 헤더를 사용하여 만료 제약을 엄격/느슨하게 할 수 있음
- `Cache-Control` 요청 지시어
    - `Cache-Control: max-stale` / `Cache-Control: max-stale = <s>`
        - 신선하지 않은 문서라도 자유롭게 제공. 만약 `<s>`가 지정되면, 만료시간이 그 매개변수만큼 지닌 문서도 받아들임. 규칙을 느슨하게 만듦.
    - `Cache-Control: mmin-fresh = <s>`
        - 클라이언트는 지금부터 적어도 `<s>`초 후까지 신선한 문서만을 받아들임. 규칙을 엄격하게 만듦.
    - `Cache-Control: max-age = <s>`
        - 캐시는 `<s>`초 보다 오래된 문서를 반환할 수 없음.  `max-stale` 가 함께 설정되지 않는 이상 규칙을 엄격하게 만듦.
    - `Cache-Control: no-cache` / `Pragma: no-cache`
        - 클라이언트는 캐시된 리소스는 재검사 하기 전에는 받아들이지 않음
    - `Cache-Control: no-store`
        - 저장소에서 문서의 흔적을 빨리 삭제해야 함. 민감정보가 포함되어있음
    - `Cache-Control: only-if-cached`
        - 클라이언트는 캐시에 들어있는 사본만 원함

### 7.9.7 주의할 점

- 문서 만료는 완벽한 시스템이 아님
    - 실수로 유효기간을 너무 길게 설정하거나, 유효기간을 사용하지 않을 수도 있음.

## 7.10 캐시 제어 설정

- 웹 서버들은 캐시 제어/만료 HTTP 헤더 설정을 서로 다른 메커니즘으로 제공

### 7.10.1 아파치로 HTTP 헤더 제어하기

- HTTP 캐시 제어 헤더를 설정할 수 있는 여러 메커니즘을 제공.
- 대부분의 메커니즘이 디폴트로 가능하지 않아서, 활성화가 필요하며, 이 중 몇 가지를 소개

***mod_header***

- 개별 헤더를 설정할 수 있게 해주는 모듈
- 모든 HTML 파일을 캐시되지 않도록 설정하는 예

    ```
    <Files *.html>
    	Header set Cache-control no-cache
    </Files>
    ```

***mod_expires***

- 적절한 만료 날짜가 담긴 `Expires` 헤더를 자동으로 생성하는 프로그램 로직 제공하는 모듈

    ```
    ExpiresDefault A3600
    ExpiresDefault M86400
    ExpiresDefault "access plus 1week"
    ExpiresByType text/html "modification plus 2 days 6 hours 12 minutes"
    ```

***mod_cern_meta***

- HTTP 헤더들의 파일을 특정 객체와 연결시키는 모듈

### 7.10.2 HTTP-EQUIV를 통한 HTML 캐시 제어

- HTTP 서버 응답 헤더는 문서의 만료와 캐시 정보를 돌려주기 위해 사용.
- 웹 서버 설정 파일과 상호작용 없이 쉽게 HTML 문서에 HTTP 헤더 정보를 부여하기 위해 [`<META HTTP-EQUIV>`](https://developer.mozilla.org/ko/docs/Web/HTML/Element/meta) 태그를 정의함

    ```html
    <html>
    	<head>
    		<title>My Document</title>
    		<meta http-equiv="Cache-control" content="no-cache">
    	</head>
    	...
    ```

- 서버는 html에서 해당 태그를 파싱하여 응답에 헤더를 삽입할 것을 의도되어있음.
    - [RFC 1866](https://tools.ietf.org/html/rfc1866)에 정의되어 있음.
    - HTTP 서버는 문서를 처리하는 과정에서 이 정보를 사용한다. 특히, 서버는 이 문서에 대한 요청의 응답에 헤더 필드를 포함할 것이다. 헤더의 이름은 HTTP-EQUIV의 속성값에서 얻고 헤더의 값은 CONTENT 속성의 값에서 얻는다.
- 서버의 부하 가중, 설정 값이 정적, HTML 외의 다른 타입 파일은 미지원의 이유로 웹 서버나 프락시는 대부분 지원하지 않음.
    - 브라우저가 기능을 지원하여, 태그를 파싱하고 실제 HTTP 헤더처럼 다루는 경우도 있는데, 그러면 중간의 프락시 캐시와 다르게 규칙을 적용하게되어 혼란을 초래함.

## 7.11 자세한 알고리즘

- HTTP 명세에서는 문서의 나이, 캐시 신선도를 계산하는 알고리즘을 제공하지만 어렵고 혼란스럽다.
- 캐시 내부에 대한 일을 하는 독자에게 유용하며, 깊은 세부사항에 관심 없다면 건너뛰어도 무방
- 해당 내용이 필요한 경우 원책 참조

## 7.12 캐시와 광고

### 7.12.1 광고 회사의 딜레마

- 캐시는 콘텐츠 제공자에게 접근/유지 비용을 줄일 수 있지만, 광고 접근 횟수를 알기 어려움.
    - 광고의 원서버에 접근하지 않아, 캐시가 접근을 흡수함.

### 7.12.2 퍼블리셔의 응답

- 광고 회사들은 광고 시청수를 가로채지 못하도록, 모든 종류의 '캐시 무력화' 기법 사용.
- 오늘 날에는 콘텐츠 제공자는 모든 접근에 재검사를 하게 한다. 물론 트랜잭션을 느리게 함.

### 7.12.3 로그 마이그레이션

- 이상적으로는 서버로 요청이 가지 않게 하는 것. 대신 캐시가 적중한 로그를 서버에게 나누어 주는 것임.
- 하지만 적중 로그는 크기가 커 옮기기 어렵고, 표준화되지 않았으며, 인증 및 프라이버시 이슈도 있음.

### 7.12.4 적중 측정과 사용량 제한

- [RFC 2227](https://www.ietf.org/rfc/rfc227.txt), "HTTP를 위한 간단한 캐시 적중량 측정과 사용량 제한(Simple Hit-Metering and Usage-Limiting for HTTP" 에서 더 간단한 방법 정의.
- 특정 URL에 대한 캐시 적중 횟수를 정기적으로 서버에게 돌려주는 Meter라는 새 헤더 하나를 추가
    - 서버가 캐시된 문서가 적중한 횟수의 정기적인 업데이트를 캐시로 부터 받음.
    - 추가적으로 원 서버가 캐시가 문서를 제공할 수 있는 횟수나 소모할 수 있는 처리 시간도 제어 가능

## 8.1 게이트웨이

- 게이트웨이: 서로 다른 프로토콜과 애플리케이션 간의 HTTP 인터페이스
    - 리소스 게이트웨이와 프로토콜 게이트웨이가 있음

### 8.1.1 클라이언트 측 게이트웨이와 서버 측 게이트웨이

- 웹 게이트웨이는 HTTP ↔ 비 HTTP 프로토콜로 통신
    - 다른 버전의 HTTP를 변환하는 웹 프락시는 게이트웨이와 같지만, 양쪽이 HTTP라 기술적으론 프락시
- `<클라이언트 측 프로토콜>/<서버측 프로토콜>` 로 구분하여 표기. 예: HTTP/FTP
- 서버 측 게이트웨이: 클라이언트와 HTTP 통신
- 클라이언트 측 게이트웨이: 서버와 HTTP 통신

## 8.2 프로토콜 게이트웨이

- 프로토콜 게이트웨이: HTTP 트래픽을 다른 프로토콜로 자동 변환해, HTTP 클라이언트가 다른 프로토콜을 알 필요 없이 서버에 접속할 수 있게 함
- 게이트웨이에 HTTP 트래픽 바로 전송 가능. 프락시에 트래픽을 바로 전송하는 것과 유사.
    - 보통 브라우저에 명시적으로 게이트웨이를 설정해 트래픽이 게이트웨이를 거쳐가게 하거나, 게이트웨이를 대리 서버(리버스 프락시)로 설정할 수도 있음
- 게이트웨이 설정시 일반적인 HTTP 요청에는 원 서버로 보내고 FTP URL을 포함한 요청을 게이트웨이로 보냄

### 8.2.1 HTTP/* 서버측 웹 게이트웨이

- 서버측 웹 게이트웨이: HTTP 요청이 원 서버 영역으로 돌아오는 시점에 HTTP 요청을 외부 프로토콜로 전환
- HTTP/FTP 서버 측 FTP 게이트웨이:
    - HTTP 클라이언트 ↔ HTTP/FTP 인바운드 변환 게이트웨이 ↔ FTP 서버
    - 게이트웨이는 HTTP 요청을 받아 FTP 커넥션을 맺고 적절한 명령 전송
        - USER 와 PASS 명령을 보내 서버에 로그인
        - 서버에서 적절한 디렉터리 변경하기 위한 CWD 명령
        - 다운로드 형식을 ASCII 로 설정
        - MDTM으로 최근 수정 시간 가져옴
        - PASV로 서버에 수동형 데이터 검색 요청
        - RETR로 객체를 검색
        - 제어 채널에서 반환된 포트로 FTP 서버에 데이터 커넥션 맺기. 데이터 채널이 열리는대로 객체가 게이트웨이로 전송 됨

### 8.2.2 HTTP/HTTPS: 서버 측 보안 게이트웨이

- HTTP 클라이언트 ↔ HTTP/HTTPS 인바운드 보안 게이트웨이 ↔ 보안 웹 서버
- 클라이언트는 일반 HTTP를 사용해 웹을 탐색할 수 있지만, 게이트웨이는 HTTPS로 자동으로 암호화.

### 8.2.3 HTTPS/HTTP 클라이언트 측 보안 가속 게이트웨이:

- HTTP 클라이언트 ↔ HTTPS/HTTP 보안 가속 게이트웨이 ↔ 웹 서버
- 게이트웨이는 HTTPS 트래픽을 받아 복호화하고, 일반 HTTP 요청을 목적지 서버로 전달
    - HTTPS 해독은 게이트웨이에 해당 서버 인증서 설치 필요
- 이런 게이트 웨이는 보안 트래픽을 복호화하는 효율적인 암호화 하드웨어를 내장하여 원 서버 부하 경감
    - 게이트웨이 ↔ 원 서버간에는 비암호화 트래픽을 전송하므로 그 사이에 있는 네트워크 안전성 확인 필요

## 8.3 리소스 게이트웨이

- 리소스 게이트웨이: 리소스와 애플리케이션을 연결하는 역할
    - 인터프리터와 같이 리소스를 받기 위한 경로를 안내.
        - 모든 리소스를 한 개의 어플리케이션으로 처리할 수 없어 고안
    - 게이트웨이는 동적인 콘텐츠를 생성하거나 데이터베이스에 질의를 보낼 수 있음.
- 애플리케이션 서버:
    - 게이트웨이의 가장 일반적인 형태로, 목적지 서버와 게이트웨이를 한개의 서버로 결합
        - 예: 클라이언트 ↔ 애플리케이션 서버(서버 ↔ 게이트웨이) ↔ 데이터베이스
    - HTTP를 통해 클라이언트와 통신하고, 서버 측 애플리케이션 프로그램에 연결하는 서버 측 게이트웨이
    - 애플리케이션 서버는 게이트웨이의 API(Application Programming Interface)를 통해 애플리케이션에 요청 전달

### 8.3.1 공용 게이트웨이 인터페이스

- 공용 게이트 웨이 인터페이스(Common Gateway Interface, CGI):
    - 특정 URL에 대한 HTTP 요청에 따라 프로그램 실행, 출력 수집, HTTP 응답 회신하는데 웹 서버가 사용하는 표준화된 인터페이스 집합
    - 애플리케이션 게이트웨이에서 유명했던 최초의 API
- 장점:
    - CGI 애플리케이션이 서버와 분리되면서 수많은 언어로 구현할 수 있게 됨
    - 단순하여 거의 모든 HTTP 서버가 지원
    - CGI 내부 처리는 사용자에게 보이지 않음
    - 거의 모든 리소스 형식과 서버의 접점에 있으면서 필요에 따라 어떤 변형도 처리해내는 단순한 기능 제공
    - 인터페이스는 문제가 많은 확장으로부터 서버를 보호
- 단점:
    - 모든 CGI 요청마다 새 프로세스를 만드는데 부하가 따름
    - CGI를 사용하는 서버는 성능을 제한하며 서버 장비에 부담을 줌
- 단점을 보완하여 Fast CGI를 개발.
    - CGI와 유사하지만 데몬으로 동작하여 요청마다 새 프로세스를 만들고 제거하면서 생기는 성능 저하 문제 해결

### 8.3.2 서버 확장 API

- 서버 자체 동작 변경이나 처리 능력을 끌어올리기 위해 서버 개발자는 자신의 모듈은 HTTP 와 직접 연결할 수 있는 강력한 인터페이스인 서버 확장 API를 제공함.
    - 이를 이용하여 자신의 코드를 서버와 연결하거나, 서버의 컴포넌트를 자신이 만든 것으로 교체 가능
- 유명 서버의 대부분은 확장 API를 제공하며, 개발자에게 매우 유용.

## 8.4 애플리케이션 인터페이스와 웹 서비스

- 데이터를 교환하는 두 애플리케이션 사이에서 프로토콜 인터페이스를 맞추는것은 까다로운 이슈
    - HTTP 헤더로는 표현하기 힘든 정보를 교환해야 할 수도 있음
    - HTTP를 확장하거나, HTTP 위에 프로토콜을 덧씌워 맞춤 정보를 교환(19장 참조)
- 웹 서비스(web services): 각 웹 애플리케이션이 서로 통신하는데 사용할 표준과 프로토콜 집합
    - 원래는 웹 서비스는 웹 애플리케이션 그 자체를 의미하지만, 이렇게 통용되게 됨
    - 여기서 말하는 웹 서비스는 애플리케이션이 정보를 공유하는데 사용하는 새로운 매커니즘 의미
- 웹 서비스는 SOAP를 통해 XML을 사용하여 정보 교환.
    - XML(eXtensible Markup Language): 데이터 객체를 담는 데이터를 생성하고 해석 방식 제공
    - SOAP(Simple Object Access Protocol): HTTP 메시지에 XML데이터를 담는 방식의 표준
    - 현대에는 SOAP대신 REST, XML대신 JSON을 많이 사용

## 8.5 터널

- 웹 터널(Web tunnel):
    - HTTP 프로토콜을 지원하지 않는 애플리케이션에서 HTTP 애플리케이션을 사용해 접근하는 방법 제공
    - HTTP 커넥션을 통해 HTTP 가 아닌 트래픽 전송 가능하고, 다른 프로토콜을 HTTP에 올릴 수 있음
- 일반적인 사용은 HTTP 커넥션에 HTTP가 아닌 트래픽을 얹기 위해서임
    - 웹 터널을 사용하면 웹 트래픽만 허락하는 방화벽이라도 HTTP가 아닌 트래픽 전송 가능

### 8.5.1 CONNECT로 HTTP 터널 커넥션 맺기

- 웹 터널은 HTTP의 `CONNECT` 메서드를 사용하여 커넥션을 맺음
- `CONNECT` 메서드는 터널 게이트웨이가 임의의 목적 서버와 포트에 TCP 커넥션을 맺고 클라이언트와 서버 간에 오는 데이터를 무조건 전달하기를 요청
- `CONNECT` 메서드를 이용한 SSL 터널 연결
    1. 클라이언트는 게이트웨이(터널 끝단)에 터널을 연결하려고 CONNECT 요청 전송
        - 클라이언트의 CONNECT 메서드는 TCP 커넥션을 위해 게이트웨이에 터널 연결 요청
    2. 게이트웨이는 서버와 443 포트로 TCP 커넥션을 연다 
    3. 서버와 게이트웨이의 커넥션 생성 완료
    4. 게이트웨이는 클라이언트에게 HTTP 200 Connection Established 응답을 전송하여 연결 됨을 알림
    5. 이 시점에 터널이 연결되어, 커넥션이 끊길 때까지 모든 데이터가 양방향으로 전달 됨

***CONNECT 요청***

- 시작줄을 제외하고 다른 HTTP 메서드와 동일.
    - 요청 URI는 호스트 명이 대신하며 콜론에 이어 포트 기술
    - 시작줄 다음에 다른 HTTP와 동일하게추가적인 HTTP 요청해더가 0+개 있음.
    - 각 행은 CRLF로 끝나고, 해더의 목록의 끝은 빈줄의 CRLF로 끝남

```
CONNECT home.netscape.com:443 HTTP/1.0
User-agent: Mozilla/4.0
```

***CONNECT 응답***

- 일반 HTTP 메시지와 같이 200 응답 코드는 성공이며, 편의상 사유 구절은 'Connect Established' 로 기술
- 일반적인 HTTP 응답과 달리 `Content-Type` 헤더를 포함할 필요는 없음
    - 커넥션에 메시지 대신 바이트를 그대로 전달하기 떄문에 포함할 필요가 없음

```
HTTP/1.0 200 Connection Established
Proxy-agent: Netscape-Proxy/1.1
```

### 8.5.2 데이터 터널링, 시간, 커넥션 관리

- 터널을 통해 전달되는 데이터는 게이트웨이에서 볼 수 없으므로, 게이트웨이는 패킷의 순서, 흐름에 대한 가정 불가능.
- 터널이 일단 연결 되면, 데이터는 언제 어디로든 흘러가버릴 수 있음
    - 터널 양 끝단에서는 두 커넥션으로부터 언제든 패킷을 받도록 준비하고, 그 데이터를 즉시 전달해야 함.
    - 터널링된 프로토콜은 데이터 의존성을 포함하고 있거나, 터널의 한 쪽에서 입력받은 데이터를 무시할 수도 있기 때문
    - 터널의 한쪽 끝 단에서 데이터를 소비하지 않으면 터널의 다른 끝단의 데이터 생산자는 행(hang)에 걸리게 되어 교착상태가 일어남
- 클라이언트는 성능 향상을 위해 CONNECT 요청을 보낸 다음 응답 받기 전에 터널 데이터를 전송할 수 있음
    - 서버에 빨리 데이터를 보낼 수 있지만, 게이트웨이가 요청에 이어 데이터를 잘 처리할 수 있어야 가능
    - 특히 게이트웨이는 네트워크 I/O 요청이 헤더 데이터만을 반환할거라 가정할 수 없어, 게이트웨이는 커넥션이 맺어지는 대로 헤더를 포함해서 읽어들인 모든 데이터를 서버에 전송해야 함.
    - 요청 후 터널을 통해 데이터를 전송한 클라이언트는 인증요구(authentication challenge)나 200 외 응답이 왔을 때 요청 데이터를 다시 보낼 준비가 되어있어야 함
        - TCP 요청 패킷이 차지한 영역 외의 나머지 영역보다 더 큰 데이터를 파이프라인을 통해 전송하면 안됨.
        - 위 경우 게이트웨이가 파이프라인을 통해 모든 TCP 패킷을 받기 전에 커넥션을 었다면 클라이언트는 TCP 리셋을 일으킬 수 있음
        - TCP 리셋은 클라이언트가 게이트웨이에서 받은 응답을 잃어, 통신실패가 네트워크 에러인지 접근 제어나 인증요구 때문인지 알 수 없게 됨
- 터널 끝단의 어느 부분이든 커넥션이 끊어지면 그 끊어진 곳으로 부터 온 데이터는 반대편으로 전달 됨.
    - 그 뒤 커넥션이 끊어졌던 터널의 끝단 반대편의 커넥션도 프락시에 의해 끊어짐
    - 끊긴 한 쪽에 아직 전송하지 않은 데이터는 버려짐

### 8.5.3 SSL 터널링

- 웹 터널은 원래 방화벽을 통해 암호화된 SSL 트래픽을 전달하려고 개발됨
    - 과거에 강력한 보안을 위해 모든 트래픽이 패킷을 필터링하는 라우터와 프락시를 지나도록 함
    - SSL과 같은 암호화된 프로토콜은 정보가 암호화되어있어 낡은 방식의 프락시에서는 처리되지 않음
- 터널을 사용하면 SSL 트래픽을 HTTP 커넥션으로 전송해, HTTP만 허용하는 방화벽을 통과시킬 수 있음
    - HTTP에 터널링 기능이 추가됨. 암호화된 날(raw) 데이터담아 HTTP 채널을 통해 데이터 전송
- 터널은 비 HTTP 트래픽이 방화벽을 통과하게 해, 악의적인 트래픽이 들어오는 경로가 될 수 있음

### 8.5.4 SSL 터널링 vs HTTP/HTTPS 게이트웨이

- HTTPS 프로토콜(SSL 상의 HTTTP)는 다른 프로토콜과 같은 방식으로 게이트웨이 통과 가능
    - 원격 HTTPS 서버와 SSL 세션을 시작하는 게이트웨이(클라이언트 대신)를 두고 클라이언트측의HTTPS 트랜잭션 수행
    - 응답은 프락시가 받아 복호화한 후, HTTP를 통해 클라이언트로 전송
- 이 방식은 단점이 있음
    - 클라이언트-게이트웨이 사이에는 보안 미적용된 HTTP 커넥션이 맺어짐
    - 프띿시가 인증을 담당하므로, 클라이언트는 원격 서버에 SSL 클라이언트 인증을 할 수 없음
    - 게이트웨이는 SSL을 완벽히 지원해야 함
- 이 경우에 SSL 터널링을 사용하면, 프락시에 SSL을 구현할 필요가 없음.
    - SSL 세션은 클라이언트가 생성한 요청과 목적지(보안이 적용된) 웹 서버간에 생성됨
    - 프락시 서버는 트랜잭션의 보안에 관여하지않고 암호화된 데이터를 그대로 터널링

### 8.5.5 터널 인증

- HTTP의 다른 기능들은 터널과 함께 사용 가능. 특히 프락시 인증 기능은 클라이언트의 터널 사용 권한 검사용으로 사용 가능
    1. 클라이언트 → 게이트웨이 : CONNECT 요청 전송
    2. 게이트웨이 → 클라이언트 : 인증요구 반환
    3. 클라이언트 → 게이트웨이: 적절한 인증과 함께 CONNECT 요청 전송
    4. 게이트웨이 → 서버: 443 포트로 TCP 커넥션 열기
    5. 서버 → 게이트웨이: 커넥션이 맺어짐
    6. 게이트웨이 → 클라이언트: HTTP 커넥션 준비 메시지 반환

### 8.5.6 터널 보안에 대한 고려사항들

- 터널 게이트웨이는 통신하고 있는 프로토콜이 터널을 올바른 용도로 사용하는지 검증 불가.
- 터널 오용을 최소화 하기 위해 게이트웨이는 HTTPS 전용 포트인 443과 같이 잘 알려진 특정 포트만 터널링 하도록 허용해야 함

## 8.6 릴레이

- HTTP 릴레이: HTTP 명세를 완전히 준수하지는 않는 간단한 HTTP 프락시
    - 컨넥션을 맺기 위한 HTTP 통신 후 바이트를 맹목적으로 전달
    - HTTP는 복잡하므로 구현이 쉬운 이 방식이 더 유용할 때가 있음.
    - 단순 필터링, 진단, 콘텐츠 변환용으로 사용되기도 하지만 잠재적 상호 운용문제를 갖고 있으므로 주의해서 배포해야 함
- 맹목적 릴레이(blind relay) 구현 시 Connection 헤더 처리를 못해서 keep-alive 커넥션이 행(hang)에 걸리는 일반적이고 악명 높은 문제가 있음
    1. 웹 클라이언트는 `Connection: Keep-Alive` 헤더를 보내 릴레이에 keep-alive 커넥션을 맺기 원한다는 내용의 요청 메시지 전송 후 응답 대기
    2. 릴레이는 요청을 받았지만 이해하지 못하므로 서버에 바로 넘김. 
        - 하지만 Connection 헤더는 홉과 홉(hop-by-hop)에만 사용되는 헤더이므로, 단일 전송 링크만 지원하고 체인에 따라 전달 불가.
    3. 웹 서버에 요청이 도착하고, 일반적인 클라이언트-서버 처럼 keep-alive 하겠다는 잘못된 결론을 내려 `Connection: Keep-Alive` 응답 헤더 반환
        - 이 시점에서 웹 서버는 릴레이와 함께 keep-alive 통신을 하고 그 규칙에 맞게 동작함. 하지만 릴레이는 아무것도 모름
    4. 릴레이는 웹 서버로부터 받은 응답을 클라이언트에게 전달
    5. 클라이언트는 응답을 통해 릴레이가 keep-alive 통신을 동의했다고 판단. 
    6. 릴레이는 keep-alive에 대해 모르므로, 원 서버가 커넥션을 끊기를 기다리며 받은 데이터를 전부 클라이언트에 전달
        - 하지만 원 서버는 계속 커넥션을 맺도록 요청했다고 판단하여 끊지 않음
        - 릴레이는 커넥션이 끊기기를 기다리며 계속 커넥션을 맺고 있는 행(hang)상태에 있음
    7. 클라이언트는 응답 메시지를 받으면 바로 다음 요청을 keep-alive 커넥션을 통해 릴레이에 전송. 하지만 릴레이는 같은 커넥션으로 또 다른 요청이 오는 것을 예측하지 못함
        - 브라우저는 돌고 있지만 아무 작업도 진행되지 않음
- 위를 예방하기 위해, 릴레이를 약간 더 똑똑하게 만드는 방법이 있지만, 상호 운용 문제가 발생할 가능성 있음
- 신중히 고민해봐야하며, 여러 문제 예방을 위헤 HTTP를 제대로 준수하는 프락시 사용을 권장

- 웹 로봇: 사람과의 상호작용 없이 연속된 웹 트랜잭션을 자동으로 수행하는 소프트웨어 프로그램
    - 방식에 따라 '크롤러', '스파이더', '웜', '봇' 등으로 불림
- 웹 로봇의 예시:
    - 주식 시장 서버에 매분 요청을 보내 얻은 데이터를 활용해 그래프를 생성하는 로봇
    - WWW의 규모와 진화에 대한 통계 정보를 수집하기 위해 웹을 떠돌며 페이지의 갯수, 크기, 언어 미디어 타입을 기록하는 로봇
    - 검색 DB를 만들기 위해 발견한 모든 문서를 수집하는 검색엔진 로봇
    - 상품에 대한 가격 DB를 만들기 위해 온라인 쇼핑몰의 카탈로그에서 웹페이지를 수집하는 가격 비교 로봇

## 9.1 크롤러와 크롤링

- 웹 크롤러(Web Crawler): 웹 페이지를 한개 가져온 뒤, 그 페이지가 가리키는 모든 웹 페이지를 가져오고, 그 방식을 재귀적으로 반복하는 방식의 웹 순회 로봇.
    - 크롤러 혹은 스파이더라고하며, 하이퍼링크들로 만들어진 웹을 따라 기어다녀서(crawl) 이렇게 명명
- 인터넷 검색엔진은 크롤러를 이용하여 검색 가능한 DB가 됨.

### 9.1.1 어디에서 시작하는가: '루트 집합'

- 크롤러가 방문하는 URL들의 초기 집합 = 루트 집합(root set)
- 모든 문서들 끼리 이어지는 것이 아니므로, 결과적으로 관심 있는 웹 페이지 대부분을 가져올 수 있게 충분히 다른 장소의 여러 URL을 선택해야 함
    - 일반적으로 루트 집합에 너무 많은 페이지가 있을 필요는 없음. (1) 인기 있는 웹 사이트, (2) 새로 생성된 페이지들, (3) 자주 링크되지않는 알려지지 않은 페이지들로 구성 되어있음
    - 대규모 크롤러 제품들은 사용자들이 루트 집합에 새 페이지 혹은 유명하지 않은 페이지를 추가하는 기능 제공

### 9.1.2 링크 추출과 상대 링크 정상화

- 크롤러는 웹을 돌아다니며 꾸준히 HTML 문서를 검색하고, 각 페이지 내의 URL 링크를 파싱해서 크롤링할 페이지 목록에 추가함
- HTML 파싱을 하여 링크를 추출하고, 상대링크를 절대 링크로 변환할 필요가 있음

### 9.1.3 순환 피하기

- 크롤링 중에 멈추거나 진행을 느려지게 하므로 루프나 순환에 빠지지 않도록 해야 함
- 로봇은 순환을 피하기 위해 어디를 방문했는지 알고 있어야 함

### 9.1.4 루프와 중복

- 순환이 크롤러에게 해로운 이유 3가지
    1. **크롤러 무효화**: 루프는 허술하게 만든 크롤러를 계속 빙빙 돌고, 같은 페이지를 가져오는데 모든 시간을 허비하게 만듦. 이런 크롤러가 네트워크 대역폭을 다 차지하고 어떤 페이지도 가져올 수 없게 됨. 
    2. **웹 서버 부담**: 같은 페이지를 반복해서 가져오면 웹 서버에 부담이 됨. 실제 사용자도 사이트에 접근할 수 없도록 될 수도 있으며, 이런 서비스 방해 행위는 법적인 문제제기의 근거가 됨
    3. **중복된 콘텐츠**: 크롤러가 수 많은 중복 페이지를 가져와, 불필요한 중복된 콘텐츠로 넘치게 됨. 예를 들면, 수 백개의 같은 페이지를 반환하는 검색 엔진

### 9.1.5 빵 부스러기의 흔적

- 동적으로 생성된 콘텐츠를 빼도 웹 페이지는 수십 억개 존재해, 방문한 곳을 지속적으로 추적하는 것은 어려움
- 어떤 URL을 방문했는지 빠르게 판단하기 위해서는, 속도와 메모리 면에서 효과적인 복잡한 자료구조 필요
    - 빠른 속도: 많은 URL은 빠른 검색 구조를 요구. 최소 검색 트리나 해시 테이블을 필요로 함
    - 효과적인 메모리: 많은 URL은 많은 공간 차지.
- 대규모 웹 크롤러가 사용하는 유용한 기법
    1. **트리와 해시 테이블(Trees and hash tables)**
        - URL을 을 빨리 찾아볼 수 있게 해주는 자료구조
    2. **느슨한 존재 비트맵(Lossy presence bit maps)**
        - 공간 사용 최소화를 위해, 존재 비트 배열(presence bit array)와 같은 느슨한 자료 구조 사용
        - 각 URL은 해시 함수에 의해 고정된 크기의 숫자로 변환되고 배열 안에 대응하는 '존재 비트(presence bit)'을 갖음
        - URL은 크롤링 되었을 때 해당 존재 비트가 만들어 지며, 이미 존재한다면 크롤링 되었다고 간주
    3. **체크 포인트(Checkpoints)**
        - 로봇이 갑작스럽게 중단 될 경우를 대비해 방문한 URL의 목록이 디스크에 저장되었는지 확인
    4. **파티셔닝(Partitioning)**
        - 웹의 성장에 따라, 하나의 컴퓨터에서 하나의 로봇이 크롤링을 완수하는 것은 불가. 메모리, 디스크 공간, 연산 능력, 네트워크 대역 폭등이 충분치 않음.
        - 대규모 웹 로봇은 각 분리된 한 대의 컴퓨터인 로봇이 동시에 일하는 '농장(farm)'을 이용. 각 로봇은 URL들의 특정한 '한 부분'이 할당되어 그에 대한 책임을 짐
        - 로봇들은 서로 도와 웹을 크롤링. 개별 로봇들은 URL을 넘겨주거나, 오작동하는 동료를 돕거나, 그 외의 활동을 조정하기 위한 커뮤니케이션을 함

### 9.1.6 별칭(alias)과 로봇 순환

- 자료 구조를 잘 갖추어도, URL이 별칭을 가질 수 있어 어떤 페이지를 방문했었는지 알기 어려울 때도 있음.
    - 한 URL이 또 다른 URL 에 대한 별칭이면, 둘이 서로 달라보이더라도 사실은 같은 리소스를 가리킴
- 별칭의 예:
    1. 기본 포트가 80번일 때
        - `http://www.foo.com/bar.html` = `http://www.foo.com:80/bar.html`
    2. URI 인코딩된 것과 안된 것이 같을 때
        - `http://www.foo.com/~fred` = `http://www.foo.com/%7fred`
    3. 태그에 따라 페이지가 바뀌지 않을 때 
        - `http://www.foo.com/x.html#early` = `http://www.foo.com/x.html#middle`
    4. 서버가 대소문자 구분하지 않을 때
        - `http://www.foo.com/readme.htm` = `http:www.foo.com/README.HTM`
    5. 기본 페이지가 index.html 일 때
        - `http://www.foo.com/` = `http://www.foo.com/index.html`
    6. 어떤 URL이 같은 IP주소를 가질 때
        - `http:www.foo.com/index.html` = `http://209.231.87.45/index.html`

### 9.1.7 URL 정규화하기

- 대부분의 웹 로봇은 URL들을 표준 형식으로 '정규화' 함으로써 다른 URL과 같은 리소스를 가리키고 있음이 확실한 것을 미리 제거하려 시도
- 다음과 같은 방식으로 모든 URL을 정규화된 형식으로 변환.
    1. 포트가 명시되지 않았다면, ':80' 추가 (별칭 예 1번 해결)
    2. 모든 %xx 이스케이핑된 문제는 대응되는 문자로 변환 (별칭 예 2번 해결)
    3. `#` 태그 제거 (별칭 예 3번 해결)
- 별칭 4~6번을 해결하는 좋은 방법은 없음
    - 4번 문제 해결을 피하려면 웹 서버가 대소문자를 구분하는지 알 필요가 있음
    - 5번 문제를 해결하려면 디렉터리에 대한 웹 서버의 색인 페이지 설정을 알 필요가 있음
    - 6번은 URL 호스트명과 URL의 IP 주소가 같은 물리적 컴퓨터를 참조하는 것 뿐 아니라, 웹 서버가 가상 호스팅을 하도록 설정되어 있는지도 알아야 함
- URL 정규화는 기본적인 문법의 별칭을 제거할 수 있지만, 로봇들은 URL을 표준 형식으로 변환하는 것만으로는 제거할 수 없는 다른 URL 별칭도 만나게 될 것임

### 9.1.8 파일 시스템 링크 순환

- 파일의 심볼링 링크(symbolic link)는 사실상 아무것도 존재하지 않으면서 끝없이 깊어지는 디렉터리 계층을 만들어, 매우 교묘한 종류의 순환을 유발
    - 심볼링 링크 순환(symbolic link loop)은 보통 서버 관리자가 실수로 만들지만, 때때로 사악한 웹 마스터가 로봇을 함정에 빠트리기위해 악의적으로 만들기도 함
- root에 `subdir` 이라는 심볼릭 링크가 있고 이는 위쪽을 가리킨다면
    1. `GET http://www.foo.com/index.html`
    `subdir/index.html` 로 이어지는 링크 발견
    2. `GET http://www.foo.com/subdir/index.html`
    `subdir/index.html` 로 이어지는 링크 발견
    3. `GET http://www.foo.com/subdir/subdir/index.html`
    `subdir/index.html` 로 이어지는 링크 발견
    4. 이를 계속 반복

### 9.1.9 동적 가상 웹 공간

- 악의적인 웹 마스터들이 의도적으로 복잡한 크롤러 루프를 만들 수 있음
- 평범한 파일처럼 보이지만 사실은 게이트웨이 애플리케이션인 URL을 쉽게 만들 수 있음
    - 이 애플리케이션은 같은 서버에 있는 가상 URL에 대한 링크를 포함한 HTML 파일을 즉석으로 만듦
    - 악의적인 서버는 저 가상의 URL로 요청을 받으면 새 가상 URL을 갖고 있는 새 HTML을 날조하여 만듦
    - URL과 HTML이 매번 전혀 달라 보일 수 있어 로봇이 순환을 감지하기 어려울 수 있음.
- 악의 없이 심볼릭 링크나 동적 콘텐츠를 통해 크롤러 함정이 만들어질 수 있음
    - 한달치 달력을 생성하고 그 다음달로 링크를 걸어주는 CGI 기반의 달력 프로그램이 있다면, 사용자라면 영원히 다음 달 링크를 누르고 있지 않겠지만, 로봇이라면 요청할 수도 있음

### 9.1.10. 루프와 중복 피하기

- 모든 순환을 피하는 완벽한 방법은 없지만, 실제로 잘 설계된 로봇은 순환을 피하기 위해 휴리스틱의 집합을 필요로 함
- 로봇 구현자가 생각해야할 트레이드오프(trade off)는 이들 휴리스틱은 문제를 피하는데 도움이 되지만, 동시에 약간의 손실을 유발할수도 있다는 점.
    - 의심스러워 보이지만 실제로 유효한 컨텐츠를 거를 수도 있기 때문
- 웹 상에서 로봇이 더 올바르게 작동하기 위해 사용하는 기법들에 대해 설명

***URL 정규화(Canonicalizing URLs)***

- URL을 표준 형태로 변환해, 같은 리소스를 가리키는 중복된 URL 생기는 것을 일부 회피

***너비 우선 크롤링(Breadth-first crawling)***

- 방문할 URL들을 웹 사이트 전체에 걸쳐 너비 우선으로 스케줄링하면 순환 영향 최소화 가능
- 혹시 로봇 함정을 건드리더라도, 그 순환에서 페이지를 받아오기 전에 다른 웹사이트들에서 페이지를 많이 받아올 수 있음
- 만약 깊이 우선 방식이라면, 순환을 건드리는 경우 영원히 다른 사이트로 나올 수 없음.
- 또한 너비 우선 크롤링은 요청을 더 분산 시켜 특정 서버가 압박 받지 않게 해주는 장점이 있음.
    - 한 로봇이 한 서버의 자원을 최소로 사용하도록 유지

***스로틀링(Throttling)***

- 로봇이 일정 시간동안 가져올 수 있는 페이지의 숫자 제한.
- 순환을 건드려 지속적으로 접근을 시도한다면, 스로틀링을 이용해 서버에 대한 접근, 중복의 총 횟수를 제한 할 수 있음

***URL 크기 제한***

- 일정 길이(보통 1KB)를 넘는 URL의 크롤링은 거부 가능
- 만약 순환으로 인해 URL이 계속 길어진다면, 길이 제한으로 인해 순환이 중단됨
- 하지만 이 기법으로 인해 가져오지 못하는 콘텐츠도 있음.
    - 오늘날 사이트들은 URL을 통해 사용자 상태를 관리하여, URL이 길어질 수 있음
- 이 기법은 요청 URL이 특정 크기가 될 때마다 에러 로그를 남겨, 특정 사이트에서 발생하는 일을 감시하는 사용자에게는 훌륭한 신호가 됨

***URL/사이트 블랙리스트***

- 순환을 만들거나 함정인 것으로 알려진 사이트와 URL 목록을 만들어 관리하여 피함
- 사람의 손을 필요로 함
- 오늘날의 실제 사용중인 대규모 크롤러들은 몇가지 형태의 블랙리스트를 가지고 있음
- 블랙리스트는 크롤링을 원치 않는 사이트를 피하기 위해서도 사용될 수 있음

***패턴 발견(Pattern detection)***

- 파일 시스템의 심볼릭 링크를 통한 순환과 그 비슷한 오설정들은 일정한 패턴이 있음
- 몇 로봇은 반복되는 구성요소를 가진 URL을 잠재적 순환으로 보고, 둘 혹은 셋 이상의 반복된 구성요소를 갖는 URL 크롤링을 거절.
- 단순히 한 디렉터리의 반복(예: `/subdir/subdir/...`)뿐 아니라 주기 길이가 2이상인 반복(예: `/subdir/images/subdir/images/...` 도 감지

***콘텐츠지문(Content fingerprinting)***

- 중복을 감지하는 보다 직접적인 방법
- 콘텐츠 지문을 사용하는 로봇은 페이지의 콘텐츠에서 몇 바이트를 얻어내 체크섬(checksum)을 계산
    - 이 체크섬은 그 페이지 내용의 간략한 표현을 의미
    - 만약 이전에 본 체크섬을 가진 페이지를 가져온다면, 그 페이지의 링크는 크롤링 하지 않음.
- 체크섬 함수는 두 페이지의 내용이 다름에도 체크섬이 같을 확률이 낮은 것을 사용해야 함.
    - 지문 생성용으로는 MD5와 같은 메시지 요약 함수가 인기
- 어떤 웹 서버는 동적으로 페이지를 수정하므로, 로봇은 웹 페이지 콘텐츠에 임베딩된 링크와 같은 특정 부분들을 체크섬 계산에 빠뜨림.
- 페이지 콘텐츠를 임의로 커스터마이징을 포함한 서버의 동적 동작은 중복 감지 방해

***사람의 모니터링***

- 로봇이 어떤 기법으로도 해결할 수 없는 문제에 봉착하면 사람이 확인할 수 있도록, 진단과 로깅을 포함하도록 설계해야 함.

## 9.2 로봇의 HTTP

- 로봇은 다른 클라이언트 프로그램과 다르지 않으며, 똑같이 HTTP 명세의 규칙을 지켜야 함.
- 많은 로봇들이 콘텐츠를 요청하기위해 필요한 HTTP를 최소한으로 구현하려고 함

### 9.2.1 요청 헤더 식별하기

- 최소한으로 HTTP를 지원하려하지만, 약간의 신원 식별헤더(특히 `User-Agent` 헤더)는 구현 및 전송
- 로봇의 능력, 신원, 출신을 알려주는 기본적인 몇 가지 헤더를 사이트에게 전송하는 것이 좋음
    - (1) 문제가 있는 크롤러의 소유자를 찾아 낼 때, (2) 로봇이 서버에게 어떤 종류의 콘텐츠를 다룰 수 있는지 정보를 줄 때 유용.
- 구현하도록 권장되는 기본 신원 식별 헤더
    - `User-Agent` : 서버에게 요청을 만든 로봇의 이름을 말해줌
    - `Form` : 로봇의 사용자/관리자의 이메일 주소 제공(RFC 822 이메일 주소 포맷)
    - `Accept` : 서버에게 어떤 미디어 타입을 보내도 되는지 안내.
    - `Referer` : 현재 요청 URL을 포함한 문서의 URL 제공. 어떻게 이 페이지를 찾았는지 알고 싶은 사이트 관리자에게 유용

### 9.2.2 가상 호스팅

- 가상 호스팅이 널리 퍼진 현실에서 요청 헤더에 Host 헤더를 포함하지 않으면 잘못된 콘텐츠를 찾게 됨
    - Host 헤더가 없는 크롤러는 A 사이트에 요청을 보냈지만, 서버가 A와 B를 가상 호스팅하므로 B의 콘텐츠를 돌려 줄 수 있음. 크롤러는 B의 내용을 A로부터 온 것으로 생각하게 됨

### 9.2.3 조건부 요청

- 로봇은 때때로 매우 많은 양의 요청을 시도하므로, 로봇이 검색하는 콘텐츠의 양을 최소화하는 것은 중요
    - 오직 변경된 콘텐츠만 가져오도록 하는 것은 의미가 있음
- 로봇 중 몇몇은 시간이나 엔터티 태그를 비교하여 마지막 버전 이후의 업데이트를 알아보는 조건부 HTTP 요청을 구현. (캐시와 유사)

### 9.2.4 응답 다루기

- 대다수의 로봇은 주 관심사가 GET 메서드로 콘텐츠를 요청해서 가져오기라 응답 다루기라고 할 만한 일을 하지 거의 하지 않음
- 그러나 몇 특정 기능(조건부 요청 등)을 사용거나 웹 탐색이나 서버와의 상호작용을 더 잘하려는 로봇은 여러 종류의 HTTP 응답을 다룰 줄 알아야 함

***상태코드***

- 일반적으로 로봇은 최소한의 일반적인 상태 코드나 예상할 수 있는 상태코드를 다룰 수 있어야 함
    - 200, 404는 이해해야하 하며, 이해할 수없는 상태 코드는 그 코드가 속한 분류에 근거하여 다루어야 함
- 모든 서버가 항상 적절한 에러코드를 반환하지 않는 것에 유의해야 함

***엔터티***

- HTTP 헤더에 임베딩 된 정보를 따라 로봇들은 엔터티 자체의 정보를 찾을 수 있음
- `http-equiv` 태그는 콘텐츠 저자가 콘텐츠를 다루는 서버가 제공할 수도 있는 헤더를 덮어쓰기 위한 수단
- 로봇 구현자는 `http-equiv` 정보를 찾기 위해 HTML문서의 HEAD태그를 탐색하는 것을 원할 수도 있음.
    - 예: `<meta http-equiv="Refresh" content="1; URL=index.html">`
        - Refresh는 리다이렉트용으로 사용되며, 위는 1초후에 index.html로 리다이렉트하라는 의미
    - 명세상으로 `http-equiv` 는 HEAD 섹션에 있어야하지만 이상한 데 있을 수도 있음

### 9.2.5. User-Agent 타겟팅

- 웹 관리자들은 많은 로봇들로 부터의 요청을 예상하고 이를 다루기위한 전략을 세워야 함.
- 많은 웹 사이트들은 브라우저 종류를 감지하여 콘텐츠를 최적화하는데, 이는 로봇에게 콘텐츠 대신 에러페이지를 제공할 수도 있음.
    - 예: 당신의 브라우저는 프레임를 지원하지 않습니다

## 9.3 부적절하게 동작하는 로봇들

***폭주하는 로봇***

- 로봇은 빠르게 HTTP 요청을 만드므로, 논리적 에러를 갖고있거나 순환에 빠진다면 웹 서버에 극심한 부하를 안겨줌
- 모든 로봇 저자는 폭주 방지를 위한 보호 장치를 신경써서 설계해야 함

***오래된 URL***

- URL 목록이 오래되었을 수 있음 = 로봇이 존재하지 않는 URL에 대한 요청을 많이 보낼 수 있음
- 이러한 요청으로 에러 로그가 채워지거나, 웹 서버는 에러페이지를 제공하는 부하 발생

***길고 잘못된 URL***

- 순환이나 프로그래밍 상의 오류로 인해 로봇은 웹 사이트에게 크고 의미 없는 URL 요청 가능
- URL이 많이 길다면 웹 서버 처리능력에 영향을 주고, 접근 로그를 어지럽게하고, 허술한 웹서버라면 고장을 일으킬수도 있음

***호기심이 지나친 로봇***

- 사적인 데이터에 대한 URL을 얻어 그 데이터를 인터넷 검색엔진이나 기타 애플리케이션을 통해 쉽게 접근하도록 만들 수도 있음.

***동적 게이트웨이 접근***

- 로봇들이 항상 접근하고 있는 것에 대해 잘 알고 있는 것은 아님
- 로봇은 게이트웨이 애플리케이션 콘텐츠에 대한 URL을 요청할 수 있고, 이는 보통 특수 목적을 위한 데이터이며 처리비용이 많이 들어 웹 사이트 관리자가 좋아하지 않음

## 9.4 로봇 차단하기

- **로봇 차단/배제 표준(Robots Exclusion Standard)** a.k.a `robots.txt`: 로봇의 접근을 막거나, 웹 마스터가 로봇의 동작을 더 잘 제어할 수 있는 메커니즘을 제공하는 기법. 1994년 제안됨
    - 웹 서버는 서버의 문서 루트에 robots.txt라는 파일을 선택적으로 제공할 수 있고, 이 파일은 어떤 로봇이 서버의 어떤 부분에 접근할 수 있는지에 대한 정보를 담고 있음
    - 로봇이 이 표준을 따른다면 웹 사이트의 다른 리소스를 접근하기 전에 `robots.txt` 를 요청

### 9.4.1 로봇 차단 표준

- 로봇 차단 표준은 임시방편으로 마련된 표준이며, 이 표준이 작성되고 있을 떄, 업체들은 이 표준의 부분 집합을 제각각 구현하고 있었음.
- 사실상 표준(de facto standard)이 되어, 대부분의 주류 업체들과 검색엔진 크롤러들은 이 차단 표준 지원
- 2019년 7 월 1 일 Google에서 IEFT 산하 공식 표준으로 "[Robots Exclusion Protocol](https://tools.ietf.org/html/draft-rep-wg-topic-00)" 을 제안. 아직 초안(draft)이며 승인 절차를 거치는 중.

### 9.4.2 웹 사이트와 robots.txt

- 웹 사이트의 어떤 URL을 방문하기 전, 그 웹 사이트에 `robots.txt` 파일이 존재한다면 로봇은 반드시 그 파일을 가져와서 처리해야 함.
    - `robots.txt` 파일이라고 부르지만, 꼭 파일시스템에 존재해야할 이유는 없으며 게이트웨이 애플리케이션이 동적으로 생성할 수도 있음
- 한 호스트명과 포트번호에 의해 정의된 어떤 웹 사이트는, 그 사이트 전체에 대한 `robots.txt` 파일을 하나만 가지고 있음
    - 이 책을 쓰고 있는 시점에서 '로컬' `robots.txt` 파일을 웹 사이트의 개별 서브디렉터리에 설치할 수 있는 방법은 없음
- 웹 사이트가 가상 호스팅된다면, 다른 모든 파일과 동일하게 가상의 `docroot` 에 서로 다른 `robots.txt` 가 있을 수 있음

***robots.txt 가져오기***

- 로봇은 어느 파일들과 마찬가지로 HTTP GET 메서드를 이용하여 `robots.txt` 리소스를 가져옴.
    - 존재한다면, 서버는 그 파일을 `text/plain` 본문으로 반환.
    - 존재하지 않는다면, 서버는 `404 Not Found` 로 응답.
        - 로봇은 접근을 제한하지 않는것으로 간주하고 어떤 파일이든 요청.
- 로봇은 사이트 관리자가 로봇의 접근을 추적할 수 있도록 `From` 이나 `User-Agent` 헤더를 통해 신원 정보와 문의나 불만사항을 보낼 연락처를 제공

    ```
    GET /robots.txt HTT/1.0
    Host: www.joes-hardware.com
    User-Agent: Slurp/2.0
    ```

***응답코드***

- 로봇은 `robots.txt` 검색결과에 따라 다르게 동작
    - 상태코드 2XX: 성공으로 응답하면 그 응답의 콘텐츠를 파싱하여 차단 규칙을 얻고, 그 사이트에서 무언가를 가져오려 할 때 그 규칙에 따름
    - 상태코드 404: 리소스가 존재하지 않는다고 응답하면 로봇은 활성화된 차단 규칙이 없다고 가정하고 제약없이 그 사이트에 접근
    - 상태코드 401, 403: 서버가 접근 제한으로 응답하면 로봇은 그 사이트로의 접근은 완전히 제한되어있다고 가정
    - 상태코드 503: 만약 요청 시도가 일시적으로 실패했다면 로봇은 그 사이트의 리소스를 검색하는 것을 뒤로 미루어야 함
    - 상태코드 3XX: 만약 서버 응답이 리다이렉션을 의미한다면 로봇은 리소스가 발견될때까지 리다이렉트를 따라가야 함

### 9.4.3 robots.txt 파일 포맷

- 매우 단순한 줄 기반 문법을 갖으며, 각 줄은 빈줄, 주석줄, 규칙 줄의 세가지 종류가 있음
    - 규칙 줄은 HTTP 헤더처럼 생겼고(`<필드>:<값>`) 패턴 매칭을 위해 사용.

```
# 이 파일은 Slurp, webcrawler에게 공개된 영역을 크롤링하는 것을 허용하고
# 그 외 다른 로봇은 안된다 (주석 영역)

User-Agent: slurp
User-Agent: webcrawler
Disalow: /private

User-Agent: *

```

- 이 줄들은 레코드로 구분되며, 각 레코드는 특정 로봇의 집합에 대한 차단 규칙의 집합을 기술.
    - 이 방법을 통해 로봇 별로 각각 다른 차단 규칙 적용
- 각 레코드는 규칙 줄들의 집합으로 되어 있으며, 빈줄이나 파일 끝(end-of-file) 문자로 끝남.
- 레코드는 어떤 로봇이 레코드에 영향을 받는지 지정하는 하나 이상의 `User-Agent` 줄로 시작하며 뒤이어 이 로봇들이 접근할 수 있는 URL을 말해주는 `Allow` 줄과 `Disallow` 줄이 옴

***User-Agent 줄***

- 각 로봇의 레코드는 하나 이상의 `User-Agent` 줄로 시작함
    - `User-Agent: <robot-name>` 나 `User-Agent: *` 형식을 갖을 수 있음
    - 로봇의 이름은 로봇의 HTTP GET 요청 안의 User-Agent 헤더를 통해 보내짐
- `robots.txt` 를 처리한 로봇은 다음의 레코드에 반드시 따라야 함
    - 로봇 이름이 자신 이름의 부분 문자열이 될 수 있는 레코드 중 첫 번째 것
    - 로봇 이름이 '*'인 레코드 중 첫 번째 것
- 만약 대응하는 `User-Agent` 를 찾지 못하였고, 와일드카드를 사용한 줄도 찾지 못하였다면 대응하는 레코드가 없는 것이므로 접근에 제한이 없음
- 로봇 이름은 대소문자를 구분하지 않는 부분 문자열과 맞춰보므로, 의도치 않게 맞는 경우에 주의해야 함

***Disallow와 Allow 줄들***

- 로봇 차단 레코드의 `User-Agent` 줄 바로 다음에 오며, 특정 로봇에 대한 어떤 URL 경로가 명시적으로 금지/허용 되는지 기술함
- 로봇은 반드시 요청하려고하는 URL을 차단 레코드의 모든 `Disallow` 와 `Allow` 규칙에 순서대로 맞춰보아야 함. 첫 번째로 맞은 것이 사용되며, 아무것도 맞지 않다면 허용된 것으로 처리함
    - robots.txt URL은 항상 허용되어야하고, `Allow` / `Disallow` 규칙에 나타나서는 안됨
- URL과 맞는 하나의 `Allow` / `Disallow` 줄에 대해, 규칙 경로는 반드시 맞춰보고자하는 경로의 대소문자를 구분하는 접두어야 함

***Disallow/Allow 접두 매칭(prefix matching)***

- `Disallow` 나 `Allow` 규칙이 어떤 경로에 적용되려면, 그 경로의 시작부터 규칙경로 길이만큼의 문자열이 규칙 경로와 같아야 함(대소문자 구분)
    - `User-Agent` 와 달리 * 대신 빈 문자열을 이용해 모든 문자열에 매치되도록 할 수 있음
    - 규칙 경로나 URL 경로의 임의의 '이스케이핑된' 문자들(%XX)은 비교 전에 원래대로 복원됨. 빗금(/)을 의미하는 %2F는 예외로 반드시 그대로 매치되어야 함
    - 어떤 규칙 경로가 빈 문자열이라면 그 규칙은 모든 URL 경로와 매치됨
- 규칙 경로 → URL 매치 예시
    - `/tmp` → `/tmp` : 매칭O. 규칙경로 == URL 경로
    - `/tmp` → `/tmpfile.html` : 매칭O. 규칙경로가 URL 경로의 접두어
    - `/tmp` → `/tmp/a.html` : 매칭O. 규칙경로가 URL 경로의 접두어
    - `/tmp/` → `/tmp` : 매칭X. 규칙경로가 URL 경로의 접두어가 아님
    - `/~fred/hi.html %` → `%7Efred/hi.html` : 매칭O. %7E = ~
    - `/%7Efred/hi.html` → `/~fred/hi.html` : 매칭O. %7E = ~
    - `/%7efred/hi.html` → `/%7Efred/hi.html` : 매칭O. 이스케이핑된 문자는 대소문자 비구분
    - `/~fred/hi.html` → `~fred%2Fhi.html` : 매칭X. 빗금은 특별히 정확히 매치되어야 함
- 접두 매칭은 꽤 잘 동작하지만, 어떤 경로 밑에있냐와 상관없이 특정 이름에 대해서 크롤링을 막는 것은 어려움.

### 9.4.4 그 외에 알아둘 점

- `robots.txt` 파일은 명세가 발전함에 따라 `User-Agent`, `Disallow`, `Allow` 외의 다른 필드를 포함할 수 있음. 로봇은 이해하지 못하는 필드는 무시해야 함
- 위 호환성을 위해, 한 줄을 여러 줄로 나누어 적는것은 비허용
- 주석은 파일 어디에서든 사용 가능.
- 로봇 차단 표준의 구버전은 `Allow` 줄을 지원하지 않았음. 몇 오래된 로봇은 `Allow` 줄을 무시함. 이 경우 허용되는 URL도 탐색하지 않음

### 9.4.5 robots.txt의 캐싱과 만료

- 매 파일접근마다 `robots.txt` 파일을 새로가져와야하면 로봇도 비효율적이며 웹 서버 부하도 커짐
- 로봇은 주기적으로 `robots.txt` 파일을 가져와서 결과를 캐시함
    - 캐시된 사본은 만료될 때까지 로봇에 의해 사용 됨
    - `robots.txt` 캐싱 제어를 위해 표준 HTTP 캐시 제어 메커니즘이 원 서버와 로봇 양쪽에서 사용됨
- 크롤러 제품들이 HTTP 버전이 다른 클라이언트일 수 있어, 캐시 지시자를 이해 못할 수도 있는 점에 유의
- 로봇 명세 초안에는 `Cache-Control` 지시자가 있는 경우 7일간 캐싱 명시
    - 실무에서 보면 보통 너무 김.
- 몇 대규모 크롤러는 활발히 크롤링하는 기간에는 매일 새로 가져옴

### 9.4.6 로봇 차단 펄 코드

- `robots.txt` 파일과 상호작용하는 공개된 펄(Pearl) 라이브러리가 몇 있음
- 책에서는 CPAN 공개 펄 아카이브의 WWW::RobustRules 모듈을 사용하며, 자세한 내용은 책 참조

### 9.4.7. HTML 로봇 제어 META 태그

- `robots.txt` 의 단점 중 하나는 그 파일을 콘텐츠 작성자 개개인이아닌 웹 사이트 관리자가 소유한다는 것
- HTML 페이지 저자는 로봇이 개별 페이지에 접근하는 것을 제어하도록 HTML 문서에 로봇 제어 태그를 추가할 수 있음.
- 로봇 제어 HTML 태그에 따르는 로봇들은 문서를 가져올 수 있지만, 로봇 차단 태그가 있다면 문서를 무시
    - `robots.txt` 와 동일하게 따르는 것을 권장하지만 강제하지 않음
- 다른 모든 META 태그와 동일하게 HEAD 섹션에 나타나야 함.

***로봇 메타 태그 지시자***

- `<meta name="robots" content=directive-list>` 와 같이 구현. (대소문자 미구분)
- 지시자에는 종류가 몇가지 있으며, 앞으로 추가될 가능성이 높음
- 지시자 목록 ([참고](https://developer.mozilla.org/ko/docs/Web/HTML/Element/meta/name#%EB%8B%A4%EB%A5%B8_%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0_%EC%9D%B4%EB%A6%84))
    - NOINDEX: 크롤러가 페이지를 색인하지 않도록 요청
    - NOFOLLOW: 크롤러가 페이지 내의 링크를 따라가지 않도록 요청
    - INDEX: 크롤러가 페이지를 색인 가능 (기본 값)
    - FOLLOW: 크롤러가 페이지 내의 링크를 따라 감 (기본 값)
    - NOARCHIVE: 크롤러가 페이지를 캐시에 포함하지 않도록 요청. Google과 Bing 에서 사용
    - ALL: index, follow와 동일
    - NONE: noindex, nofollow와 동일

***검색엔진 META 태그***

- 그 외에 콘텐츠의 색인을 만드는데 사용되는 메타 태그들
    - DESCRIPTION: 페이지에 대한 짧고 정확한 요약.
        - `<meta name="description" content="여기는 골동품 상점">`
    - KEYWORDS: 페이지의 콘텐츠와 관련된, 쉼표로 구분한 키워드 목록.
        - `<meta name="keywords" content="antiques,mary,restoration">`

## 9.5 로봇 에티켓

- 로봇 차단 표준을 제안한 마틴 코스터(Martijn Koster) 로봇 제작자를 위한 가이드라인 목록 작성
    - 일부는 구식이지만 여전히 상당수는 유용하며, 현대적으로 고쳐쓴 가이드라인을 아래 소개

***(1) 신원 식별***

- 로봇의 신원을 밝히기
    - HTTP `User-Agent` 를 이용하여 로봇 이름 밝히기. 몇 로봇은 `User-Agent` 헤더에 목적과 정책을 기술한 URL을 포함하기도 함
- 기계의 신원 밝히기
    - 로봇이 DNS 엔트리를 가진 기계에서 실행되는 것을 확실히 해, 웹 사이트가 로봇의 IP 주소를 호스트 명을 통해 역방향 DNS 할 수 있도록 해야 함.
- 연락처를 밝히기
    - HTTP 폼 필드를 사용하여 연락할 수 있는 이메일 주소를 제공

***(2) 동작***

- 긴장하라
    - 로봇 운영자는 반드시 로봇이 바르게 행동하는지 감시해야 함
- 대비하라
    - 로봇을 실행하기 앞서 자신이 속한 조직에 알려둘 필요가 있음. 조직은 네트워크 대역폭의 소비를 감시하고, 어떤 문의에도 응할 준비를 하고 싶어 함.
- 감시와 로그
    - 로봇은 진행상황을 추적하고, 로봇 함정을 식별하고, 모든 것이 정상 동작하는지 기본적인 검사가 가능하도록 진단, 로깅 기능을 풍부하게 갖추어야 함.
- 배우고 조정하라
    - 크롤링을 할 때마다 새로운것을 배우게 될 것이며, 매번 로봇을 조정하고 개선하여 흔한 함정에 빠지는 것을 피해야 함

***(3) 스스로를 제한하라***

- URL을 필터링하라
    - 만약 URL을 이해할 수 없거나 관심 없는 데이터를 참조하는것 같다면 무시하는 것이 좋음
    - 예: URL의 확장자를 보고 판단. `.exe` 나 `.zip` 일 수 있음
- 동적 URL을 필터링하라
    - 보통 로봇들은 동적인 게이트웨이로부터 콘텐츠를 크롤링할 필요가 없음.
    - 게이트웨이에 적합한 쿼리를 보낼 줄 모를 것이며, 그 결과는 불규칙하거나 일시적임
- Accept 관련 헤더로 필터링
    - HTTP Accept 관련 헤더를 이용해 서버에게 어떤 콘텐츠를 이해할 수 있는지 말해줘야 함
- robots.txt 에 따르라
    - 방문한 웹 사이트의 robots.txt 제어를 따라야 함
- 스스로를 억제하라
    - 웹 사이트 접근 빈도를 세고, 이를 이용해 특정 사이트에 너무 자주 방문하지 않도록 해야 함.
    - 일반적으로 최대 분당 수 회 정도로 제한해야 하며, 각 요청 사이의 간격은 수 초 이상이 되어야 함. 총 접근 횟수도 제한되어야 함

***(4) 루프와 중복 견뎌내기, 그리고 그 외의 문제들***

- 모든 응답 코드 다루기
    - 리다이렉트, 에러를 포함한 모든 HTTP 상태 코드를 다룰 수 있어야 함. 또한 이에 대한 로그를 남기고 모니터링해야 함.
    - 특정 사이트의 응답이 성공이 아닌 것이 많다면, URL이 신선하지 않거나 서버가 로봇에 문서를 제공하지 않으려는 것일 수 있음
- URL 정규화하기
    - 모든 URL을 정규화해 같은 자원을 가리키는 중복 URL을 제거해야 함
- 적극적으로 순환 피하기
    - 순환을 감지하고 피하기 위해 많은 노력을 해야 함
- 함정 감시하라
    - 의도적이고 악의적인 순환을 잘 감시하라
- 블랙리스트를 관리하라
    - 함정, 사이클, 깨진 사이트, 로봇을 붙잡고싶은 사이트들을 블랙리스트에 추가해 방문하지 말아야 함

***(5) 확장성***

- 공간 이해하기
    - 풀고 있는 문제가 얼마나 큰지에 대해 미리 계산 필요. 로봇 작업 하나에 많은 메모리 요구.
- 대역폭 이해하기
    - 얼마나 네트워크 대역폭이 사용 가능한지, 요구 시간 내에 로봇작업이 끝내는데 얼마나 필요할지 이해할 것
    - 네트워크 사용량을 모니터링하면서 더 최적화하는 가능성을 찾을 수 있음
- 시간 이해하기
    - 로봇이 작업을 끝내는데 얼마나 많은 시간이 필요한지 이해하고, 추정시간과 실제 시간을 비교해야 함. 너무 다르다면 조사해봐야할만한 문제가 있을 수 있음.
- 분할 정복
    - 대규모 크롤링을 한다면 더 많은 하드웨어를 사용할 필요가 있음

***(6) 신뢰성***

- 철저하게 테스트하라
    - 로봇을 세상에 내놓기 전에 철저하게 테스트하고, 처음 밖에 내놓을 때는 작은 규모로 몇 차례 수행할 것
- 체크포인트
    - 어떤 로봇은 실패 위치에서 다시 시작하기 위해 진행상황의 스냅숏을 저장하고 싶어함
    - 대규모 로봇은 실패 할 때마다 처음부터 다시 시작할 수 없으므로 체크포인트/재시작 기능을 설계 해야 함
- 실패에 대한 유연성
    - 실패에 대비하여 로봇이 실패가 발생해도 계속 동작하도록 설계

***(7) 소통***

- 준비하라
    - 로봇에 대한 문의에 빠르게 응답할 수 있도록, 로봇 정책 안내 페이지와, 그 페이지에 robots.txt를 만드는 법을 포함해야 함
- 이해하라
    - 모든 문의자가 로봇에 대해 잘 아는 것이 아니므로 로봇 차단 규칙 표준에 대해 설명하고, 그래도 만족하지 못한다면 그 URL을 블랙리스트에 추가해야 함
- 즉각 대응하라
    - 로봇에 대한 항의/문의는 빠르게 대응해야 함

## 9.6 검색엔진

- 로봇을 가장 광범위하게 사용하는 것은 인터넷 검색 엔진

### 9.6.1 넓게 생각하라

- 웹 초창기에는 검색엔진은 웹상 문서 위치를 알아내는 것을 돕는 상대적으로 단순한 데이터베이스
- 현재는 수많은 사용자와 수많은 웹 페이지에서 원하는 정보를 찾으므로 복잡한 크롤러를 사용해야 함

### 9.6.2 현대적인 검색엔진의 아키텍처

- 요즘 검색엔진들은 '풀 텍스트 색인(full-text indexes)'라는 복잡한 로컬 데이터베이스 생성
- 검색엔진 사용자는 웹 검색 게이트웨이를 통해 풀 텍스트 색인에 대해 질의 전송
    - 크롤링을 하는데는 시간이 걸리고 웹 페이지는 실시간으로 변하기때문에 풀 텍스트 색인은 기껏해야 특정 순간의 스냅숏에 불과

### 9.6.3 풀 텍스트 색인

- 풀 텍스트 색인: 입력 받은 단어를 포함하는 문서를 즉각 알려줄 수 있는 데이터베이스
    - 색인이 생성된 후에는 검색할 필요 없음

### 9.6.4 질의 보내기

- 사용자가 질의를 웹 검색엔진 게이트로 보내는 방법은 HTML 폼을 이용해 GET이나 POST 요청을 게이트웨이로 보냄
- 게이트웨이 프로그램은 검색 질의를 추출하고 풀텍스트 색인을 검색할 때 사용되는 형식으로 변환

### 9.6.5 검색 결과를 정렬하고 보여주기

- 질의의 결과를 확인하기 위해 검색엔진이 색인을 한번 사용 했다면, 게이트웨이 애플리케이션은 그 결과를 이용하여 최종 사용자를 위한 결과 페이지를 즉석으로 만들어 냄
- 많은 페이지가 검색어를 포함할 수 있으므로, 결과에 순위를 매기기위한 알고리즘을 사용
    - 관련도 랭킹(relevancy ranking): 검색 결과의 목록에 관련도로 점수를 매기고 정렬하는 과정
    - 이를 더 잘 지원하기 위해 많은 검색 엔진이 웹을 크롤링하는 과정에서 수집된 통계 데이터도 사용.

### 9.6.6 스푸핑

- 사용자들은 자신이 찾는 내용이 최상위 몇줄에서 보이지 않으면 불만족스러워 하므로, 검색 결과 순서는 중요
- 웹 마스터는 자신이 만든 사이트가 상단으로 만들 동기가 충분함
    - 이를 위해 관련없는 키워드를 나열하거나 가짜 페이지를 만들어 검색엔진을 더 잘 속일수 있도록하는 경우도 있음
- 검색엔진 및 로봇 구현자들은 이러한 속임수를 잘 잡아내기 위해 관련도 알고리즘을 계속 수정해야 함

책은 HTTP/2.0의 [8번째 초안](https://tools.ietf.org/html/draft-ietf-httpbis-http2-08#section-4.1) 기준으로 작성되어있으며, 책 내용에 [RFC 7540](https://tools.ietf.org/html/rfc7540) 내용을 추가하였다.

## 10.1 HTTP/2.0의 등장 배경

- HTTP/1.1의 메세지 포맷은 구현의 단순성, 접근성에 주안점을 두고 최적화 되다보니, 성능에 희생이 있었음
    - 심각한 회전 지연(latency)가 있었고, 이를 회피하기 위해 병렬 커넥션, 파이프라인 커넥션이 도입되었지만 근본적인 해결책은 아니었음.
- 이 문제 해결을 위해 여러 노력이 있었고, 그 중 구글이 기존의 HTTP에 속도 개선을 위한 기능을 추가한 프로토콜인 SPDY(스피디)를 내놓았음
    - 헤더 압축하여 대역폭 절약
    - 하나의 TCP 커넥션에 여러 요청을 동시에 보내 회전 지연 줄이기
    - 클라이언트가 요청을 보내지 않아도 서버가 능동적으로 리소스를 푸시
- HTTP 작업 그룹은 SPDY를 기반으로 HTTP/2.0 초안을 만들기 시작하였음

## 10.2 개요

- HTTP/2.0은 서버와 클라이언트의 TCP 커넥션 위에서 동작. TCP 커넥션을 초기화 하는 것은 클라이언트
- HTTP/2.0 요청, 응답은 길이가 정의된(최대 16383바이트) 한 개 이상의 프레임에 담김.
    - HTTP 헤더는 압축되어 담김
- 프레임에 담긴 요청/응답은 스트림을 통해 보내지며, 한 개의 스트림이 한 쌍의 요청/응답을 처리
    - 한 커넥션 위에 여러 개의 스트림이 동시에 만들어질 수 있어, 여러 개의 요청/응답 동시 처리 가능
- 스트림에 대한 흐름 제어와 우선순위 부여 기능 제공
- **서버 푸시**:
    - 서버가 클라이언트에게 필요하다고 생각하는 리소스라면, 요청을 명시적으로 받지 않더라도 클라이언트에게 능동적으로 보낼 수 있음
    - 최근의 HTTP/2.0과 3.0에는 제거 됨. [참고 링크1](https://evertpot.com/http-2-push-is-dead/), [참고 링크 2](https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYLvmQUBY/m/vOWBKZGoAQAJ?pli=1)
- 호환성 유지를 위해, 요청/응답 메시지의 의미를 동일하게 유지하였으나, 일부 표현하는 문법은 변경됨
    - `Content-Length` 헤더 → `.content-length`
    - 상태 줄에서 표현되는 `404 Not Found` → `.status` 헤더에 `404` 로 제공

## 10.3 HTTP/1.1과의 차이점

### 10.3.1 프레임

- 모든 메시지는 프레임에 담겨 전송.
    - 프레임은 8바이트 크기의 헤더로 시작하여, 16383바이트 크기의 페이로드가 옴

```
+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                      ...
+---------------------------------------------------------------+
```

- 프레임 헤더의 각 필드(RFC 7540 발췌)
    - **Length(길이)**: 페이로드의 길이를 나타내는 24비트 무부호 정수(unsigned integer). 2^14(16,384) 보다 큰 값이 오면 안됨. 이 길이에 프레임 헤더는 미포함.
    - **Type(타입)**: 프레임의 타입을 타나내는 8비트.
    - **Flags(플래그)**: 8비트 플래그. 플래그 값의 의미는 프레임 종류에 따라 다름.
    - **R**: 예약된 1비트 필드. 값의 의미가 정의 되어있지 않으며 반드시 0이여야함. 받는 쪽에서는 값을 무시
    - **Stream Identifier(스트림 식별자)**: 31비트 무부호 정수로 표현된 스트림 식별자. 특별히 0 값은 개별 스트림이 아닌 커넥션 전체와 연관된 프레임을 의미하는 것으로 예약 되어있음.
- HTTP/2.0은 `DATA`, `HEADERS`, `PRIORITY`, `RST_STREAM`, `SETTINGS`, `PUSH_PROMISE`, `PING`, `GOAWAY`, `WINDOW_UPDATE`, `CONTINUATION` 이라는 총 10개의 프레임을 정의하고 있으며, 페이로드의 형식이나 내용은 프레임 종류에 따라 다름.
    - 자세한 설명은 RFC 7540의 ["6. Frame Definitions"](https://tools.ietf.org/html/rfc7540#section-6) 참고.

### 10.3.2 스트림과 멀티플렉싱

- **스트림**: HTTP/2.0 커넥션을 통해 클라이언트와 서버 사이에서 교환되는 프레임들의 독립된 양방향 시퀀스
- 한쌍의 요청/응답은 하나의 스트림을 통해 이루어짐.
    - 클라이언트는 새 스트림을 만들어 그를 통해 HTTP 요청을 보내고, 서버는 요청과 같은 스트림으로 응답을 보냄. 이후 스트림은 닫힘
- HTTP/1.1은 TCP 커넥션으로 요청을 보내면, 응답 받기 전까지는 같은 커넥션으로 요청을 보낼 수 없어 회전 지연을 피하기 어려움
    - 회전 지연을 줄이기 위해 TCP 커넥션을 여러 개 만들지만, 무한정 만들 수 없음.
- HTTP/2.0은 한 커넥션에 여러 개의 스트림이 동시에 열릴 수 있어 쉽게 해결됨.
- 스트림은 우선 순위가 있음
    - 네트워크 대역폭이 충분치 않아 프레임 전송이 느리다면, 웹 브라우저는 중요한 리소스(예: 이미지보다 HTML)를 요청하는 리소스에게 더 높은 우선순위 부여 가능
    - 단, 우선순위에 따르는 것은 의무 사항이 아니므로, 요청 우선순위대로 처리된다는 보장은 없음
- 모든 스트림은 31비트의 무부호 정수로된 고유한 식별자를 가짐.
    - 스트림 초기화 주체가 클라이언트면 홀수, 서버면 짝수
    - 새 스트림의 식별자는 이전에 만들어지거나 예약된 스트림의 식별자보다 커야 함
    - 이 규칙을 어기는 식별자를 받았다면 에러코드가 `PROTOCOL_ERROR` 인 커넥션 에러로 응답해야 함
- 서버/클라이언트는 스트림을 상대방과 협상 없이 일방적으로 만듦
    - 스트림 만들 때 협상을 위해 TCP 패킷을 주고 받는 시간 낭비를 하지 않기 위함
- 동시에 여러 스트림을 사용하면 스트림이 블록될 우려가 있다는 주장에 의해, `WINDOW_UPDATE` 프레임을 이용한 흐름 제어(flow control)을 통해, 스트림들이 서로 간섭해서 망가지는 것을 막음

### 10.3.3 헤더 압축

- 요즘 웹 페이지는 수 십~수 백번의 요청을 보내기 때문에, 헤더의 크기가 회전 지연 및 대역폭에 영향을 끼침
- HTTP/2.0은 헤더를 압축하여 보냄.(HTTP/1.1은 무압축)
    - 헤더는 HPACK 명세에 정의된 헤더 압축 방법으로 압축된 뒤, '헤더 블록 조각'들로 쪼개져서 전송.
    - 받는 쪽에서는 이 조각을 이은 뒤 압축을 풀어 원레 헤더 집합으로 복원
- HPACK 헤더를 압축하고 해제 할 때 '압축 콘텍스트(compression context)'를 사용
    - 압축 콘텍스트는 수신한 헤더의 압축을 풀면 변경 됨. 송신측은 이를 가정함.
    - 수신 측은 어떤 경우에도(헤더를 안쓰더라도) 헤더 압축 해제를 수행하고, 불가하다면  `COMPRESSION_ERROR` 와 함께 커넥션 끊어야 함.

### 10.3.4 서버 푸시

- 하나의 요청에 대해 응답으로 여러 개의 리소스를 보낼 수 있도록 해줌
    - 서버가 클라이언트에서 어떤 리소스를 요구할 것인지 미리 알 수 있는 상황에 유용
    - 이는 클라이언트가 HTML 파싱 후 필요 리소스를 다시 요청하느라 발생하는 트래픽 및 회전 지연을 감소
- 리소스를 푸시하려는 서버는 클라이언트에게 `PUSH_PROMISE` 프레임을 보내 푸시할 것임을 미리 알림
    - 클라이언트가 그 프레임을 받으면, 그 프레임의 스트림은 클라이언트 입장에서 '예약됨(원격)' 상태가 됨
    - 클라이언트가 `RST_STREAM` 프레임을 보내 푸시를 거절할 수 있음. 보내면 그 스트림은 바로 닫힘
    - 스트림이 닫히기 전까지는 서버가 푸시하려고 하는 리소스를 요청해서는 안됨
- 사전에 서버가 `PUSH_PROMISE` 프레임을 보내는 이유는, 푸시하려는 자원을 클라이언트가 별도로 요청하는 상황을 피하기 위함
- 서버 푸시 사용시 주의사항
    - 서버 푸시를 사용하기로 했더라도, 중간 프락시가 추가 리소스를 클라이언트에게 전달 안하거나, 반대로 서버로부터 받지 않았더라도 보낼 수 있음
    - 서버는 오직 안전하고, 캐시 가능하고, 본문을 포함하지 않은 요청에 대해서만 푸시 가능
    - 푸시할 리소스는 클라이언트가 명시적으로 보낸 요청과 연관된 것이여야 함
    - 클라이언트는 서버가 푸시한 리소스를 **동일 출처 정책(Same-origin policy)** 에 따라 검사해야함.
        - 예: `example.org` 로의 커넥션은 `www.example.org` 로부터의 푸시 응답 비허용
    - 서버 푸시를 끄고 싶다면 `SETTINGS_ENABLE_PUSH` 를 0으로 설정

## 10.4 알려진 보안 이슈

### 10.4.1 중개자 캡슐화 공격

- HTTP/2.0 메시지를 중간 프락시(중개자)가 HTTP/1.1 로 변환시에 메시지가 변질될 가능성 있음
- HTTP/2.0은 HTTP/1.1과 달리 헤더 필드의 이름/값을 바이너리로 인코딩
    - 이는 HTTP/2.0이 헤더필드로 어떤 문자열(줄바꿈 포함)이든 사용하게 해줌
    - 이를 통해 HTTP/2.0 요청이 불법적이거나 위조된 HTTP/1.1 메시지로 번역 될 수 있음
    - 반대의 케이스(HTTP/1.1 → HTTP/2.0)는 발생하지않음

### 10.4.2 긴 커넥션 유지로 인한 개인정보 누출 우려

- 회전 지연을 줄이기 위해 커넥션을 오래 유지하는 것이 개인 정보 유출에 악용될 수 있음
    - 예: 브라우저 사용시에 이전 브라우저 사용자가 무엇을 했는지 알아 낼 수 있음.
    - HTTP가 현재 갖고있는 문제이지만, 짧게 유지되는 커넥션에서는 위험이 적음

## 11.1 개별 접촉

- HTTP: 익명, 상태 없음(stateless), 요청/응답으로 통신하는 프로토콜
- 현대의 웹 사이트는 개인화된 서비스를 제공.
    - **개별 인사**: 사용자에게 특화된 환영 메시지, 페이지 내용 만듦.
    - **사용자 맞춤 추천**: 고객의 흥미를 학습해서 좋아할 것으로 예상되는 제품 추천, 생일 등의 중요한 일정에 특별한 제품 제시
    - **저장된 사용자 정보**: 복잡한 주소, 신용 카드 정보를 데이터 베이스에 저장.
    - **세션 추적**: 사용자와 사이트가 상호작용할 수 있게, 상태를 남김(HTTP 트랜잭션은 상태가 없어, 각 요청/응답이 독립적으로 발생.)
- 이 장에서는 HTTP가 사용자를 식별하는데 사용하는 기술 정리
## 11.2 HTTP 헤더

- 사용자 정보를 전달하는 가장 일반적인 일곱가지 HTTP 요청 헤더
    - `Form`: 요청 헤더. 사용자의 이메일 주소
        - 악의적인 서버가 이메일 주소를 모아 스팸을 발생하는 문제가 있어서, 해당 헤더를 브라우저를 보내는 브라우저가 많지 않음
        - 로봇/스파이더가 데이터 수집 중에 웹 사이트에 문제를 일으켰을 때, 항의 메일을 보내도록 Form 헤더에 이메일 주소를 기술하기도 함
    - `User-Agent`: 요청 헤더. 사용자의 브라우저
        - 사용자가 쓰고있는 브라우저 이름, 버전 정보, 혹은 운영체제 정보를 포함
        - 특정 브라우저에서 잘 동작하도록 콘텐츠 최적화에는 유용하지만, 사용자 식별에는 도움이 되지 않음

        ```html
        크롬 38.0.2125.111 User-Agent
        Mozilla/5.0 (Windows NT 6.1) AppleWebkit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36

        인터넥 익스플로러 9.0.29 User-Agent
        Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC@; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E
        ```

    - `Referer`: 요청 헤더. 사용자가 현재 링크를 타고 온 근원 페이지
        - 해당 헤더 자체만으로는 사용자 식별불가. 이전에 어떤 페이지를 방문했는지는 알 수 있어, 웹 사용행태나 취향 파악 가능.
    - `Authorization`: 요청 헤더. 사용자 이름, 비밀번호
    - `Client-ip`: 확장(요청) 헤더. 클라이언트 IP 주소
    - `X-Forwarded-For`: 확장(요청) 헤더. 클라이언트 IP 주소
    - `Cookie`: 확장(요청) 헤더. 서버가 생성한 ID 라벨

## 11.3 클라이언트 IP 주소

- 초기에는 사용자 식별을 IP주소로 하려고 함
    - 사용자가 확실한 IP 주소를 갖고 있고, 주소가 변경되지 않고, 매 요청시에 IP를 알 수 있다면 문제 없음
- HTTP 헤더에 IP는 없지만, HTTP 요청을 보내는 반대쪽 TCP 커넥션의 IP주소를 알아낼 수 있음

    ```html
    // 예: 유닉스 시스템에서 getpeername을 이용한 클라이언트 IP 주소 받기
    status = getpeername(tcp_connection_socket, ...);
    ```

- 하지만 IP주소로 사용자를 식별하는 방법은 다음과 같은 약점이 있음
    - IP주소는 사용자가 아닌 컴퓨터를 가리키므로, 여러 사용자가 사용시 식별 불가능
    - 많은 ISP는 사용자가 로그인하면 동적으로 IP주소 할당. 로그인 시간에 따라 매번 다른 주소를 받으므로 식별 불가.
    - 보안 강화 및 부족한 주소 관리를 위해 많은 사용자가 네트워크 주소 변환(Network Address Translation, NAT) 방화벽을 통해 인터넷 사용. 이 NAT 장비들은 실제 IP주소를 방화벽 뒤로 숨기고, 실제 IP주소를 내부에서 사용하는 하나의 방화벽 IP주소(및 다른 포트 번호)로 변환
    - 보통, HTTP 프락시와 게이트웨이는 원 서버에 새로운 TCP 연결. 웹 서버는 클라이언트 IP주소 대신 프락시 서버 IP주소를 보게 됨. 일부 프락시는 원본 IP 주소를 보존하지만, 모든 프락시가 그러지는 않음.

## 11.4 사용자 로그인

- 사용자 이름 및 비밀번호로 인증(로그인)을 요구하여 명시적 식별 요청을 할 숫 있음.
- HTTP는 웹 사이트 로그인이 쉽도록 웹 사이트에 사용자 이름을 전달하는 자체적인 체계가 있음
    - `WWW-Authenciate` , `Authorization` 헤더 사용
- 서버에서 사용자가 사이트 접근 전에 로그인을 시키고자 한다면 HTTP `401 Login Required` 를 브라우저애 보낼 수 있으며, 브라우저는 로그인 대화 상자를 보여주고, 다음 요청에 `Authorization` 헤더에 그 정보를 기술하여 전송
    1. 브라우저가 `www.joes-hardware.com` 사이트로 요청
    2. 사이트는 사용자의 식별정보를 알지 못하므로, `401 Login Required` 응답 코드와 `WWW-Authenciate` 헤더 반환하여 로그인을 요청. 브라우저는 로그인 대화상자 띄움. 
    3. 사용자가 사용자 이름, 비밀번호를 입력하고 브라우저는 기존 요청을 다시보내서 사용자 식별을 시도
    4. 서버는 사용자의 식별정보를 알게됨
    5. 이 시점 이후에 요청에 대해서 브라우저는 서버로부터 사용자 식별정보를 요청받으면, 자동으로 사용자 이름, 비밀번호를 전달하고, 요청하지 않았을 때도 전달함. 이는 사이트에서 한번만 로그인 하면 브라우저는 요청마다 `Authorization` 헤더에 담아 서버로 전송해 세션 진행 내내 식별을 유지.

## 11.5 뚱뚱한 URL

- 어떤 웹 사이트는 사용자의 URL 마다 버전을 포함해, 사용자 식별 및 추적
    - URL 경로의 처음이나 끝에 상태 정보를 추가해 확장
    - 사용자가 사이트를 탐색하면, 웹 서버는 URL에 있는 상태 정보를 유지하는 하이퍼링크를 동적으로 생성
- 사용자의 상태 정보를 포함하는 URL을 뚱뚱한 URL(fat URL)이라고 함
    - 아마존에서 사용하는 뚱뚱한 URL의 예(사용자 식별번호 002-145265-801683):

    ```html
    <a href="/exec/obidos/tg/browse/-/2290220/ref=gr_gifts/002-145265-801683">All Gifts</a>
    <a href="/exec/obidos/wishlist/ref=gr_pl1_/002-145265-801683">Wish List</a>
    ...
    ```

- 뚱뚱한 URL 동작 방식
    1. 사용자가 웹 사이트에 처음 방문하면 유일 ID가 생성
    2. 유일 ID 값은 서버가 인식할 수 있는 방식으로 URL에 추가되며, 서버는 이 뚱뚱한 URL로 리다이렉트 시킴
    3. 서버가 뚱뚱한 URL을 포함한 요청을 받으면, 사용자 아이디와 관련 정보를 찾아서 밖으로 향하는 모든 하이퍼링크를 뚱뚱한 URL로 바꿈 
- 뚱뚱한 URL의 단점
    - **못생긴 URL**: 브라우저에 보이는 URL이 혼란을 줌
    - **공유하지 못하는 URL**: 특정 사용자의 정보를 포함하여, 누군가에게 공유하면 개인정보도 공유하게 됨
    - **캐시를 사용할 수 없음**: URL이 바뀌므로 기존 캐시에 접근 불가
    - **서버 부하 가증**: 서버는 뚱뚱한 URL에 해당하는 HTML 페이지를 다시 그려야 함.
    - **이탈**: 링크를 타고 다른 사이트를 이동하거나, 특정 URL을 요청하여 뚱뚱한 URL 세션에서 이탈하기 쉬움.
    - **세션 간 지속성 부재**: 특정 뚱뚱한 URL을 북마킹하지 않는 이상, 로그아웃시에 모든 정보를 잃음

## 11.6 쿠키

- 사용자 식별 및 세션 유지 방식 중 제일 널리 사용하는 방식
    - 앞선 기술들의 문제점을 겪지 않지만, 쿠키만으로 불가능한 경우는 앞선 기술과 같이 사용하기도 함
- 넷스케이프가 최초로 개발했고, 현재는 모든 브라우저에서 지원.
- 쿠키는 캐시와 충돌할 수 있어서, 대부분의 캐시, 브라우저는 쿠키를 캐싱하지 않음

### 11.6.1 쿠키의 타입

- 세션 쿠키 session cookie:
    - 사이트 탐색할 때, 관련 설정및 선호 사항을 저장하는 임시 쿠키
    - 브라우저를 닫으면 삭제됨
- 지속 쿠키 peristent cookie:
    - 사용자가 주기적으로 방문하는 사이트에 대한 설정 정보나 로그인 이름 유지에 사용
    - 브라우저를 닫거나 컴퓨터를 재시작하더라도 남음
- 세션쿠키와 지속 쿠키는 파기시점만 다름

### 11.6.2 쿠키는 어떻게 동작하는가

1. 처음에 사용자가 웹 사이트 방문시, 서버는 사용자에 대해 아무것도 모름
2. 웹 서버는 사용자가 다시 돌아 왔을 때에 사용자를 식별하기 위한 유일한 값을 쿠키에 할당
    - key=value 형태의 리스트를 가지고, `Set-Cookie` 같은 HTTP 응답 헤더에 포함
    - 어떤 정보든 포함할 수 있지만, 사용자 추적용 ID를 주로 사용하며 그 외에 많은 정보를 유지할 수 있음
3. 브라우저는 서버로 온 `Set-Cookie` 헤더의 쿠키 콘텐츠를 브라우저 쿠키 데이터베이스에 저장
4. 사용자가 미래에 같은 사이트를 방문 시 서버가 사용자에게 할당했던 쿠키를 Cookie 요청헤더에 기술해 전송 

### 11.6.3 쿠키 상자: 클라이언트 측 상태

- 쿠키의 기본 발상은 브라우저가 서버 관련 정보를 저장하고 사용자가 해당 서버에 접근할 때마다 그 정보를 함께 정보하는 것.
- 즉 브라우저는 쿠키 정보를 저장할 책임이 있고, 이를 '**클리아언트 측 상태(client-side state)**' 라고 함.
    - 쿠키 명세에서 공식적인 이름은 '**HTTP 상태 관리 체계(HTTP State Management Mechanism)**'

***구글 크롬 쿠키***

- Cookies라는 SQLite 파일에 쿠키를 저장.
- 총 15개의 필드가 있으며, 그 중 몇 필드는 다음과 같음(크롬 87.0.4280.88 기준):

    ```sql
    CREATE TABLE cookies
    	( 
    		creation_utc    INTEGER NOT NULL, 
    		host_key        TEXT NOT NULL, 
    		name            TEXT NOT NULL, 
    		value           TEXT NOT NULL, 
    		path            TEXT NOT NULL, 
    		expires_utc     INTEGER NOT NULL, 
    		is_secure       INTEGER NOT NULL, 
    		is_httponly     INTEGER NOT NULL, 
    		last_access_utc INTEGER NOT NULL, 
    		has_expires     INTEGER NOT NULL DEFAULT 1, 
    		is_persistent   INTEGER NOT NULL DEFAULT 1, 
    		priority        INTEGER NOT NULL DEFAULT 1, 
    		encrypted_value BLOB DEFAULT '', 
    		samesite        INTEGER NOT NULL DEFAULT -1, 
    		source_scheme   INTEGER NOT NULL DEFAULT 0, 
    		UNIQUE (host_key, name, path) 
    	);
    ```

    - `creation_utc`: 쿠키가 생성된 시점. UNIX 시간
    - `host_key`: 쿠키의 도메인
    - `name`: 쿠키의 이름
    - `value`: 쿠키의 값
    - `path`: 쿠키와 관련된 도메인에 있는 경로
    - `expires_utc`: 쿠키의 파기 시점. UNIX 시간
    - `is_secure`: SSL 커넥션인 경우에만 보낼지 여부

***마이크로소프트 인터넷 익스플로러 쿠키***

- 캐시 디렉터리에 각각 개별 파일로 쿠키를 저장 했었음.
    - Internet options > Browsing history - Settings > View files
- 인터넷 익스플로러 자체 형식을 가지고 기술되지만, 이해할 수 있게 쉽게 되어있음.
    - 과거에는 일반 IDE로 열어서 확인 가능했지만, 현재는 모든 쿠키 파일이 `deprecated.cookie` 파일에 심링크 되어있음

    ```
    // deprecated.cookie 파일 내용
    Cookies are no longer stored in files. Please use Internet*Cookie* APIs to access cookies.
    ```

### 11.6.4 사이트마다 각기 다른 쿠키들

- 브라우저는 수천개의 쿠키를 가지고 있을 수 있지만, 브라우저가 쿠키 전부를 모든 사이트에 보내지 않음
    - 쿠키를 모두 전달하면 성능이 크게 저하되기 때문. 실제 콘텐츠보다도 더 많아질 수 있음
    - 쿠키는 대부분 각 서버에 특화된 key/value 이므로, 다른 사이트에서는 인식하지 않는 무의미한 값이기 때문
    - 모든 사이트에 쿠키 전체를 전달하는 것은, 신뢰하지 않는 사이트에 잠재적인 개인정보를 전달할 수 있기때문
- 브라우저는 보통 쿠키를 생성한 서버에게만 쿠키에 담긴 정보를 전달.
- 많은 웹 사이트는 광고 관리 업체와 계약하여, 광고들이 웹 사이트의 일부인거처럼 제작하고 지속쿠키를 만들어 냄.
    - 같은 광고사에서 서로 다른 웹 사이트에 사용자가 방문하면, 브라우저는 앞서 만든 지속 쿠키를 다시 광고사로 서버 전송.(지속 쿠키 도메인이 같으므로)
    - 이 기술에 `Referer` 헤더를 접목하여, 사용자의 프로필가 웹 사용 습관 데이터를 구축
    - 최신 브라우저는 개인정보 설정 기능을 통해 쿠키 사용 방식에 제약을 가할 수 있음

***쿠키 Domain 속성***

- 서버는 쿠키 생성 시 `Set-Cookie` 응답 헤더에 `Domain` 속성을 기술해, 어떤 사이트가 그 쿠키를 읽을수 있는지 제어 가능.

    ```
    Set-cookie: user="mari17"; domain="airtravelbargains.com"
    ```

- 사용자가 해당 도메인, 혹은 서브 도메인을 방문하면 `Cookie: user="mari17"` 가 항상 적용될 것임

***쿠키 Path 속성***

- URL 경로의 앞부분을 가리키는 `Path` 속성을 기술해 웹 사이트 일부에만 쿠키 적용 가능.

    ```
    Set-cookie: pref=compact; domain="airtravelbargain.com"; path=/autos/
    ```

- `http://www.airtravelbargains.com/autos/...` 로 접속시에는 pref 도 쿠키로 받게 됨.

### 11.6.5 쿠키 구성 요소

- 쿠키의 버전
    - Version 0 쿠키(a.k.a 넷프케이 쿠키) Version 1 쿠키([RFC 2965](https://tools.ietf.org/html/rfc2965)) 쿠키가 있으며, Version 1은 Version 0 쿠키의 확장. RFC 2965는 [RFC 6265](https://tools.ietf.org/html/rfc6265)에 의해 폐기됨.
    - Version 0, 1쿠키 명세는 HTTP/1.1 명세로 기술되어있지 않음.
- 아래의 헤더 내용은 책 내용과 MDN 내용을 포함하여 현대의 방식으로 기술하였음.

***Set-Cookie 헤더 [[참고]](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Set-Cookie)*** 

```
Set-Cookie: name=value [; expires=date] [; path=path] [; domain=domain] [; secure]
Cookie: name1=value1 [; name2=value2] ...
```

- **이름=값**: 필수 속성. 이름과 값은 모두 큰 따옴표로 감싸지 않은 세미콜론, 쉼표, 등호, 공백을 포함하지 않는 문자열
- **Expires**: 선택 속성. 쿠키의 생명주기를 가리키는 문자열로, 타임존은 GMT. 명시하지 않으면 사용자 세션이 끝날 때 파기 됨.
- **Max-Age**: 선택 속성. 쿠키가 만료될 때 까지의 시간(초 단위). 0, 음수 값이면 쿠키는 즉시 만료. 오래된 브라우저는 지원하지 않으며, 지원하는 브라우저는 `Expires` 와 `Max-Age` 둘 다 있으면 `Max-Age` 를 우선시
- **Domain**: 선택 속성. 브라우저는 이 속성이 기술된 도메인(및 서브도메인)을 사용하는 서버 호스트명으로만 쿠키를 전송. 명시하지 않으면 응답을 생성한 서버의 호스트 명(서브 도메인을 미포함)을 기본 값으로 사용.
- **Path**: 선택 속성. 서버에 있는 특정 문서에만 쿠키를 할당할 수 있음. URL의 경로의 앞부분과 일치하면 쿠키 전달.
- **Secure**: 선택 속성. 이 속성이 포함되면 HTTP가 SSL 보안 연결을 사용할 때만 쿠키 전송

***Cookie 헤더 [[참고]](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Cookie)***

- 클라이언트가 서버에 요청을 보낼 때는, `Domain`, `Path`, `Secure` 필터가 맞는 경우에 파기되지 않은 쿠키들을 함께 보낸다. 모든 쿠키는 한 `Cookie` 헤더에 이어 붙여서 보낸다.

```
Cookie: session-id=002-1145265-0816838; session-id-time=1007884800
```

### 11.6.6 Version 0(넷스케이프) 쿠키

- 최초의 쿠키 명세로, `Set-Cookie` 응답 헤더, `Cookie` 요청 헤더, 쿠키를 조작하는데 필요한 필드를 정의

### 11.6.7 Version 1(RFC 2965) 쿠키

- 확장된 버전은 Version 0 시스템과 호환되며, `Set-Cookie2`, `Cookie2` 헤더를 소개 함
- 현재 미사용 되고 있으며 필요시에는 원본 책을 참고

### 11.6.8 쿠키와 세션 추적

1. 브라우저가 [Amazon.com](http://amazon.com) 의 루트 페이지를 처음 요청
2. 서버는 클라이언트를 전자상거래 소프트웨어 URL로 리다이렉트
3. 클라이언트는 리다이렉트 URL 로 요청 보냄
4. 서버는 응답에 세션 쿠키를 기술하고, 사용자를 다른 URL로 리다이렉트, 클라이언트는 다시 이 쿠키를 첨부하여 요청. 
5. 클라이언트는 새로운 URL 요청을 앞서 받은 쿠키와 함께 전송
6. 서버는 hoem.html 페이지로 리다이렉트 시키고, 쿠키를 더 첨부
7. 클라이언트는 home.html 페이지를 가져오고 더 첨부된 쿠키를 포함하여 전달
8. 서버는 콘텐츠를 전송 

### 11.6.9 쿠키와 캐싱

- 쿠키 트랜잭션과 관련된 문서를 캐싱하는 것을 주의해야함.
    - 다른 사용자의 쿠키가 할당되거나, 다른 사람에게 개인정보가 노출될 수 있음.
- 쿠키와 캐싱에 관련된 규칙은 잘 정리되어있지 않지않음.

***캐시를 다루는 기본 원칙***

- **캐시되지 말아야할 문서가 있다면 표시하라**
    - `Cache-Control: nocache="SetCookie"` 를 기술하여 명확히 표시
    - 캐시해도 되는 문서라면 `Cache-Control: public` 을 사용하면 대역폭 절약 가능
- **Set-Cookie 헤더를 캐시하는것에 유의하라**
    - `Set-Cookie` 헤더를 포함하는 응답의 본문은 캐시할 수 있지만, `Set-Cookie` 헤더를 캐시하는것은 주의해야함.
    - 캐시가 모든 요청마다 원 서버와 재검사 시킴(`Cache-Control: must-revalidate, max-age=0`)으로 문제 개선 가능
- **Cookie 헤더를 가지고 있는 요청을 주의하라**
    - 요청이 `Cookie` 헤더와 함께 오면, 개인정보를 담을수도 있다는 힌트.
    - 동일하게 원 서버와 재검사하여 해결할 수 있음.

### 11.6.10 쿠키, 보안 그리고 개인정보

- 쿠키를 미사용하거나, 로그 분석같은 방법으로 대체 가능하므로 그 자체로 보안상으로 위험한 것은 아님
    - 원격 데이터베이스에 개인정보를 저장하고 해당 데이터의 키값을 쿠키에 저장하면 예민한 데이터가 오가는 것을 줄일 수 있음
- 개인 정보 정책에만 유의한다면, 쿠키에 관련한 위험성보다 세션 조작이나 트랜잭션상의 편리함이 큼

## 12.1 인증

- **인증**: 당신이 누구인지 증명하는 것(예: 비밀번호)

### 12.1.1 HTTP의 인증요구/응답 프레임워크

- HTTP는 사용자 인증을 하는 데 사용하는 자체 인증요구/응답 프레임워크를 제공
- HTTP 인증 모델:
    - HTTP 요청 메시지를 받으면, 서버는 요청을 처리 대신에 현재 사용자가 식별을 위해 비밀번호 같이 개인 정보를 요구하는 '인증 요구'로 응답 가능
    - 사용자는 다시 요청시에 인증 정보를 첨부하고 서버는 정보를 맞으면 요청을 처리, 맞지 않으면 재인증요구를 보내거나 에러 발생

### 12.1.2 인증 프로토콜과 헤더

- HTTP는 필요에 따라 수정할 수 있는 제어 헤더를 통해, 다른 인증 프로토콜에 맞춰 확장할 수 있는 프레임워크 제공
    - 헤더 형식과 내용은 인증 프로토콜에 따라 다르며, 인증 프로토콜은 HHTTP 인증 헤더에 기술되어 있음
- HTTP의 공식적인 인증 프로토콜:
    - 기본 인증(basic authentication)
    - 다이제스트 인증(digest authentication) 13장 참고
    - 현대에는 HTTP 인증요구/응답 프로토콜을 사용하는 새 프로토콜을 사용(예: [OAuth](https://tools.ietf.org/html/6749))
- 네가지 인증 단계
    - 요청(GET): 인증정보 미포함
    - 인증 요구(`401 Unauthhroized`)
        - 서버는 사용자에게 이름/비밀번호를 제공하라는 지시의 의미로 401과 함께 요쳥 반려. 서버 `WWW-Authenticate` 헤더에 인증 방법에 대한 정보를 제공.
    - 인증(GET)
        - 클라이언트는 인증 알고리즘과 사용자 이름/비밀번호를 기술한 `Authorization` 헤더를 함께 보냄
    - 성공(`200 OK`)
        - 인증정보가 정확하면, 서버는 문서와 함께 응답. 인증 알고리즘에 따라 선택적으로 `Authentication-Info` 에 인증 세션에 관한 추가 정보를 기술해서 응답

### 12.1.3 보안 영역

- 웹 서버는 기밀문서를 보안 영역(realm) 그룹으로 나누고, 보안 영역마다 다른 사용자 권한을 요구
    - 예: 일반 자료, 대외비 자료, 기밀 자료
- `realm` 파라미터가 함께 기술된 기본 인증 예:

    ```
    HTTP/1.0 401 Unauthroized
    WWW-Authenticate: Basic realm="Corporate Financials"
    ```

- realm은 "Corporate Financials(회사 재무)"와 같이 해설 형식(예: 권한 명,서버 호스트명 등)으로 되어, 권한을 가진 사용자에게 권한의 범위 알리는 데 사용

## 12.2 기본 인증

- 기본 인증은 가장 잘 알려진 HTTP 인증 규약으로, 모든 주요 클라이언트/서버에 구현되어있음

### 12.2.1 기본 인증의 예

- 클라이언트가 요청하면 서버는 `WWW-Authenticate` 헤더와 함께 `401 Unauthhroized` 응답 반환
- 브라우저는 응답을 받아, 이름/비밀번호를 요구하는 대화상자를 띄움. 사용자가 입력하면 브라우저는 이를 콜론으로 이어 붙이고, base-64방식으로 인코딩하고, `Authorization` 헤더에 담아 서버로 요청
- 서버는 사용자 이름/비밀번호를 디코딩하여 검사하고, 문제 없으면 `200 OK` 를 보냄
- 인증 요구/응답의 헤더
    - **인증 요구(서버 → 클라이언트)**: realm은 요청 받은 문서의 집합의 이름을 따옴표로 감싼 것으로, 사용자는 이 정보를 보고 어떤 비밀번호를 사용해야 하는지 알 수 있음

        ```
        WWW-Authenticate: Basic realm=<realm>
        // <realm> = 따옴표로 감산 문서 집합 정보
        ```

    - **응답(클라이언트 → 서버)**: 사용자 이름/비밀번호는 콜론으로 잇고, base-64로 인코딩해서 사용자 이름, 비밀번호에 쉽게 국제문자를 포함할 수 있게 하고 네트워크 트래픽에서 사용자 이름/비밀번호가 미노출되게 함

        ```
        Authorization: Basic <credentials>
        // <credentials> = base-64로 인코딩한 사용자이름/비밀번호
        ```

    - `Authentication-Info` 기본인증에서는 미사용

### 12.2.2 Base-64 사용자 이름/비밀번호 인코딩

- **base-64 인코딩**: 8비트 바이트로 이루어져있는 시퀀스를 6비트 덩어리의 시퀀스로 변환.
    - 각 6비트 조각은 대부분 문자, 숫자로 이루어진 특별한 64개 문자 중에서 선택됨
- base-64 인코딩 사용 예시
    - 이름: 'brian-totty', 비밀번호: 'Ow!'
    - → 'brian-totty:Ow!'
    - → 'YnJpYW4tdG90dHk6T3ch'
    - → `Authorization: Basic YnJpYW4tdG90dHk6T3ch`
- base-64 인코딩은 바이너리/텍스트/국제 문자 데이터 문자열을 전송할 수 있도록 전송 가능 문자인 알파벳으로 변환하기 위해 발명
    - 전송 중 변질 위험 없이 디코딩 가능

### 12.2.3 프락시 인증

- 중개 프락시 서버를 통해 인증 가능.
    - 예: 사용자들이 회사내 서버, LAN, 무선 네트워크 접근 전에 프락시 서버를 거치게 해 사용자 인증
- 프락시 인증은 웹 서버의 인증과 헤더와 상태 코드만 다르고 절차는 같음
    - 비인증 상태 코드: 401 ↔ 비인증 상태 코드: 407
    - `WWW-Authenticate` ↔ `Proxy-Authenticate`
    - `Authorization` ↔ `Proxy-Authorization`
    - `Authentication-Info` ↔ `Proxy-Authentication-Info`

## 12.3 기본 인증의 보안 결함

- 보안 결함
    1. 기본 인증의 사용자 이름/비밀번호는 쉽게 디코딩 가능함. HTTP 트랜잭션을 SSL 암호화 채널로 보내거나, 보안이 더 강화된 다이제스트 인증 프로토콜 사용 권장
    2. 보안 비밀번호가 디코딩하기 어려운 방식으로 인코딩되어 있다하더라도, 제 3자가 디코딩된 정보 그대로 다시 원 서버에 보내서 인증에 성공할 수 있음. 기본 인증은 재전송 공격을 예방하지 않음
    3. 기본 인증을 보안이 뚫리더라도 치명적이지 않은 애플리케이션에 사용하더라도, 일반 사용자는 같은 아이디/비밀번호를 사용하는 경향이 있어, 동일한 정보로 중요한 사이트에 사용하여 문제가 발생할 수 있음
    4. 메시지 인증 헤더를 건드리지 않았지만, 다른 부분을 수정해 트랜잭션의 본래 의도를 바꾸는 프락시/중개자가 개입하는 경우, 기본 인증이 정상 작동하지 않을 수 있음
    5. 기본 인증은 가짜 서버 위장에 취약. 사용자가 가짜 서버/가짜 게이트에 그대로 보낼 수 있음. 
- 즉 기본 인증은 우연이나 사고로 정보에 접근해 보는 것을 예방하는 정도로 사용
- 암호화된 데이터 전송(예: SSL)과 함께 연계해서 사용

## 13장은 패스
> 사장된 기술

## 14.1 HTTP를 안전하게 만들기

- 사용자는 웹 트랜잭션을 중요한 일에 사용하므로, 안전한 방식의 HTTP를 필요로 함
- 우리는 다음을 제공해줄 수 있는 HTTP 보안 기술이 필요
    - **서버 인증** - 클라이언트가 위조 서버가 아닌 진짜와 통신 중임을 알 수 있어야 함
    - **클라이언트 인증** - 서버가 가짜가 아닌 진짜 사용자와 통신 중임을 알 수 있어야 함
    - **무결성** - 클라이언트와 서버는 그들의 데이터가 위조 되는 것으로부터 안전해야 함
    - **암호화** - 클라이언트와 서버는 도청에 대한 걱정없이 통신할 수 있어야 함
    - **효율** - 저렴한 클라이언트/서버도 이용가능하도록 알고리즘이 충분히 빨라야 함
    - **편재성(Ubiquity)** - 프로토콜은 거의 모든 클라이언트와 서버에서 지원되어야 함
    - **관리상 확장성** - 누구든지 어디서든 즉각적인 보안 통신을 할 수 있어야 함
    - **적응성** - 현재 알려진 최선의 보안 방법을 지원해야 함
    - **사회적 생존성** - 사회의 문화, 정치적 요구를 만족시켜야 함

### 14.1.1 HTTPS

- HTTPS는 HTTP 를 안전하게 만드는 방식 중에 가장 인기 있는 것으로, 넷스케이프 커뮤니티 주식회사(Netscape Communications Corporation)에서 만들어, 모든 주류 브라우저/서버에서 지원 중
- HTTPS로 접근 여부는 url이 `https://` 로 시작하는 것으로 알 수 있음. (몇 브라우저는 보안 아이콘도 같이 제공)
- HTTPS를 사용하면, 모든 HTTP 요청/응답 데이터는 네트워크로 보내지기 전에 암호화 됨
    - HTTPS는 하부에 전송 레벨 암호 보안 계층을 제공함으로써 동작
    - 이 보안 계층은 안전 소켓 계층(Secure Sockets Layer, SSL) 또는 전송 계층 보안(Transport Layer Security, TLS)을 이용하여 구현
    - SSL과 TLS는 매우 비슷하므로, 엄밀하진 않지만 통칭하여 'SSL'이라고 부름

- 어려운 인코딩/디코딩 작업은 대부분 SSL 라이브러리 안에서 일어나므로, 보안 HTTP를 사용하기위해 웹 클라이언트와 서버가 프로토콜을 처리하는 로직을 변경할 필요는 없음
    - 대부분, TCP 입력/출력 호출을 SSL 호출로 대체하고, 보안 정보 설정, 관리를 위한 몇가지 호출을 추가하면 됨

## 14.2 디지털 암호학

- HTTPS에 대한 배경지식으로 디지털 암호학의 중요 내용을 간략히 설명
- **암호**: 텍스트를 아무나 읽지 못하도록 인코딩하는 알고리즘
- **키**: 암호의 동작을 변경하는 숫자로 된 매개 변수
- **대칭키 암호 체계**: 인코딩/디코딩에 같은 키를 사용하는 알고리즘
- **비대칭키 암호 체계**: 인코딩/디코딩에 다른 키를 사용하는 알고리즘
- **공개키 암호법**: 비밀 메시지를 전달하는 수백만 대의 컴퓨터를 쉽게 만들 수 있는 시스템
- **디지털 서명**: 메시지가 위변조 되지 않았음을 입증하는 체크섬
- **디지털 인증서**: 신뢰할 만한 조직에 의해 서명되고 검증된 신원 확인 정보

### 14.2.1 비밀 코드의 기술과 과학

- 암호법(cryptography): 메시지 인코딩/디코딩에 대한 과학이자 기술
    - 단순 암호화 뿐 아니라, 메시지 위변조 방지에도 사용 가능

### 14.2.2 암호(cipher)

- 암호: 시지를 인코딩하는 특정한 방법과, 디코딩하는 방법.
    - 인코딩 전의 원본 메세지: 텍스트 혹은 평문(plaintext, cleartext)
    - 암호가 적용된 메시지: 암호문(ciphertext)
- 수 천년간 암호는 비밀 메시지를 만들기위해 사용되어왔으며, 율리우스 카이사르는 [카이사르 암호](https://ko.wikipedia.org/wiki/%EC%B9%B4%EC%9D%B4%EC%82%AC%EB%A5%B4_%EC%95%94%ED%98%B8)를 사용했음

### 14.2.3 암호 기계

- 암호는 사람이 직접 인코딩/디코딩을 했어야 해서, 상대적으로 간단한 알고리즘으로 시작함
- 기술이 진보하면서 사람들은 보다 복잡한 암호로 인코딩/디코딩하는 기계를 만들기 시작함
    - 가장 유명한 기계식 암호 기계는 제 2차 세계대전당시 독일의 에니그마 암호 기계

### 14.2.4 키가 있는 암호

- **키**: 암호의 동작방식을 변경할 수 있는 다이얼. 누군가 기계를 훔치더라도, 올바른 키 없이는 디코더가 동작하지 않음
    - 예: N번-회전(rotate-by-N) 암호. 같은 입력 메시지도, 키의 값에 따라 다른 출력을 생성

### 14.2.5 디지털 암호

- 디지털 계산의 도래로 두가지 주요한 발전이 있었음
    - 속도, 기능에 대한 기계 장치의 한계에 벗어나, 복잡한 인코딩/디코딩 알고리즘이 가능해 짐
    - 키를 매우 큰 값으로 지원하는 것이 가능해져서, 단일 암호 알고리즘으로 키 값을 달리해 수 조개 이상의 가상 암호 알고리즘을 만들 수 있음. 키가 길수록 인코딩의 조합이 많아져, 키 무작위 추측에 의한 크래킹이 어려워짐
- **디지털 키**: 물리적인 금속 키, 다이얼 설정과 달리 숫자 값으로, 키 값은 인코딩/디코딩 알고리즘에 대한 입력값
- **코딩 알고리즘**: 데이터를 받아 알고리즘, 키 값에 근거하여 인코딩/디코딩하는 함수

## 14.3 대칭키 암호법

- **대칭키**: 인코딩과 디코딩용 키가 같음.
- 발송자/수신자는 모두 동일한 비밀 키 k를 공유하고, 발송자는 해당 키로 암호화하고, 수신자는 같은 키로 평문으로 복원
- 대칭키 암호 알고리즘의 예: DES, Triple-DES, RC2, RC4

### 14.3.1 키 길이와 열거 공격(Enumeration Attack)

- 비밀 키가 알려 지지 않는 게 중요. 대부분 인코딩/디코딩 알고리즘은 공개적으로 알려져있고, 키만 비밀임.
- 열거 공격(Enumeration Attack): 가능한 모든 키 값을 대입해보는 공격
- 좋은 알고리즘은 코드 크래킹을 하려면 열거 공격밖에 없게 만듦.
    - 가능한 키 값이 별로 없다면 열거 공격으로 암호를 깰 수 있음.
- 키 값의 갯수는 키가 몇 비트이며, 얼마나 많은 키가 유효한지(예: RSA는 소수만 키 가능)에 달려있음
    - 대칭키는 보통 모든 값이 유효하므로 비트에 달려있음. 예: 8bit = 2^8 = 256가지 키 값
    - 40비트면 작고 중요하지 않은 업무에 충분. 워크스테이션이 빠르다면 깨질 수 있음.
    - 128비트면 강력한 것으로 간주.
- 긴 키를 사용하는 암호화 소프트웨어는 NSA(미국 국가 안보국)에 의해 수출을 통제

### 14.3.2 공유키 발급하기

- 대칭키 암호의 단점 중 하나는, 발송자/수신자가 둘 다 공유 키를 가져야 한다는 것.
- 만약 N개의 노드가 있고, 각 노드가 상대 N-1 과 은밀하게 대화를 나누어야 한다면, N^2개의 비밀 키가 필요
    - 너무 많은 키로 인해 관리상에 어려움이 있음

## 14.4 공개키 암호법

- 공개키 암호 방식: 두 개의 비대칭 키 사용
    - 각각 호스트 메시지 인코딩용(공개키)/디코딩용(개인키)
- 인코딩 키는 모두를 위해 공개 되어있고(공개키라는 명칭이 붙은 이유), 호스트만 개인 디코딩 키를 알고 있음
    - 대칭 키처럼 키의 쌍이 폭발적으로 증가하는 것을 피할 수 있음
- 공개키 암호화 기술은 보안 프로토콜을 전 세계 모든 컴퓨터 사용자에게 적용하는 것을 가능케 함

### 14.4.1 RSA

- 공개키 비대칭 암호는 아래의 내용을 알더라도, 개인 키를 계산 할 수 없다는 것
    - 공개키(공개이므로 누구나 얻을 수 있음)
    - 가로채서 얻은 암호문의 일부(네트워크 스누핑으로 획득)
    - 메시지와 그것을 암호화한 암호문(인코더에 텍스트를 넣고 실행해서 획득)
- 이 요구를 만족하는 공개키 암호 체계 중 RSA 알고리즘이 유명.
    - 공개키, 평문 일부, 평문에 대한 암호문, RSA 구현 소스 코드까지 알아도, 암호를 크래킹하여 개인키를 찾아내는 것은 어려움

### 14.4.2 혼성 암호 체계와 세션 키

- 비대칭 공개키 암호 방식은, 누구나 공캐키만 알면 안전하게 메시지를 보낼 수 있고, 개인키에 대한 협상을 할 필요가 없음
- 대신 계산이 느린경향이있어, 실제로는 대칭과 비대칭을 섞은 것을 사용함
- 안전한 소통 채널 수립에는 공개 키를 사용하고, 만들어진 채널을 통해 무작위 대칭키를 생성하여 교환 후 나머지 데이터를 암호화 할때는 빠른 대칭키를 사용하는 방식이 흔히 쓰임

## 14.5 디지털 서명

- 암호 체계는 암호화와 해독 뿐 아니라, 누가 메시지를 썼으며 그 메시지가 위조되지 않음을 증명하기위해 서명하는데에 이용할 수 있음
- 디지털 서명(digital signing)이라고 불리며, 인터넷 보안 인증서에게 중요

### 14.5.1 서명은 암호 체크섬이다

- 디지털 서명은 메시지에 붙은 특별한 암호 체크섬.
    - **메시지 저자 입증**: 저자는 저자의 극비 개인키를 갖고 있어, 저자만이 체크섬을 계산할 수 있음
        - 개인 키가 훔쳐진 것이 아님을 가정하며, 대부분 개인키는 시간이 지나면 만료됨. 훔치거나 훼손된 키를 추적하기위한 '폐기 목록 '도 존재
    - **메시지 위조 방지**: 악의적인 공격자가 송신 중 메시지를 수정하면, 체크섬이 메시지와 다르게 됨. 체크섬은 저자의 비밀 개인키와 관련 되어있어, 위조된 메시지에 대한 올바른 체크섬을 날조할 수 없음
- 디지털 서명은 보통 비대칭 공개키에 의해 생성
    - 개인 키는 오직 소유자만이 알고 있으므로, 일종의 지문 처럼 사용
- 메시지 전송과 서명 진행 방식
    1. 노드 A가 가변 길이의 메시지를 정제하고, 고정된 길이의 요약(digest)로 만듦
    2. 노드 A는 그 요약에, 사용자의 개인키를 매개 변수로 하는 '서명' 함수 적용. 사용자만 개인키를 알고 있으므로, 올바를 서명 함수는 서명자가 소유자임을 의미.
    3. 한번 서명이 계산 되면, 노드A는 그것을 메시지 끝에 붙이고, 메세지와 그에 대한 서명 둘다 노드 B에 전송
    4. 메시지를 받은 노드 B가, 메시지가 저자 입증과 위조 확인을 원하면 서명 검사를 할 수 있음. 노드 B는 서명에 공개 키를 이용한 역함수를 적용함. 풀어낸 요약과 노드B가 갖고 있는 버전의 요약이 일치하지 않았다면, 위조가 되었거나 발송자가 개인키를 갖고 있지 않은 것임(=저자가 아님).

## 14.6 디지털 인증서

- 디지털 인증서(digital certificates, certs): 신뢰할 수 있는 기관으로 부터 보증받은 사용자/회사에 대한 정보를 담음
- 현실 세계에는 많은 형태의 신원 증명이 있지만, 위조하기 어려울수록 높은 신뢰를 받으며, 위조하기 쉬울수록 정보를 덜 신뢰함

### 14.6.1 인증서의 내부

- 인증서 내부에는 '인증 기관'에 의해 디지털 서명된 정보의 집합을 포함
    - 대상의 이름(서버, 사람, 조직 등)
    - 유효기간
    - 인증서 발급자(누가 인증서를 보증하는지)
    - 인증서 발급자의 디지털 서명
    - 추가적으로, 디지털 인증서에 대상과 사용된 서명 알고리즘에 대한 서술적인 정보, 대상의 공개 키를 포함
- 누구나 디지털 인증서를 만들 수 있지만, 모두가 인증서의 정보를 보증하고 인증서를 개인 키로 서명할 수 있는 널리 인정받는 서명 권한을 얻을 수 있지 않음

### 14.6.2  X.509 v3 인증서

- 디지털 인증서에 대한 전 세계적인 단일 표준은 없지만, 대부분의 인증서는 X.509라 불리는 표준화된 서식에 저장하고 있음.
    - X.509 v3 인증서는 인증 정보를 파싱 가능한 필드에 넣어 구조화하는 표준화된 방법 제공
    - 다른 종류의 인증서는 다른 필드값을 갖지만, 대부분 X.509 v3 구조를 따름
- X.509 인증서의 필드
    - **버전**: 이 인증서가 따르는 X.509 인증서의 버전 번호. 요즘은 보통 버전 3
    - **일련번호**: 인증기관에 의해 생성된 고유 정수. CA로부터의 각 인증서는 반드시 고유한 일련번호를 가짐
    - **서명 알고리즘 ID**: 서명을 위해 사용된 알고리즘. (예: "RSA 암호화를 이용한 MD2 요약")
    - **인증서 발급자**: 인증서를 발급하고 서명한 기관 이름. X.500 포맷으로 기록되어 있음.
    - **유효 기간**: 인증서가 유효한 기간. 시작일과 종료일로 정의
    - **대상의 이름**: 인증서에 기술된 사람, 조직같은 엔티티. 이 대상의 이름은 X.500 포맷으로 기록됨
    - **대상의 공개 키 정보**: 인증 대상의 공개 키, 공개 키에 사용된 알고리즘, 추가 매개 변수.
    - **발급자의 고유 ID(optional)**: 발급자의 이름이 겹치는 경우를 대비한, 인증서 발급자에 대한 고유 식별자
    - **대상의 고유 ID(optional)**: 대상의 이름이 겹치는 경우를 대비한, 인증 대상에 대한 고유 식별자
    - 확장: 선택적인 확장 필드의 집합(v3 이상에서 지원).
        - 각 확장 필드는 중요한 것인지 여부가 표시되어있음. 중요한 확장은 인증서 사용자에 의해 반드시 이해되어야 함. 만약 인증서 사용자가 이해하지 못하면, 인증서를 거절해야 함.
        - 흔히 사용되는 확장의 예
            - **기본 제약**: 대상과 인증 기관과의 관계
            - **인증서 정책**: 인증서가 어떤 정책 하에 승인되었는지
            - **키 사용**: 공개키가 어떻게 사용될 수 있는지에 대한 제한
- X.509 기반 인증서에는 웹 서버 인증서, 클라이언트 이메일 인증서, 소프트웨어 코드사인(code-signing) 인증서, 인증기관 인증서를 비롯한 몇가지 변종이 있음

### 14.6.3 서버 인증을 위해 인증서 사용하기

- HTTPS 를 통한 안전한 웹 트랜잭션을 시작할 떄, 최신 브라우저는 자동으로 접속한 서버에서 디지털 인증서를 가져옴.
    - 서버가 인증서가 없으면 보안 커넥션은 실패
- 서버 인증서는 다음을 포함한 많은 필드가 있음
    - 웹 사이트의 이름과 호스트 명
    - 웹 사이트의 공개키
    - 서명 기관의 이름
    - 서명 기관의 서명
- 브라우저가 인증서를 받으면 먼저 서명 기관을 검사
    - 그 기관이 공공이 신뢰할만한 기관이라면, 브라우저는 공개키를 이미 알고 있음(브라우저에 설치된채로 출하).
    - 기관을 모르는 곳이라면, 브라우저는 신뢰해야할지 확신할 수 없으므로, 대개 사용자가 서명 기관을 신뢰하는지 확인하기 위한 대화상자 노출

## 14.7 HTTPS의 세부사항

- HTTPS: HTTP + 대칭, 비대칭 인증서 기반 암호 기법의 집합
    - HTTP의 가장 유명한 버안 보전으로, 널리 구현되었고 주류 상용 브라우저 및 서버에 구현되어있음
    - 이러한 암호 기법들의 집합은 분권화된 글로벌 인터넷 환경에서 HTTPS를 매우 안전한 동시에 유연하게 만들어 줌

### 14.7.1 HTTPS 개요

- HTTPS: 보안 전송 계층을 통해 전송되는 HTTP.
- 암호화되지 않은 HTTP 메시지를 TCP를 통해 보내기 전에 암호화 보안계층으로 보냄
- HTTPS 의 보안계층은 SSL, TLS(현대적 대체품) 으로 구현되었으며, 둘 다 SSL로 통칭할 것임(관행)

### 14.7.2 HTTPS 스킴

- 보안 HTTP는 옵션이므로, 웹 서버로의 요청을 만들 때 웹 서버에게 HTTP의 보안 프로토콜 버전을 수행하는 것을 스킴으로 나타냄
    - 보안이 없는 일반적 HTTP의 URL 스킴 접두사는 `http`
    - 보안이 되는 HTTPS 프로토콜에서 URL 스킴 접두사는 `https`
- 웹 브라우저 등의 클라이언트는 웹 리소스에 대한 트랜잯션 수행 요청을 받으면 URL 스킴을 검사
    - URL이 http 스킴을 갖고 있다면, 클라이언트는 80번(기본 값) 포트로 연결하고 일반 HTTP 명령 전송
    - URL이 https 스킴을 갖고 있다면, 클라이언트는 443번(기본 값)포트로 연결하고, 서버와 바이너리 포맷으로 된 몇몇 SSL 보안 매개 변수를 교환하면서 '핸드셰이크'하고 암호화된 HTTP 명령이 뒤 따름
- SSL 트래픽은 바이너리 프로토콜이므로, HTTP와 완전히 다름
- SSL 트래픽은 HTTP와 다른 포트(보통 443 포트)로 전달되며, 만약 SSL, HTTP 모두 80번으로 도착한다면, 대부분의 웹 브라우저는 SSL 트래픽을 잘못된 HTTP로 해석하고 커넥션을 닫음
    - 보안 서비스가 HTTP 쪽으로 좀 더 계층 통합이 되면 포트가 둘 이상 필요할 이유가 사라지겠지만, 사실 큰 문제를 일으키지는 않음

### 14.7.3 보안 전송 셋업

- HTTPS에서의 절차(SSL 보안 계층때문에 약간 더 복잡):
    1. 클라이언트는 먼저 웹 서버의 443포트로 연결
    2. TCP가 연결되고 나면 클라이언트와 서버는 암호법 매개 변수와 교환키를 협상하면서 SSL 계층 초기화
    3. 핸드셰이크가 완료되면 SSL 초기화가 완료되었으므로, 요청 메시지를 보안 계층에 보내 암호화된 요청을 수 있게 됨
    4. SSL을 통해 암호화된 응답을 전달
    5. SSL 닫힘 통지
    6. TCP 커넥션 닫힘 

### 14.7.4 SSL 핸드셰이크

- 암호화된 HTTP 메시지를 보낼 수 있기 전에 클라이언트&서버는 SSL 핸드셰이크를 해야 함
    - 프로토콜 버전 번호 교환
    - 양쪽이 알고 있는 암호 선택
    - 양쪽의 신원을 인증
    - 채널 암호화하기 위한 임시 세션 키 생성
- 암호화된 HTTP 데이터가 네트워크를 오가기도 전에, SSL은 통신을 시작하기 위한 상당한 양의 핸드셰이크 데이터를 주고 받음
- 핸드셰이크는 복잡하지만 단순화하면 다음과 같음
    1. 클라이언트가 암호 후보를 보내고 인증서를 요구
    2. 서버는 선택된 암호와 인증서 전달
    3. 클라이언트가 비밀정보 전달. 클라이언트와 서버는 키를 만듦
    4. 클라이언트와 서버는 서로에게 암호화를 시작한다고 말함

### 14.7.5 서버 인증서

- SSL는 서버 인증서를 클라이언트로, 다시 클라이언트 인증서를 서버로 날라주는 상호 인증 지원
- 오늘날 클라이언트 인증서는 웹 브라우징에선 흔히 쓰이지 않음
    - 대부분 사용자는 개인 클라이언트 인증서를 갖고 있지않고, 웹 서버도 클라이언트 인증서를 요구할 수 있지만 잘 하지 않음
    - 몇몇 회사 설정에서 웹 브라우징을, 보안 이메일, 인트라넷 직원 정보 접근 제어용으로 사용
- 한편 보안 HTTPS 트랜잭션은 항상 서버 인증서를 요구
- 서버 인증서는 조직 이름, 주소, **서버 DNS 도메인 이름**, 그 외 정보를 보여주는 X.509 v3 에서 파생된 인증서
    - 사용자 및 클라이언트 소프트웨어는 인증서를 검증할 수 있음

### 14.7.6 사이트 인증 검사

- SSL 자체는 웹 서버 인증서 검증을 요구하지 않지만, 최신 웹 브라우저는 대부분 간단하게 기본 적 검사를 하고, 그 결과를 더 철저한 검사를 할 수 있는 법과 함께 사용자에게 안내
- 대부분의 웹 브라우저 검사 기법의 기초를 구축한 넷스케이프의 웹 서버 인증 검사 알고리즘 수행 단계는 4가지가 있음

***날짜 검사***

- 브라우저는 인증서가 유효함을 확인하기위해 인증서 시작/종료 일을 검사. 만료되었거나 활성화되지 않았다면 검사가 실패하고 에러를 표시

***서명자 신뢰도 검사***

- 모든 인증서는 서버를 보증하는 인증기관(Certificate Authority, CA)에 의해 서명되어있음
    - 인증서에는 여러 수준이 있으며, 각각은 다른 수준의 배경 검증을 요구
    - 예: 전자상거래 서버 인증서를 발급받고 싶다면, 사업체로서의 법인에 대한 법적 증명 제시
- 누구나 인증서 생성 가능하지만, 몇몇 CA는 잘 알려지고, 인증서 지원자의 신원과 사업을 입증하는 알기 쉬운 절차를 갖고 있음
    - 이러한 이유로 브라우저는 신뢰할만한 서명 기관의 목록을 포함한 채로 배포됨.
- 브라우저가 알려지지 않은(혹은 악의적일 수 있는) 인증기관으로 부터 서명된 인증서를 받으면 경고 노출
- 또한 브라우저는 신뢰할만한 CA가 간접적으로 서명한 인증서를 받아들일 수 있음.
    - 예: 신뢰할만한 CA가 '샘의 서명 가게'를 위한 인증서에 서명하고 '샘의 서명 가게'가 어떤 사이트 인증서에 서명한다면, 브라우저는 그 인증서를 올바른 CA 경로에서 파생 된 것으로 보고 받아들임

***서명 검사***

- 서명 기관이 믿을만 하다고 판단하면, 브라우저는 서명 기관의 공개키를 서명에 적용하여 그 체크섬을 비교하여 무결성을 검사

***사이트 신원 검사***

- 서버가 다른 이의 인증서 복사, 트래픽 가로채기 하는 것을 방지하기 위해 대부분의 브라우저는 인증서의 도메인 이름이 대화중인 서버의 도메인 이름과 비교하여 맞는지 검사
- 서버 인증서에는 보통 단일 도메인 이름이 들어있지만, 몇 CA는 서버 클러스터나 서버 팜을 위해 서버 이름 목록, 서버 이름들에 대한 와일드카드 표현이 들어있는 인증서를 만듦
- 호스트명이 인증서 신원가 맞지 않는다면, 사용자에게 이 사실을 알리거나 잘못된 인증서 에러와 함께 커넥션을 종료

### 14.7.7. 가상 호스팅과 인증서

- 가상 호스트(하나의 서버에 여러 호스트명)로 운영되는 사이트의 보안 트래픽 및 인증서 관리은 까다로움
    - 몇 웹 서버 프로그램은 하나의 인증서만 지원함
    - 만약 사용자가 인증서의 이름과 정확히 맞지 않는 가상 호스트명에 도착하면 경고상자가 나타남
- 이러한 문제를 피하기 위해, 보안 트랜잭션을 시작하는 사용자를 인증서에 나타나는 호스트 명으로 리다이렉트함.
    - 예: `Cajun-Shop.com` 이 있을 때, 호스팅 제공자는 `cajun-Shop.scuresites.com` 을 제공. 사용자가 `https://www.cajun-shop.com` 으로 접속시에 호스트명이 달라 경고가 나타나므로, `cajun-Shop.scuresites.com` 으로 리다이렉트

## 14.8 진짜 HTTPS 클라이언트

- SSL은 복잡한 바이너리 프로토콜이지만, 몇 가지 SSL 클라이언트와 서버 프로그래밍을 쉽게 만들어주는 상용/오픈 소스 라이브러리가 있음

### 14.8.1 OpenSSL

- **[OpenSSL](https://www.openssl.org/)** 은 SSL과 TLS 의 가장 인기 있는 오픈 소스 구현
- 강력한 다목적 암호법 라이브러리인 동시에 SSL, TLS 프로토콜을 구현한 강건하고 완전한 기능을 갖춘 상용 수준의 툴킷을 개발하고자 한 자원봉사자의 협업 결과물

### 14.8.2 간단한 HTTPS 클라이언트

- 기본적인 HTTPS 클라이언트를 작성하기위해 OpenSSL을 이용한 예제 (책 참조)
    - 이 예제에서는 서버와 SSL 커넥션을 맺고, 그 사이트 서버로부터 가져온 신원 정보를 출력하고, HTTP GET 요청을 보안 채널로 보내고, HTTP 응답을 받아 출력함
    - 에러 처리와 인증서 처리 로직은 제외됨

### 14.8.3 우리의 단순한 OpenSSL 클라이언트 실행하기

- 14.8.2 에서 작성한 코드를 실행시켜 봄 (책 참조)

## 14.9 프락시를 통한 보안 트래픽 터널링

- 클라이언트는 종종 대신 웹 서버에 접근해주는 웹 프락시를 사용
- 그러나 클라이언트가 서버로 보낼 데이터를 서버의 공개키로 암호화하기 시작하면, 프락시는 HTTP 헤더를 읽을 수 없음
    - 프락시가 HTTP 헤더를 못읽으면, 요청을 어디에 보낼지 알 수 없음
- HTTPS와 프락시가 잘 동작하도록 어디에 접속하려고 하는지 알려 주기 위한 방법 중, SSL 터널링 프로토콜이 유명
    - 클라이언트는  프락시에게 자기가 연결하고자 하는 안전한 호스트와 포트를 암호화 전에 평문으로 전달
    - HTTP는 CONNECT라는 새 확장 메서드를 이용해 이 평문으로된 정보를 프락시에게 전송
    - 완료되면 클라이언트와 서버 사이에 데이터가 직접 오갈 수 있게해주는 터널 생성
- CONNECT 메서드는 원 서버의 호스트명, 포트를 구분된 형태로 제공하는 한줄로 된 텍스트 명령
    - `호스트:포트` 뒤에는 스페이스 하나와 HTTP 버전 문자열, CRLF, 0 개이상의 HTTP 요청 헤더줄, 빈줄이 차례로 옴
    - 이후 핸드셰이크가 성공하면 SSL 데이터를 포함한

    ```
    CONNECTT home.netscape.com:443 HTTP/1.0
    User-agent: Mozilla/1.1N

    <SSL로 암호화된 데이터가 이 다음에 옴>
    ```

- 프락시는 요청을 평가하여 그것이 유효하고 사용자가 그러한 커넥션을 요청할 수 있도록 허가 받았는지 확인함. 만약 모든 것이 적법하다면 프락시는 목적지 서버로 연결하고 성공하면 `200 Connection Established` 응답을 클라이트에 제공

- HTTP는 메시지가 바르게 수송, 식별, 추출, 처리되는 것을 보장하기 위해 잘 라벨링 된 엔터티를 사용
    - 객체는 (`Content-Type` 미디어 포맷과, `Content-Language` 헤더를 이용해) 올바르게 식별되므로, 브라우저나 다른 클라이언트가 콘텐츠를 바르게 처리
    - 객체는 올바르게 압축이 풀림(`Content-Length`, `Content-Encoding` 헤더 이용)
    - 객체는 항상 최신(엔터티 검사기와 캐시 만료 제어 이용)
    - 사용자의 요구 만족(`Accept` 관련 헤더에 기반하여 내용 협상)
    - 네트워크 사이를 빠르고 효율적으로 이동(범위 요청, 델타 인코딩, 그 외 데이터 압축 이용)
    - 조작되지 않고 온전하게 도착(전송 인코딩 헤더와 `Content-MD5` 체크섬 이용)

## 15.1 메시지는 컨테이너, 엔터티는 화물

- HTTP 메시지 = 컨테이너, HTTP 엔터티 = 화물

```
HTTP/1.0 200 OK
Server: Netscape-Enterprise/3.6
Date: Sun, 17 Sep 2000 00:01:05 GMT
Content-type: text/plain
Content-length: 18

Hi! I'm a message!
```

- 엔터티 헤더 2개: `Content-Length: 18`,  `Content-Type: text/plain`
    - → 18자인 플레인 텍스트 문서를 의미
- 빈 줄 (CRLF)은 헤더 필드와 본문의 시작을 나눔

***엔터티 헤더***

- `Content-Type`: 엔터티에 의해 전달된 객체의 종류
- `Content-Length`: 전달된 메시지의 길이나 크기
- `Content-Language`: 전달된 객체와 가장 잘 대응되는 자연어
- `Content-Encoding`: 객체 데이터에 대해 행해진 변형(압축 등)
- `Content-Location`: 요청 시점을 기준으로, 객체의 또 다른 위치
- `Content-Range`: 만약 이 엔터티가 부분 엔터티라면, 이 헤더는 이 엔터티가 전체에서 어느 부분에 해당하는지 정의
- `Content-MD5`: 엔터티 본문의 콘텐츠에 대한 체크섬
- `Last-Modified`: 서버에서 이 콘텐츠가 생성/수정된 날
- `Expires`: 엔터티 데이터가 더 이상 신선하지 않은 것으로 간주되기 시작는 날짜와 시각
- `Allow`: 리소스에 대해 어떤 요청 메서드가 허용되는지(GET, HEAD 등)
- `ETag`: 인스턴스에 대한 고유한 검사기. 엄밀히 말하면 엔터티 헤더는 아니지만, 엔터티와 관련된 중요 헤더
- `Cache-Control`: 어떻게 이 문서가 캐시될 수 있는지에 대한 지시자. 위와 동일하게 엔터티 헤더는 아님.

### 15.1.1 엔터티 본문

- 엔터티 본문 = 가공되지 않은 데이터, 헤더 = 그 외 다른 정보
    - 엔터티 헤더는 본문의 날 데이터의 의미에 대해 설명할 필요가 있음
        - 예: `Content-Type` → 데이터 해석 방법, `Content-Encoding` → 압축 및 인코딩 여부
    - 엔터티 본문은 타입, 압축여부, 언어와 별개로 CRLF 바로 다음에 위치

## 15.2 Content-Length: 엔터티의 길이

- `Content-Length` 는 본문의 크기를 바이트 단위로 나타냄
    - 인코딩 종류 및 여부와 무관하며, 압축되었다면 압축 후 크기를 나타냄
    - 엔터티를 포함한 메시지에 필수(메세지를 청크 인코딩으로 전송하는 경우 제외)
    - 서버 충돌로 메시지가 잘렸는지 감지, 지속 커넥션을 공유하는 메시지를 바르게 분할 할 때 필요

### 15.2.1 잘림 검출

- 구 버전은 HTTP 커넥션을 닫힌 것으로 메시지가 끝났음을 인지.
    - 정상 닫힘과 서버 충돌을 구분 못함 → 즉 메시지 잘림 검출을 위해 `Content-Length` 필요
- 메시지 잘림은 캐싱 프락시서버에 특히 취약
    - 잘린 메시지를 수신했으나 잘린 것을 모른다면 결함이 있는 콘텐츠를 저장하고 계속해서 제공
    - 잘린 메시지를 캐싱하는 위험을 줄이기 위해, 명시적으로 `Content-Length` 가 없으면 캐싱하지 않음

### 15.2.2 잘못된 Content-Length

- `Content-Length` 가 잘못된 값을 담고 있으면 없는 것보다 큰 피해 유발
    - 몇 클라이언트/서버/프락시는 오동작 탐지하고 교정을 시도
    - 공식적으로 HTTP/1.1은 사용자 에이전트가 잘못된 길이를 받게된 것을 알았을 때 사용자에게 안내함

### 15.2.3 Content-Length와 지속 커넥션(Persistent Connection)

- `Content-Length` 는 지속 커넥션을 위해 필수
    - 지속 커넥션을 통해 응답이 온다면 다른 응답이 뒤따르므로, 메세지의 끝과 다음의 시작이 필요
    - 15.6절의 청크인코딩 사용 시에는 `Content-Length` 헤더가 없는 지속 커넥션이 발생

### 15.2.4 콘텐츠 인코딩

- HTTP는 보안 강화하거나 압축을 통해 공간을 절약하도록, 엔터티 본문 인코딩을 허용.
    - 본문 콘텐츠가 인코딩되어 있다면, `Content-Length` 는 인코딩된 본문 길이를 정의
    - 인코딩 전 크기를 보내면, 특히 지속 커넥션일 때 심각한 오류 발생.
    - HTTP/1.1 명세에는 인코딩 전 길이를 보내기 위한 스펙은 없고, 때문에 디코딩 중 문제 발생시 검증을 하기 어려움

### 15.2.5 엔터티 본문 길이 판별을 위한 규칙

- 엔터티 본문의 길이, 끝나는 위치 판별 규칙. 순서대로 적용 되어야 함
    1. 본문을 갖을 수 없는 특정 타입의 HTTP 메시지에서는(예: HEAD), `Content-Length` 헤더 무시
        - 이 경우 해당 헤더는 실제 본문길이를 서술하지 않는 부가 정보에 불과
        - 만약 해당 헤더가 있으면 ,언제나 본문이 있다고 가정해서 만들면 문제 발생
    2. 메시지가 `Transfer-Encoding` 헤더를 포함하고 있다면(기본 HTTP 인코딩, 'identity'이 아니라면), 메시지가 커넥션이 닫혀서 먼저 끝나지 않는 이상 엔터티는 '0 바이트 청크'라고 불리는 특별한 패턴으로 끝나야 함. 
    3. 1과 2에 해당하지 않고, 메시지에 `Content-Length` 가 있다면 이 헤더는 본문의 길이를 담음
        - 2에 해당한다면 `Content-Length` 헤더를 무시해야함.
    4. 위 규칙에 해당 되지 않는다면, 엔터티는 커넥션이 닫힐 때 끝남
        - 서버가 응답을 되돌려줘야하므로 커넥션은 서버만 닫을 수 있음
    5. 엔터티 본문이 있는 HTTP/1.1 요청은 유효한 `Content-Length` 를 가져야 함 

## 15.3 엔터티 요약

- 엔터티 본문에 대한 의도치 않은 변경을 감지 하기 위해, 최초 엔터티가 생성될 때 송신자는 데이터에 대한 체크섬을 생성할 수 있고, 수신자는 그 체크섬으로 검사 가능
- `Content-MD5` 헤더는 서버가 엔터티 본문에 MD5 알고리즘을 적용한 결과를 보내기 위해 사용
    - 콘텐츠 인코딩의 적용 후, 전송 인코딩 적용 전 엔터티 본문에 대한 MD5 값
    - 종단간 무결성 검증을 위해, 응답을 처음 만든 헤더만이 계산하여 헤더를 추가하고, 프락시/캐시는 건드리지 않음
    - 무결성 검증시, 클라이언트는 전송 인코딩 디코딩 후 엔터티 본문에 대한 MD5계산 하여 비교
- 실제로 `Content-MD5` 는 자주 사용되지 않으며, 여러 다른 요약 알고리즘이 있음
    - 클라이언트가 응답에 대해 기대하는 요약 유형을 정의할 수 있는 `Want-Digest` 를 제안하였음

## 15.4 미디어 타입과 차셋(Charset)

- `Content-Type` 헤더: 엔터티 본문의 MIME 타입을 기술. 클라이언트가 콘텐츠를 적절히 해석/처리하기 위해 사용.
    - 콘텐츠 인코딩 전의 엔터티 본문 유형 명시
    - HEAD 요청인 경우에는, GET이였을 때 발송할 타입을 알려줌
- [MIME 타입](https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/MIME_types): 전달되는 데이터 매체의 기저 형식의 표준화된 이름
    - `text/html`: HTML 문서
    - `text/plain`: 플레인 텍스트 문서
    - `image/gif`: GIF 이미지
    - `image/jpeg`: JPEG 이미지
    - `audio/x-wav`: WAV 음향 데이터
    - `application/vnd.ms-powerpoint`: 마이크로소프트 파워포인트 프레젠테이션

### 15.4.1 텍스트 매체를 위한 문자 인코딩

- `Content-Type` 헤더는 엔터티 비트 집합을 텍스트 파일의 글자들로 변환하기 위한 선택적인 'charset' 매개변수를 지원

```
Content-Type:text/html; charset=iso-8859-4
```

### 15.4.2 멀티파트 미디어 타입

- MIME "멀티파트" 이메일 메시지는 서로 붙어있는 여러 개의 메시지를 포함해 하나의 복합 메시지로 보내짐.
    - 각 구성요소는 자신에 대해 서술하는 헤더를 포함.
    - 여러 구성요소들이 이어져있고, 문자열 하나로 서로의 경계 식별
- HTTP는 멀티 파트본문도 지원. 일반적으로 폼을 채워서 제출시, 문서의 일부분을 실어나르는 범위 응답시 에만 사용됨

### 15.4.3 멀티 파트 폼 제출

- HTTP 폼을 채워 제출시, 가변 길이 텍스트 필드와 업로드 될 객체는 각각 멀티파트 본문을 구성하는 하나의 파트가 되어 전송됨.
    - 멀티파트 본문은 여러 다른 종류, 다른 길이의 값으로 채워진 폼 허용
    - [boundary:](https://developer.mozilla.org/ko/docs/Web/HTTP/Methods/POST) 본문의 서로 다른 부분을 구분하기 위한 구분자.

```
Content-Type: multipart/form-data; boundary=AaB93x
--AaB93x
Content-Disposition: form-data; name="field1"
value1
--AaB93x
Content-Disposition: form-data; name="files"; filename="example.txt"
...contents of example.txt...
--AaB93x--
```

### 15.4.4 멀티파트 범위 응답

- 범위 요청에 대한 응답 또한 멀티파트가 될 수 있음.
    - `Content-Type: multipart/byteranges` 헤더 및 각각 다른 범위를 담고 있는 멀티 파트 본문 포함

## 15.5 콘텐츠 인코딩

- 압축, 암호화를 위해 콘텐츠 전송 전에 인코딩.
    - 발송하는 쪽에서 콘텐츠에 적용하며, 인코딩이 끝난 데이터는 엔터티 본문에 담아 수신자에게 전송

### 15.5.1 콘텐츠의 인코딩 과정

1. 웹 서버가 원본 `Content-Type` 과 `Content-Length` 헤더를 수반한 원본 응답 메시지 생성
2. 콘텐츠 인코딩 서버(원서버 혹은 다운스트림 프락시)가 인코딩 된 메시지 생성
    - `Content-Type` 은 같지만 압축되었다면 `Content-Length` 는 다름
    - `Contetnt-Encoding` 헤더를 인코딩된 메시지에 추가하여 수신에서 디코딩할 수 있도록 함
3. 수신 측 프로그램은 인코딩 된 메시지를 받아 디코딩 후 원본을 얻음 

### 15.5.2 콘텐츠 인코딩 유형

- HTTP는 표준 인코딩을 정의하고, 확장 인코딩으로 인코딩 추가도 허용
    - `Contetnt-Encoding` 헤더는 표준화된 인코딩 토큰 값을 이용해, 인코딩에 사용된 알고리즘 기술
- [콘텐츠 인코딩 토큰](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Content-Encoding) 목록
    - `gzip`: GNU zip 인코딩이 적용되었음
    - `compress`: 유닉스 파일 압축 프로그램인 'compress'가 실행되었음
    - `deflate`: zlib 포맷으로 압축되었음
    - `identity`: 어떤 인코딩도 수행되지 않았음. `Contetnt-Encoding` 가 없다면 이 값으로 간주.
- `gzip`, `compress`, `deflate` 는 무손실 압축 알고리즘이고, 이 중 `gzip` 이 일반적으로 효율적이고 가장 널리 쓰임

### 15.5.3 Accept-Encoding 헤더

- 서버에서 클라이언트 미지원 인코딩 사용하는 것을 방지하기 위해, 클라이언트는 `Accept-Encoding` 헤더를 이용하여 지원하는 인코딩 목록을 전달
    - 해당 헤더를 미포함 한다면, 어떤 인코딩도 받아드릴 수 있는 것으로 간주. = `Accept-Encoding: *`
    - 각 인코딩에 Q(quality) 값을 매개변수로 하여 선호도를 추가할 수 있음. 0.0(비선호)~1.0(선호)

```
Accept-Encoding: compress, gzip
Accept-Encoding:
Accept-Encoding: *
Accept-Encoding: compress;q=0.5, gzip;q=1.0
```

## 15.6 전송 인코딩과 청크 인코딩

- 콘텐츠 인코딩
    - 메시지의 엔터티 부분만 인코딩
    - 콘텐츠 포맷은 긴밀하게 연관되어있음
        - 예: 텍스트파일은 gzip 압축을 하지만, JPEG는 gzip으로 잘 압축되지 않으므로 하지 않음
- 전송 인코딩
    - 전체 메시지에 대해 적용되어 메시지 구조 자체를 변경.
    - 콘텐츠 포맷과 독립적

### 15.6.1 안전한 전송

- 전송 인코딩은 다른 프로토콜에서도 '안전한 전송'을 위해 존재했지만, HTTP는 '안전한 전송'의 초점을 다른 데 맞추고 있음
- HTTP에서 전송된 메시지 본문이 문제를 일으키는 이유
    1. 알 수 없는 크기
        - 몇 게이트웨이 애플리케이션, 콘텐츠 인코더는 콘텐츠 생성 전에 최종 크기 판단 불가하며 사이즈 알기 전에 데이터 전송을 시작
        - HTTP는 데이터에 앞서 `Content-Length` 헤더를 요구하므로, 몇 서버는 데이터의 끝을 알리는 종결 꼬리말을 포함시켜 전송 인코딩으로 보내려 시도함
    2. 보안
        - 이미 SSL과 같은 유명한 전송 계층 보안 방식이 있어 전송 인코딩 보안은 흔하지 않음

### 15.6.2 Transfer-Encoding 헤더

- 전송 인코딩 제어, 서술을 위해 정의 된 헤더
    - `Transfer-Encoding`: 안전한 전송을 위해 어떤 인코딩이 메시지에 적용되었는지 응답 헤더에 사용
    - `TE`: 어떤 확장된 인코딩을 사용할 수 있는지 서버에게 알려주기 위해 요청 헤더에 사용

```
GET /new_product.html HTTP/1.1
Host: www.joes-hardware.com
User-Agent: Mozilla/4.61 [en] (WinNT; I)
TE: trailers, chunked
...

HTTP/1.1 200 OK
Trasfer-Encoding: chunked
Server: Apache/3.0
...
```

### 15.6.3 청크 인코딩

- 청크 인코딩은 메시지를 일정 크기의 청크 여럿으로 쪼개, 순차적으로 보냄
    - 청크 인코딩을 사용하면 메시지 전송 전에 전체 크기를 알 필요가 없어짐
    - 본문이 동적으로 생성됨에 따라, 서버는 그 중 일부를 버퍼에 담은 뒤 그 청크를 그것의 크기와 함께 전송
    - 본문 전체를 모두 보낼 때까지 반복
- 청크 인코딩은 전송 인코딩의 한 형태이며, 즉 본문이 아닌 메시지의 속성임
    - 멀티파트 인코딩은 본문의 속성이므로 청크 인코딩과 완전히 분리되어있음

***청크와 지속 커넥션***

- 커넥션이 지속적이지 않다면, 클라언트는 서버가 커넥션을 닫을 때까지 본문으로 간주하므로, 본문의 크기를 알 필요가 없음
- 청크 인코딩은 서버가 본문을 여러 청크로 쪼개서 보낼 수 있게 해줌으로써, 동적으로 콘텐츠를 생성해 본문 크기를 알아내지 못하는 상황에 대한 해법 제공
    - 본문 전체를 모두 보낼 때까지 청크를 보내고, 마지막으로 크기가 0인 청크로 본문이 끝났음을 알림
- 클라이언트도 청크 인코딩된 데이터를 서버로 전송할 수 있으며, 해당 인코딩을 받아들일지 모르기때문에 `411 Length Required` 응답으로 거절당하는 것을 대비해야 함

***청크 인코딩된 메시지의 트레일러***

- 하나 이상의 조건을 만족하면 청크 메시지에 트레일러 추가 가능
    - 클라이언트의 `TE` 헤더가 트레일러를 받아들일 수 있음을 나타내는 경우
    - 트레일러가 응답을 만든 서버에 의해 추가되었고, 그 트레일러의 콘텐츠는 클라이언트가 이해하고 사용할 필요가 없는 선택적 메타데이터이므로 클라이언트가 버려도 되는 경우.
- 트레일러에는 본문의 콘텐츠가 먼저 생성되어야하는 등의 이유로 메시지 시작 시점에서 값을 알 수 없는 추가적인 헤더 필드를 담을 수 있음
    - `Transfer-Encoding`, `Trailer`, `Content-Length` 를 제외한 모든 헤더가 가능하며, 대표적으로 `Content-MD5` 헤더가 있음

### 15.6.4 콘텐츠와 전송 인코딩의 조합

- 콘텐츠 인코딩, 전송 인코딩은 동시에 사용 가능
    - 콘텐츠 인코딩 → 전송 인코딩(청크 인코딩) → 전송

### 15.6.5 전송 인코딩 규칙

- 전송 인코딩 집합은 반드시 'chunked'를 포함. 유일한 예외는 메시지가 커넥션의 종료로 끝나는 경우
- 청크 인코딩이 사용되었다면, 메시지 본문에 적용된 마지막 전송 인코딩이 존재해야 함
- 청크 전송 인코딩은 반드시 메시지 본문에 한 번 이상 적용되어야 함

## 15.7 시간에 따라 바뀌는 인스턴스

- 웹 객체는 동적이며, URL은 시간에 따라 다른 버전의 객체를 가리킬 수 있음
- 인스턴스 조작(instance manipulatiton):
    - HTTP 프로토콜은 어떤 특정 종류의 요청, 응답을 다루는 방법을 정의
    - 대표적인 것은 범위 요청과 델타 인코딩으로, 클라이언트가 자신이 갖고 있는 리소스 사본이 서버와 정확히 같은지 판단하고, 상황에 따라 새 인스턴스를 요청하는 능력을 가질 것을 요구.

## 15.8 검사기와 신선도

- 조건부 요청이라고 불리는 특별한 요청은, 클라이언트가 서버에게 자신이 갖고 있는 버전을 말해주고, 검사기를 통해 자신의 사본 버전이 더 이상 유효하지 않을 때만 사본을 보내달라고 요청.

### 15.8.1 신선도

- 서버는 `Expires` 나 `Cache-Control` 헤더를 통해 캐시 신선도 정보 제공
    - `Expires`: 문서가 만료되어 신선하다고 간주할 수 없게 되는 정확한 날짜
    - `Cache-Control`: 문서의 최대 수명을 문서가 서버를 떠난 후로부터의 총 시간(초 단위
- `Cache-Control` 헤더에 동반될 수 있는 지시자. [참고 링크](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Cache-Control)
    - `no-cache`: [요청] 서버와의 최초 재검사 없이는 문서의 캐시된 사본을 반환 하지 않음
    - `no-store` : [요청] 문서의 캐시된 사본을 반환하지 않음. 서버로부터의 응답을 저장 하지 않음
    - `max-age`: [요청] 캐시의 문서는 명시한 나이보다 오래되어서는 안 됨
    - `max-state`: [요청] 신선하지 않게 된 뒤로, 이 지시자로 지정한 시간보다 크지 않다면 받아들임
    - `min-fresh`: [요청] 응답은 지정한 값만큼은 신선해야 함.
    - `no-transform`: [요청] 문서는 보내기 전에 변형되어서는 안됨
    - `only-if-cached`: [요청] 서버에 접근하지말고, 캐시에 들어있는 경우에만 문서 전송 요청
    - `public`: [응답] 응답은 어떤 캐시로든 캐시됨
    - `private`: [응답] 하나의 클라이언트만 접근할 수 있는 형태로 캐시됨
    - `no-cache`: [응답] 지시자가 헤더 필드의 목록을 동반하고 있다면, 콘텐츠는 캐시될 수 있지만 그 전에 헤더 필드가 반드시 제거 되어야 함
    - `no-store`: [응답] 절대로 캐시되어서는 안됨
    - `no-transform`: [응답] 제공되기 전에 어떤 식으로든 수정되어서는 안됨
    - `must-revalidate`: [응답] 제공되기 전에 반드시 서버를 통해 재검사
    - `proxy-revalidate` : [응답] 공유된 캐시는 반드시 응답을 원 서버를 통해 재검사해야 함. 개인 캐시에 의해 무시될 수 있음
    - `max-age`: [응답] 문서가 캐시될 수 있고 신선하다고 간주되는 시간의 최대 길이
    - `s-max-age`  [응답] 공유된 캐시에 적용될 수 있는 문서의 최대 수명을 정의. `max-age` 값을 덮어씀.  개인 캐시에 의해 무시될 수 있음

### 15.8.2 조건부 요청과 검사기

- HTTP 클라이언트에게 리소스가 바뀐 경우에만 사본을 요청하는 조건부 요청이라는 특별한 요청을 할 수 있는 방법 제공
    - `If-` 로 시작하는 조건부 헤더에 의해 구현되며, 참일 때 수행되고 아니면 서버는 HTTP 에러 코드 반환
- 각 조건부 요청은 특정 검사기 위에 동작하며, 검사기는 문서의 테스트된 특정 속성임
    - 예) 일련번호, 버전번호, 문서의 최종 변경일 등
    - `If-Modified-Since` 상태 헤더는 문서의 인스턴스의 마지막 수정된 날짜를 검사하므로, 마지막 수정된 날짜를 검사기라고 할 수 있음.
- 조건부 헤더와 검사기 유형
    - `If-Modified-Since` ⇒ `Last-Modified`: Last-Modified 응답 헤더의 시간 이후로 변한 것이 있다면, 리소스 사본을 반환
    - `If-Unmodified-Since` ⇒ `Last-Modified`: Last-Modified 응답 헤더의 시간 이후로 변한 것이 없다면, 리소스 사본을 반환
    - `If-Match` ⇒ `ETag`: ETag 응답 헤더와 엔터티 태그가 같다면 그 리소스의 사본 반환
    - `If-None-Match` ⇒ `ETag`: ETag 응답 헤더와 엔터티 태그가 다르다면 그 리소스의 사본 반환
- 검사기에는 강한 검사기와 약한 검사기가 있음
    - 약한 검사기: 리소스의 인스턴스를 고유하게 식별하지(uniquely identify) 못하는 경우도 있음
        - 예: 객체의 바이트 단위 크기, 최정 변경 시각(정확도가 최대 1초라서)
    - 강한 검사기: 언제나 고유하게 식별
        - 예: 콘텐츠에 대한 암호 체크섬, ETag
- 클라이언트와 서버는 필요에 따라 엔터티 태그 검사를 통과하지 못한 버전을 채택하기도 함
    - 태그 앞에 'W/'를 붙임으로써 약한 엔터티 태그임을 알려 사소한 변화는 통과하지 않아도 사용할 수 있음
    - 강한 엔터티 태그라면 아무리 사소하게 바뀌었더라도 변경됨

## 15.9 범위 요청

- HTTP는 더 나아가, 클라이언트가 문서의 일부분이나 특정 범위만 요청할 수 있도록 해줌
    - 예: 소프트웨어의 3/4를 다운로드 완료후 실패했을 떄, 엔터티 일부를 요청함으로써 다운로드 재개

```
GET /bigfile.html HTTP/1.1 
Host: www.joes-hardware.com
Range: bytes=4000-  //← 4000바이트 이후 부분 요청
...
```

- `Range` 헤더는 여러 범위로 요청하기 위해 사용 될 수 있음
    - 각 범위는 순서 없이 정해지며, 겹칠 수 있음
- 서버는 클라이언트에게 범위를 받아들일 수 있는지 응답에 `Accept-Range` 헤더를 포함시켜 안내
    - 모든 서버가 범위 요청을 받아들일 수 있는 것은 아니지만 많은 경우 가능

```
HTTP/1.1 200 OK
Date: Fri, 05 Nov 1999 22:35:15 GMT
Server: Apache/1.2.4
Accept-Ranges: bytes
```

- 범위 요청은 어떤 객체의 특정 인스턴스들에 대한 클라이언트↔서버 사이의 정보 교환이므로 일종의 인스턴스 조작
- 엔터티 범위 요청
    1. 클라이언트 요청 메시지
    2. 서버 응답메시지 (`Accept-Range` 헤더 포함)
    3. 범위 요청 메시지(`Range` 헤더 포함)
    4. 범위 응답 메시지(`Content-Range`, `Accept-Range` 헤더 포함)

## 15.10 델타 인코딩

- 웹 페이지의 각기 다른 버전은 그 페이지에 대한 각기 다른 인스턴스
    - 어떤 페이지의 만료된 사본을 갖고 있으면, 새 인스턴스를 요청하는데 일부분만 변경되었더라도 전체 인스턴스를 받아야 함
    - 페이지에 대한 클라이언트 사본에 대해 변경된 부분을 보내 페이지를 더 빨리 얻게 할 수 있음
- 델타 인코딩: 객체 전체가 아닌 변경된 부분에 대해서만 통신하여 전송량을 최적화하는 HTTP 프로토콜 확장. [RFC 3329](https://tools.ietf.org/html/rfc3329)에서 기술.
    - 어떤 객체의 특정 인스턴스들에 대한 클라이언트↔서버 사이의 정보 교환에 의존하므로 일종의 인스턴스 조작
- 델타 인코딩의 메커니즘
    - 클라이언트는 페이지의 어떤 버전을 가지고 있는지 서버에게 전송. 이는 델타를 받아들일 의사가 있음을 의미. 현재 버전에 델타를 적용하기 위해 어떤 알고리즘을 알고 있는지 같이 전송
        - 클라이언트는 갖고 있는 버전에 대한 유일 식별자를 담아 `If-None-Match` 헤더만 보내면, 전체 버전을 받게 되므로 `A-IM` (Accept-Instance-Manipulation) 헤더를 보내 델타를 받아들일 수 있음을 알려줄 수 있음.
    - 서버는 자신이 클라이언트가 갖고 있는 버전을 가지고 있는지, 그리고 어떻게 최신 버전과 클라이언트 버전 사이의 델타를 계산할 것인지(여러 알고리즘 존재) 체크
    - 서버는 계산한 델타, 계산시 사용한 알고리즘, 델타를 계산한 기반 문서의 ETag를 클라이언트에 `226 IM Used` 상태 코드와 전송. 페이지 최신 버전에 대한 새 식별자(`ETag`) 명시
        - `IM` 헤더에 델타 계산 알고리즘 명시
        - `Delta-Base` 헤더에 델타를 계산한 기반 문서(이상적으로는 클라이언트의 `If-None-Match` 값)
        - 새 식별자는 클라이언트가 오래된 버전에 델타를 적용하여 새로 만들 페이지의 버전이 됨
- 델타 인코딩에 사용된 헤더들
    - `ETag`: 문서의 각 인스턴스에 대한 유일 식별자. 클라이언트는 이 값을 `If-Match`, `If-None-Match` 에 사용
    - `If-None-Match`: 클라이언트가 보내는 요청 헤더로, 서버가 클라이언트와 다른 버전의 문서를 갖고 있는 경우에만 문서 요청
    - `A-IM`: 받아들일 수 있는 인스턴스 조작의 종류를 가리키는 클라이언트 요청 헤더
    - `IM`: 요청에 적용된 인스턴스 조작의 종류를 명시하는 서버 응답 헤더. `226 IM Used` 일 때 이 헤더를 보냄
    - `Delta-Base`: 델타를 생성하기 위해 사용된 기저 문서의 ETag를 명시하는 서버 응답 헤더

### 15.10.1 인스턴스 조작, 델타 생성기 그리고 델타 적용기

- 클라이언트/서버의 인스턴스 조작의 종류
    - `vcdiff`: vcdiff 알고리즘을 이용한 델타
    - `diffe`: 유닉스 diff -e 명령을 이용한 델타
    - `gdiff`: gdiff 알고리즘을 이용한 델타
    - `gzip`: gzip 알고리즘을 이용한 압축
    - `deflate`: deflate 알고리즘을 이용한 압축
    - `range`: 현재 응답이 범위 선택에 대한 결과인 부분(partial) 콘텐츠임을 말해주기 위해 서버 응답에서 사용
    - `identity`: 클라이언트가 identity 인스턴스 조작을 받아들일 의사가 있음을 말해주기 위해 클라이언트 요청의 A-IM 헤더에서 사용
- 델타 생성기: 기저 문서와 그 문서의 최신 인스턴스를 취하여 클라이언트의 `A-IM` 헤더에 지정된 알고리즘을 이용해 델타 계산
- 델타 적용기: 델타를 취득하여 기저 문서에 적용하여 문서의 최신 인스턴스 생성
- 델타 인코딩은 전송 시간을 줄일 수 있지만 구현하기 까다로움.

## 16.1 국제적인 콘텐츠를 다루기 위해 필요한 HTTP 지원

- 엔터티 본문은 비트들일 뿐이며, HTTP 메시지는 어떤 언어든 어떤 미디어든 전송 가능
- 국제 콘텐츠 지원을 위해 서버는 각 문서의 문자, 언어를 알려 클라이언트가 바르게 제공할 수 있도록 해야 함
- 서버는 클라이언트에게 문서의 문자와 언어 안내
    - `Content-Type` 의 `charset` 매개 변수와 `Content-Language` 헤더 사용
- 클라이언트는 서버에게 어떤 언어를 이해하고, 어떤 알파벳 코딩 알고리즘이 있는지 안내
    - `Accept-Charset` 과 `Accept-Language` 헤더 사용
    - 매개 변수 q는 품질 인자(quality factor)로, 이를 이용하여 가중치를 줄 수 있음

```
Accept-Charset: fr, en;q=0.8
Accept-Charset: iso-8869-1, utf-8
```

## 16.2 문자집합과 HTTP

### 16.2.1 차셋(Charset)은 글자를 비트로 변환하는 인코딩이다

- HTTP Charset은 어떻게 엔터티 콘텐츠 비트들을 특정 문자 체계의 글자들로 바꾸는지 안내
    - 각 Charset 태그는 비트↔글자 변환의 알고리즘을 명명.
        - Charset 태그는 MIME 문자 집합에 표준화되어있고, [IANA가 관리](http://www.iana.org/assignments/character-sets/character-sets.xhtml)
- 예: iso-8859-6: 아랍문자집합 디코딩 기법. 8비트 값을 숫자, 구두점, 기호, 라틴 문자, 아랍문자로 매핑
- 몇 문자 인코딩(UTF-8, iso-2022-jp 등)은 글자 당 비트 수가 일정하지 않아 더 복잡한 가변 길이 코드.
    - 이런 종류는 중국어, 일본어와 같이 많은 글자로 이루어진 문자 체계 지원을 위해 추가적인 비트를 사용할 수 있게 해줌

### 16.2.2 문자 집합과 인코딩은 어떻게 동작하는가

- 세상에는 문자가 많고, 글자를 비트로 인코딩하는 각각의 장단점을 가진 방법들이 있으므로, 디코딩 알고리즘을 지칭하고 적용하는 표준화된 방법이 필요
- 비트를 문자로 변환하는 방법
    1. 데이터 비트(11100001) →(인코딩 구조를 사용하여 디코딩)→ 문자 코드(225)
    2. 문자 코드(225) →(코딩된 문자집합을 사용하여 색인)→ 유일한 문자(ARABIC LETTER FEH)
- 국제화된 문자 시스템의 핵심 목표는 표현(시각적인 표현 방식)에서 의미(글자들)을 분리하는 것.
    - ⇒ 문자 데이터, 그와 관련된 언어,  Charset 라벨 전송에만 관심을 가짐
    - 글자 모양을 표현 방식은 사용자의 그래픽 디스플레이 소프트웨어(브라우저, 운영체제, 글꼴)가 결정
        - 예: 'ARABIC LETTER FEH' →(그래픽 디스플레이 소프트웨어)→ ف

### 16.2.3 잘못된 차셋은 잘못된 글자를 낳는다

- 같은 값이여도 잘못된 charset 매개변수를 사용한다면 글자가 깨짐
    - 예: 225(이진 값 11100001)
        - iso-8859-1 서유럽 문자 코드 → á
        - iso-8859-6 아랍 코드→ ف
        - iso-8859-7 그리스어 → α
        - iso-8859-8 히브리어 → ב

### 16.2.4 표준화된 MIME 차셋 값

- MIME Charset 인코딩 구조 몇가지
    - `us-ascii`: `ASCII` 로 불리며, 7비트를 128가지 글자에 대응시킴.
    - `iso-8859-1`: 서유럽 언어 지원을 위한 ASCII의 8비트 확장. = `iso-latin-1`, `Latin1`
    - `iso-8859-2`: 체코, 폴란드, 루마니아어 등 중부, 동유럽을 포함시키는 ASCII 확장. = `iso-latin-2`
    - `iso-8859-5`: 러시아, 세르비아, 불가리아어 등 키릴문자를 포함하기 위한 ASCII 확장
    - `iso-8859-6`: 아랍 문자를 포함하기 위한 ASCII 확장.
        - 아랍 문자의 모양은 단어에서의 위치에 좌우되므로, 문맥 분석 후 모양을 생성하는 디스플레이 엔진 필요
    - `iso-8859-7`: 현대 그리스 문자 포함하기 위한 ASCII 확장. = `ELOT-928`, `ECMA-118:1986`
    - `iso-8859-8`: 히브리어, 이디시어 문자를 포함하기위한 ASCII 확장.
    - `iso-8859-15`: 덜 필요한 구두점, 분수 기호를 고대 프랑슷어와 핀란드어로 대체. = `Latin0`
        - 국제 통화기호를 유로 통화 기호로 대체하기 위해 `iso-8859-1` 를 갱신한 것
    - `iso-2022-jp`: 일본어 전자우편, 웹 콘텐츠용 인코딩. 1바이트로 된 ASCII문자들을 지원하는 가변길이 인코딩. 일본어 문자 집합으로 전환하기 위해 3글자로 된 모달 이스케이프 문자열(modal escape sequence) 사용
    - `euc-jp`: 모드나 이스케이프 문자열 없이 글자 식별을 위해 명시적 비트 패턴을 사용하는 ISO 2022 호환 가변 길이 인코딩. 일본어 문자 집합에서의 글자를 식별하기위해 1, 2, 3바이트 문자열 사용.
    - `SHIFT_JIS`: 마이크로소프트에 의해 개발된 일본어 인코딩. = `SJIS`, `MS Kanji`
        - 호환성 문제에 따른 복잡도와 모든 문자 대응 불가이슈가 있지만 여전히 흔히 쓰임
    - `koi8-r`: IETF RFC 1490로 정의된 인기있는 러시아용 8비트 인터넷 문자 집합 인코딩.
    - `utf-8`: 전 세계 문자들에 대한 보편적 문자 집함인 UCS(유니코드)를 표현하기 위한 가변 길이 문자 인코딩 구조. 각 글자를 1~6바이트로 표현하는 가변 길이 인코딩을 문자 코드 값에 사용.
        - 주요 기능 중 하나는 통상적인 7비트 아스키 문자열에 대한 하휘 호환성
    - `windows-1252`: 마이크로소프트에서 개발한 `iso-8859-1` 확장 인코딩. = 윈도우 코드 페이지 1252, `CP1252`, `WinLatin1`

### 16.2.5 Content-Type charset 헤더와 META 태그

- 웹 서버는 클라이언트에게 MIME Charset 태그, charset 매개 변수를 Content-Type 헤더에 담아 전송
    - 문자 집합이 명시적으로 나열되지 않았따면, 수신자는 문서의 콘텐츠로 부터 추측.
- HTML 콘텐츠에서 `<meta http-equiv="Conttent-Type">` 태그는 문자 집합을 서술

    ```html
    <head>
    	<meta http-equiv="Conttent-Type" content="text/html; charset=iso-2022-jp">
    	<meta lang="jp">
    	...
    </head>
    ...
    ```

### 16.2.6 Accept-Charset 헤더

- 대부분의 클라이언트는 모든 종류의 문자 코딩, 매핑 시스템을 지원하지 않음.
- 클라이언트는 `Accept-Charset` 를 통해 어떤 문자 체계를 지원하는지 알려줌
    - 제공한 목록 중  어떤 것으로 콘텐츠를 반환할지는 서버의 자유

## 16.3 다중언어 문자 인코딩에 대한 지침

### 16.3.1 문자 집합 용어

1. 문자(character)
    - 알파벳 글자, 숫자, 구두점, 표의문자, 기호 등 글쓰기의 최소 단위.
    - 유니코드(Unicode)라고 불리는 국제 문자 세트(Universal Character Set, UCS)계획에 따라 여러 글자에게 알맞고 유일한 이름을 부여하기 위한 표준화된 이름 집합 개발되어 옴
2. 글리프(glyph)
    - 하나의 글자를 표현하기 위한, 획의 패턴이나 다른 것과 구분되는 유일한 시각적 형태
    - 하나의 글자를 여러 방식으로 쓰는게 가능하다면 글리프를 여러개 가질 수 있음.
3. 코딩된 문자(coded character)
    - 우리가 글자를 다룰 수 있도록 각 글자에 할당된 유일한 숫자
4. 코드 공간(coding space)
    - 문자 코드 값으로 사용하려고 계획 해 둔 정수의 범위
5. 코드 너비(code width)
    - 각 문자 코드의 (고정된 크기의) 비트 개수
6. 사용 가능한문자 집합(character repertoire)
    - 글자들에 대한 특정한 작업 집합(세상에 존재하는 모든 글자의 부분 집합)
7. 코딩된 문자 집합(coded character set)
    - 사용 가능 문자 집합(세상의 모든 글자에서 일부분을 선택한 것)을 받아서 각 글자에 코드 공간의 코드를 할당해주는 코딩된 문자들의 집합.
    - = 실제 글자들에 숫자로된 문자 코드를 대응시킨 것
8. 문자 인코딩 구조(character encoding scheme)
    - 숫자로 된 문자 코드들 ↔ 콘텐츠 비트의 연속을 인코딩/디코딩하는 알고리즘.
    - 문자 인코딩 구조는 글자를 식별하기 위해 필요한 데이터의 양을 줄이거나(압축), 전송상의 어떠한 제약을회피하거나, 중복된 코딩 된 문자 집합을 통합하는데 사용

### 16.3.2 '차셋(Charset)'은 형편 없는 이름이다

- `Content-Type` charset 매개 변수와, `Accept-Charset` 헤더에서 쓰이는 MIME charset 태그는 문자 집합을 의미하는 것이 아님
- 데이터 비트를 고유한 문자의 코드로 매핑하는 알고리즘의 이름. 즉 문자 인코딩 구조 + 코딩된 문자 집합
- 하지만 이를 지칭하는 다른 표준이 있으므로, 표준 문서를 읽을 때에는 정확히 무엇이 정의되어있는지 확인하여 읽어야 함.

### 16.3.3 문자

- 문자는 기본적인 구성 요소로, 글꼴이나 스타일에 독립적임
- 같은 글자라도 그 글자가 단어의 어디에 위치하냐에 따라 각각 다른 모양을 갖는 표기 체계도 있음.

### 16.3.4 글리프(glyphs), 연자(ligatures) 그리고 표현 상태

- 글리프와 문자를 헷갈려서는 안됨. 각 문자는 미적인 양식과 스크립트에 따라 여러 글리프를 가짐.
    - 문자: 유일하고 추상화된 언어의 요소
    - 글리프: 각 글자를 그리는 특정한 방법
- 많은 필기체와 활자체에는 인접한 글자들이 부드럽게 이어지는 연자(ligature)를 지원
    - 일반적인 규칙은(항상 명확하진 않음), 글리프 하나를 다른 것으로 바꾸었을 때
        - 텍스트의 이미지가 바뀐다 → 그 글리프는 서로 다른 글자임.
        - 바뀌지 않는다 → 모양만 다를뿐인 같은 글자임.

### 16.3.5 코딩된 문자 집합(Coded Character Set)

- 코딩된 문자 집합은 정수를 글자로 대응 시킴.
    - 68 →(US-ASCII 코딩된 문자 집합)→ 'LATIN CAPITAL LETTER D'

***US-ASCII: 모든 문자 집합의 어머니***

- 가장 유명한 코딩된 문자 집합으로, 코드값 0~127을 사용(7bit).
- 다른 국제 변종과 구분하기위해 'US-ASCII'라는 이름 선호됨
- HTTP 메시지(헤더, URI) 등은 US-ASCII 사용

***iso-8859***

- 국제적인 글쓰기를 위해 필요 글자들을 하이 비트를 이용해서 추가한 US-ASCII의 9비트 확대 집합
- 추가 비트에 의해 제공되는 추가 공간이 충분히 크지 않으므로, 지역에따라 커스터마이징된 문자 집합을 제공
    - 서유럽어: `iso-8859-1`, 중앙,동유럽어 `iso-8859-2`, 남유럽어: `iso-8859-3`, 북유럽어: `iso-8859-4`, 키릴: `iso-8859-5`,  아랍어: `iso-8859-6`,  그리스어: `iso-8859-7`,  히브리어: `iso-8859-8`,  터키어: `iso-8859-9`,  노르딕어: `iso-8859-10`
    - 유로 통화를 포함한 `iso-8859-1` 의 변형 : `iso-8859-15`

***JIS X 0201***

- 아스키를 일본어 가타카나 반각문자를 더해 확장한 매우 작은 문자 집합. 보통 `JIS Roman` 으로 불림

***JIS X 0208과 JIS 0212***

- 최초의 멀티 바이트 일본어 문자 집합. JIS X 0201 대신 실질적 사용을 위해 제작

UCS

- 국제 문자 세트(Universal Character Set, UCS)는 전 세계의 모든 글자를 하나의 코딩된 문자 집합으로 통합하려는 세계적인 표준.

### 16.3.6 문자 인코딩 구조

- 인코딩 구조는 숫자로 된 문자 코드 ↔ 콘텐츠 비트들로 인코딩/디코딩 함.
- 문자 인코딩은 크게 세종류로 분류
    1. **고정폭**
        - 각 코딩된 문자를 고정된 길이의 비트로 표현. 빠르게 처리되지만, 공간 낭비 우려
    2. **가변폭(비모달)**
        - 다른 문자 코드 번호에 다른 길이의 비트 사용.
        - 자주 사용하는 글자의 비트 길이를 줄일 수 있고, 국제 문자에 대해서는 여러 바이트를 사용하도록 하여 이전 8비트 문자 집합과의 호환성 유지 가능
    3. **가변폭(모달)**
        - 다른 모드로의 전환을 위해 특별한 'escape' 패턴 사용
            - 예: 어떤 모달 인코딩은 텍스트에서 중첩된 여러가지 문자집합 간의 전환을 위해 사용.
        - 모달 인코딩은 처리하기 복잡하지만, 복잡한 표기 체계를 효과적으로 지원
- 몇가지 문자 인코딩의 구조
    - 8비트
        - 8비트 고정폭 아이덴티티 인코딩은 간단히 각 문자 코드를 그에 대응하는 8비트 집합으로 인코딩
        - 256개(2^8) 문자 코드 범위에 대한 문자 집합만 지원.
        - iso-8859 문자 집합군은 9비트 아이덴티티 인코딩 사용
    - UTF-8
        - UTF-8(UCS Transformation Format 8-bit)는 인기있는 UCS를 위해 설계된 문자 인코딩
        - 문자 코드의 값을 위해 비모달 가변길이 인코딩 사용.
        - 첫 바이트의 선두 비트들은 인코딩된 문자의 길이를 바이트 단위로 나타냄.
            - 첫번째 인코딩된 바이트의 하이 비트가 0이라면 길이는 1바이트. 나머지 7비트는 문자 코드.
                - 이 때문에 아스키와 호환성이 확보됨. 예: 90 → Z
                - iso-8859는 하이비트를 사용하므로 호환되지 않음
        - 이후 바이트들은 각각 6비트의 코드 값을 포함

    - iso-2022-jp
        - 8비트 문자 지원하지 않는 소프트웨어와의 문제 방지를 위해 128보다 작은 값으로만 이루어진 가변 길이 모달 인코딩
        - 인코딩 콘텍스트(encoding context)는 언제나 네가지 미리 정의된 문자 집합 중 하나로 설정 됨.
        - 특별한 '이스케이프 문자열'을 이용해 한 집합에서 다른 집합으로 전환
            - iso-2022-jp는 처음에 US-ASCII를 사용하지만 3바이트 이스케이프 문자열을 이용해 JIS X 0201, JIS X 0208-1978, JIS X 0208-1983으로 전환 가능
    - euc-jp
        - EUC(Extended Unix Code)는 유닉스 운영체제에서 아시아 문자를 지원하기 위해 처음 개발 됨.
        - 가변 길이 인코딩이나 모달이 아니며, 모드 전환을 위한 이스케이프 문자열이 없음.
        - JIS X 0201, JSX 0208, 반각 가타카나, JIS X 0212, 총 4개의 코딩된 문자 집합을 지원
    - euc-kr
        - 한글 문서를 위해 사용된 가변길이 인코딩으로 KS X 1003, KS X 1001 두 가지 문자 집합 지원
        - KS X 1003: 1바이트로 인코딩되는 로마자 문자 집합. (US-ASCII에서 \만 원화 기호로 치환한 것)
        - KS X 1001: 2바이트로 인코딩되는 한글, 한자, 특문으로 이루어진 한국어 문자 집합.
            - 총 2,350자의 한글을 담고 있으며, 전체 한글을 담기에는 적음.
            - [한글 채움 문자](https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B8%80_%EC%B1%84%EC%9B%80_%EB%AC%B8%EC%9E%90)(fill code)를 사용하여 한글을 초, 중, 종성으로 표현하여 표현함.

## 16.4 언어 태그와 HTTP

- 언어 태그: 언어에 이름을 붙이기 위한 짧고 표준화 된 문자열
    - 브라질 포르투갈어: pt-BR, 미국 영어: en-US, 허난 중국어: zh-xiang

### 16.4.1 Content-Language 헤더

- `Content-Language` 엔터티 헤더 필드는 엔터티가 어떤 언어 사용자를 대상으로 하는지 서술
    - 여러 언어 사용자를 대상으로 한다면 나열 할 수 있음

```html
Content-Language: fr, en
```

- `Content-Language` 헤더는 텍스트 문서외에, 오디오 클립, 동영상, 애플리케이션도 대상으로 할 수 있음.
- 여러가지 언어가 하나에 엔터티에 사용되었다 하더라도 반드시 여러 언어 사용자를 대상으로 하는 것이 아님
    - 예: 영어 사용자를 위한 라틴어 수업은 'en' 만 포함해야 함.

### 16.4.2 Accept-Language 헤더

- 우리의 언어 제약과 선호도를 웹 서버에 전달. 서버는 이 값을 보고 컨텐츠를 맞춰 제공

```html
Accept-Language: es
```

### 16.4.3 언어 태그의 종류

- 언어 태그는 [RFC 3066 "Tags for the Identification of Languages"](https://tools.ietf.org/html/rfc3066) 에 문서화된 표준 문법을 갖음
- 언어 태그는 다음을 표현하기 위해 사용 될 수 있음
    - 일반적인 언어의 종류. (예: 스페인어 'es')
    - 특정 국가의 언어. (예: 영국 영어 'en-GB')
    - 방언. (노르웨이어의 'Book Language'를 의미하는 'no-bok')
    - 지방어(마서스 비니어드 섬의 수화를 의미하는 'sgn-US-MA')
    - 그 외의, 다른 언어의 변형이 아닌 표준 언어 (예: 나바호어 'i-navajo')
    - 비표준 언어(예: 스노우보드를 타는 사람들의 은어 'x-snowboarder-slang')

### 16.4.4 서브태그

- 언어 태그는 하이픈으로 분리된 하나 이상의 서브 태그로 이루어짐
    - 첫 번째 서브태그는 주 서브 태그. 표준화 되어있음. 알파벳만 포함
    - 두 번째 서브태그는 선택적이고 자신만의 이름 표준을 따름. 알파벳, 숫자를 포함할 수 있으며 최대 8자
    - 세 번째 서브태그는 등록 되어 있지 않음

### 16.4.5 대소문자의 구분 및 표현

- 모든 태그는 대소문자가 구분되지 않음. 하지만 관용적/권고안으로 언어는 소문자, 국가는 대문자를 사용
    - 예: fr은 프랑스어 FR은 국가 프랑스

### 16.4.6 IANA 언어 태그 등록

- IANA는 RFC 3066 규칙에 따라 표준 언어 태그 목록(첫 번째, 두 번째 언어 서브 태그) 관리.
- 언어가 표준 국가와 언어 값의 조합이면, 틍록되지 않아도 무방하며, 표준 국가와 언어 값으로 구성될 수 없는 언어 태그는 IANA 에 의해 등록될 필요가 있음.

### 16.4.7 첫 번째 서브태그: 이름 공간

- ISO 639 표준 언어 집합에서 선택된 표준화된 언어 토큰.
- 첫 번째 서브태그 규칙
    - 두 글자라면, ISO 639, 639-1 표준 언어 코드
    - 세 글자라면, ISP 639-2 표준과 확장에 열거된 언어 코드
    - 글자 'i' 라면, IANA에 등록된 것
    - 글자 'x' 라면, 특정 개인/집단 전용 비표준 확장자
- [639와 639-2 이름 목록](https://ko.wikipedia.org/wiki/ISO_639-1_%EC%BD%94%EB%93%9C_%EB%AA%A9%EB%A1%9D)

### 16.4.8 두 번째 서브태그: 이름 공간

- ISO 3166 국가 코드와 지역 표준 집합에서 선택된 표준화된 국가 토큰
- 두 번째 서브태그 규칙
    - 두 글자라면, ISO 3166에 정의된 국가/지역(혹은 예약어)
    - 3~8 글자라면, IANA에 등록된 것
    - 한 글자라면, 잘못 된 것
- [ISO 3166 국가 코드 목록](https://ko.wikipedia.org/wiki/ISO_3166-1)

### 16.4.9 나머지 서브태그: 이름 공간

- 세 번째와 그 이후 서브태그에는 8자 이하의 알파벳, 숫자로 이루어져야하는 것 외에는 다른 규칙 없음

### 16.4.10 선호 언어 설정하기

- 웹 브라우저 프로필에서 언어 설정을 할 수 있음

## 16.5 국제화된 URI

- 처음의 URI는 국제화를 지원하지 않아, US-ASCII의 부분 집합으로 구성되어 있었음.
- URI에 대한 최신 명세인 [RFC 3986](https://tools.ietf.org/html/rfc3986) 은 URI에 UTF-8 문자를 사용할 수 있는 방법을 명시적으로 제시
    - 오늘날엔 다양한 문자들로 URI를 사용할 수 있음.

### 16.5.1 국제적 가독성 vs 의미 있는 문자들

- URI 설계자들은 두 가지 목표가 있고, 이는 서로 충돌 함
    - 국제적 가독성: 전 세계 모두가 URI를 다른 사람들과 쉽게 공유
    - 의미 있는 문자들: URI가 사용하기 쉽고 기억하기 쉬움
- 문자 집합에는 제한이 있고, 국제적 가독성을 더 중요시 여겨서, ASCII 문자로 제한된 집합으로 이루어진 URI를 갖게되었음.

### 16.5.2 URI에서 사용될 수 있는 문자들

- URI에서는 예약된 문자, 예약되지 않은 문자, 이스케이프 문자로 나뉨.
    - 예약되지 않은 문자: URI의 어떤 컴포넌트에서도 일반적으로 사용됨
    - 예약된 문자: URI에서 특별한 의미를 가지며, 일반적으로 사용될 수 없음.
        - `;`, `/`, `?`, `:`, `@`, `&`, `=`, `+`, `$`, `,`
    - 이스케이프
        - `%`

### 16.5.3 이스케이핑과 역이스케이핑(unescaping)

- 이스케이프: 퍼센트 글자(%) 와 뒤 이은 16진수 글자 둘(US-ASCII)로 이루어진 세글자 문자열.
    - 예약된 문자, 미지원 문자들을 안전하게 URI에 삽입할 수 있는 방법 제공
- URL에 스페이스(ASCII 32)를 삽입하고 싶다면, `%20`사용할 수 있음
- HTTP 애플리케이션은 URI를 데이터가 필요할 때만 언이스케이핑(unescaping)을 해야하며, 두 번 언이스케이핑이 되지 않도록 해야 함
    - 예: %를 이스케이핑하고 두 번 언이스케이핑하면 데이터 손실 유발

### 16.5.4 국제 문자들을 이스케이핑하기

- 초반에는 이스케이프 값들이 US-ASCII코드의 범위(0-127)에 있어야 했음
- 오늘날에는 ASCII 범위 밖 문자도 인코딩 할 수 있음.

## 16.6 기타 고려사항

### 16.6.1 헤더와 명세에 맞지 않는 데이터

- HTTP 헤더는 반드시 US-ASCII 문자 집합의 글자들로만 이루어져야 함
    - 대부분의 라이브러리는 지원하지않으며, 잘못된 글자가 주어졌을 떄 충돌을 일으킴

### 16.6.2 날짜

- HTTP 명세는 올바른 GMT 날짜 형식을 정의하고 있지만, 모든 서버/클라이언트가 규칙을 따르고 있지 않음
- HTTP 애플리케이션은 명세에 맞지 않는 날짜를 관대하게 받아들이면서도, 충돌을 일으키지 말아야 함.
    - 아예 처리할 수 없는 날짜라면, 보수적으로 다루어야 함

### 16.6.3 도메인 이름

- 국제화 문자를 포함하는 도메인 이름을 '국제화 도메인 이름(Internationalizing Domain Name)'이라고 함.
- 퓨니코드(punycode): 유니코드 문자열을 호스트 명에서 사용 가능한 문자만으로 이루어진 문자열로 변환하는 방법
    - RFC 3492에 정의되어있으며, 오늘날 대부분의 웹브라우저가 이것을 이용해 국제화 도메인 이름 지원
    - 예: '한글.com' → 'xn--bj0bj06e.com'

- 하나의 URL로 여러 리소스에 대응할 필요가 있는 경우가 있음
- HTTP는 클라이언트/서버가 판단을 할 수 있도록 내용 협상(content-negotiation)방법을 제공
    - 이를 이용해 하나의 URL에 여러 리소스 중 하나를 선택할 수 있음.
    - 리소스의 서로 다른 버전을 배리언트(variant)라고 함

## 17.1 내용 협상 기법

- 서버에 있는 페이지 중 어떤 것이 클라이언트에게 맞는지 판단하는 방법 3가지
    1. 클라이언트 주도(Client-driven) 협상
    2. 서버 주도(Server-driven 협상
    3. 투명한(Transparent) 협상

## 17.2 클라이언트 주도 협상

- 동작 방식:
    - 서버는 클라이언트 요청에 대해 선택지를 주고 클라이언트가 선택
    - 요청에 대한 선택지 전달 방법 2가지:
        - 각 버전에 대한 링크와 설명이 담긴 HTML → 브라우저 사용자에 의해 수동으로 결정
        - `300 Multiple Choices` 응답 코드로 응답
- 장점:
    - 서버 입장에서 구현이 가장 쉬움
    - 클라이언트가 선택하므로 가장 적절한 것이 선택 됨(적절한 목록을 줬다면)
- 단점:
    - 각 페이지에 두번의 요청 필요해(1. 목록 얻기, 2. 목록 중 선택한 사본 얻기) 대기시간 증가

## 17.3 서버 주도 협상

- 동작 방식:
    - 클라이언트는 선호 정보를 서버에게 전달하고 서버는 요청을 보고 어떤 것을 제공할지 선택
        - 응답 계산을 위해 사용 하는 메커니즘:
            1. 내용 협상 헤더(Accept 관련 헤더)를 보고 결정
            2. 내용 협상 외 헤더(예: User-Agent)를 보고 결정 
- 장점:
    - 클라이언트 주도 협상보다 빠름.
- 단점:
    - 헤더에 맞는 적절한 것이 없으면 서버는 추측해야 함.
- 그 외:
    - 서버가 다운 스트림 장치에게 요청이 어떻게 평가되는지 말해줄 수 있도록 `Vary` 헤더 제공

### 17.3.1 내용 협상 헤더

- 클라이언트는 아래 헤더를 이용하여 자신의 선호 정보 전송
    - `Accept`: 서버가 어떤 미디어 타입으로 보내도 되는지
    - `Accept-Language`: 서버가 어떤 언어로 보내도 되는지
    - `Accept-Charset`: 서버가 어떤 차셋(Charset)으로 보내도 되는지
    - `Accept-Encoding`: 서버가 어떤 인코딩으로 보내도 되는지
- 위 헤더는 엔터티 헤더와 비슷하지만, 목적이 다름
    - 엔터티 헤더: 메시지를 전송시 필요한 메시지 본문의 속성
    - 내용 협상 헤더: 선호 정보 교환 및 적절한 문서 제공을 위해 사용
- Accept 관련 헤더와 엔터티 헤더와 짝
    - `Accept` ↔ `Content-Type`
    - `Accept-Language` ↔ `Content-Language`
    - `Accept-Charset` ↔ `Content-Type`
    - `Accept-Encoding` ↔ `Content-Encoding`
- HTTP는 상태가 없으므로(stateless) 선호 정보를 매 요청시마다 보내야 함.

### 17.3.2 내용 협상 헤더의 품질 값

- HTTP는 클라이언트 선호에 대한 설명을 품질값(quality value, q값)을 이용해 전달할 수 있는 메커니즘 제공

```
Accept-Language: en;q=0.5, fr;q=0.0, nl;q=1.0; tr;q=0.0
```

- 엔터티 헤더의 품질값과 동일하게 0.0(낮은 선호)~1.0(높은 선호)값을 가짐
- 선호에 대응하는 문서가 없을 수도 있으므로, 서버는 선호에 맞추기 위해 문서를 고치거나 트랜스 코딩할 수 있음

### 17.3.3 그 외의 헤더들에 의해 결정

- 서버는 `User-Agent` 와 같은 클라이언트의 다른 요청 헤더를 이용해 응답을 만들 수 있음
    - 예: 오래된 웹 브라우저는 자바스크립트를 지원하지않으므로 없는 버전의 페이지 전송
- 이 경우 q 값 메커니즘은 없으므로 서버는 정확한 대응을 찾아내거나 갖고 있는 것을 제공
- 캐시는 캐시된 문서의 올바른 최선의 버전을 제공해야 하기 때문에, HTTP 프로토콜을 서버가 응답을 넣어 보낼 수 있는 `Vary` 헤더 정의
    - `Vary` 헤더: 응답의 최선의 버전을 결정하기 위해 어떤 요청 헤더를 참고해야하는지 안내

### 17.3.4 아파치의 내용 협상

- 아파치 웹 서버가 내용 협상을 지원하는 방법
    - 웹 사이트 디렉터리에서 배리언트(variant)를 갖는 웹 사이트의 각 URI를 위한 type-map 파일 생성. 그 파일은 모든 배리언트와 각각에 대응하는 내용 협상 헤더 나열
    - 아파치가 디렉터리에 자동으로 type-map을 생성하도록 하는 MultiViews 지시어 켜기

***type-map 파일 사용하기***

1. 서버 설정 파일에 type-map 파일을 명시 

    ```
    AddHandler type-map .var // .var 확장자 파일이 type-map 파일임
    ```

2. type-map 파일 작성

    ```
    URI: joes-hardware.html

    URI: joes-hardware.en.html
    Content-type: text/html
    Content-language: en

    URI: joes-hardware.fr.de.html
    Content-type: text/html;charsett=iso-8859-2
    Contentt-language: fr, de
    ```

    - 품질값 지원은 [아파치 서버 문서 참조](http://httpd.apache.org/docs/2.4/content-negotiation.html)

***MultiViews 사용하기***

- MultiView를 켜키:
    - `access.conf` 파일에 적절한 절(<Directory>, <Location>, <Files>)에 Options 지시어를 이용해 웹사이트를 포함한 디렉터리에
- 자동 type-map 생성:
    - MultiViews가 켜져있을 때, joes-hardware 라는 이름의 리소스를 요청 했다면, 서버는 이름에 'joes-hardware' 가 들어있는 모든 파일을 살펴보고 그들에 대한 type-map 파일 생성
    - 이름에 근거해 서버는 각 파일에 대응하는 적절한 내용 협상 헤더를 추측
        - 예: fr이 들어간 파일이면 프랑스어 버전으로 추측

### 17.3.5 서버 측 확장

- 마이크로소프트의 ASP(액티브 서버 페이지)와 같이 서버에서 확장하는 방법이 있음. 8장 참조

## 17.4 투명 협상

- 동작 방식:
    - 투명한 중간장치(주로 프락시 캐시)가 서버를 대신해 협상
    - 프락시는 클라이언트 요청을 보고 요구사항을 파악할 수 있는 것으로 간주
    - 서버는 협상하려면 `Vary` 헤더를 통해 어떤 헤더를 검사해야하는지 프락시에게 안내
- 장점:
    - 클라이언트와의 메시지 교환을 최소화 하는 동시에, 서버 주도 협상으로 인한 부하 제거
    - 클라이언트 주도 협상보다 빠름
- 단점:
    - 투명 협상을 어떻게 하는지에 대한 정형화된 명세 없음

### 17.4.1 캐시와 얼터네이트(alternate)

- 캐시는 같은 URL에 대해 두 개 이상(예: 프랑스어 버전과 스페인어 버전)의 문서를 가질 수 있음
    - 이 다른 버전을 배리언트(variant)나 얼터네이트(alternate)라고 부름
- 내용 협상: 배리언트 중 가장 클라이언트 요청에 맞는 것을 선택하는 과정

### 17.4.2 Vary 헤더

- 캐시는 올바른 캐시된 응답을 돌려주기 위해, 서버가 응답을 돌려줄 때 사용한 의사결정 로직을 거의 그대로 사용해야 함
- 서버의 판단이 `User-Agent` 와 같이 Accept 이외의 헤더에 기초할 수 있으므로 캐시는 어떤 헤더를 기반으로 파악했는지 알아야 함
- `Vary` 응답 헤더: 판단에 근거한 요청 헤더 모두(일반적인 내용 협상 헤더 + a)를 나열
- 새 요청이 도착 했을 때, 캐시는 `Vary` 헤더가 있는지 확인해야 함
    - `Vary` 헤더가 있다면, 거기에서 명시한 헤더들은 새 요청과 캐시된 요청의 값이 같아야 함
- 투명 협상을 구현하기 위해 캐시는 반드시 캐시된 배리언트(variant)와 함께 클라이언트 요청헤더와 그에 맞는 서버 응답 헤더 모두 저장해야 함
    - 예를 들어 `Vary: User-Agent, Cookie` 라면 매우 많은 배리언트가 생성됨

## 17.5 트랜스코딩

- 트랜스 코딩: 서버가 클라이언트의 요구에 맞는 문서가 없을 때, 기존 문서를 변환하여 제공하는 것
- 트랜스 코딩에는 포맷 변환, 정보 합성, 내용 주입의 세 종류가 있음

### 17.5.1 포맷 변환

- 포맷변환(Format Conversion): 데이터를 클라이언트가 볼 수 있도록 특정 포맷으로 변환하는 것
    - 예: HTML 문서 → WML 문서, 고해상도 이미지 → 저해상도 이미지
- 내용 협상헤더에 의해 주도됨. `User-Agent` 헤더에 의해 주도될 수도 있음
- 내용 변환/트랜스코딩이 콘텐츠 인코딩/전송 인코딩과 다름을 주의
    - 내용 변환/트랜스코딩 → 특정 접근 장치에서 볼 수 있도록 하기 위함
    - 인코딩/전송 인코딩 → 콘텐츠의 효율적인, 안전한 전송

### 17.5.2 정보 합성

- 정보 합성(information synthesis): 문서에서 정보의 요점을 추출하는 것
    - 예: 각 절의 제목에 기반하여 문서 개요 생성, 페이지에서 광고나 로고 제거
- 해당 기술을 사용하여 다른 곳에서도 사용 가능
    - 포털 사이트의 웹페이지 디렉터리와 같은 자동화된 웹 페이지 분류시스템 등

### 17.5.3 콘텐츠 주입

- 콘텐츠 주입(Content Injection): 웹 문서의 양을 늘리는 종류의 트랜스 코딩
    - 포맷 변환, 정보 합성은 웹 문서의 양을 줄임
    - 예: 자동 광고 생성, 사용자 추적 시스템
- 이런 종류의 트랜스코딩은 동적으로 이루어짐

### 17.5.4 트랜스코딩 VS 정적으로 미리 생성해놓기

- 트랜스 코딩의 대안은 웹 서버에서 웹 페이지의 사본을 여럿 만드는 것
    - 예: 서버에서 HTML과 WML, 고화질과 저화질 이미지 모두 가지고 있는 것
- 정적으로 미리 생성하는 것은 현실적인 기법은 아님
    1. 많은 버전 유지를 위한 많은 저장 공간 필요, 작은 변화에 많은 수정, 관리 문제 등
    2. 동적으로 수행되는 광고 삽입 등은 정적으로 수행될 수 없음
- 대신 트랜스 코딩은 콘텐츠 제공에 있어 대기시간이 증가 함.
    - 몇 계산이나 변환은 외부로 넘겨 웹 서버의 부담을 줄이는 방법이 있음

## 17.6 다음 단계

- 내용 협상은 Accept나 Content 관련 헤더만으로 끝나지 않음
    1. HTTP 내용 협상은 성능 제약 초래. 
    2. HTTP는 내용 협상이 필요한 유일한 프로토콜이 아님

- 웹 호스팅: 웹 서버에서 중요한 기능 중 하나로 콘텐츠 리소스를 저장, 중개, 관리하는 일의 통틀어 말함

## 18.1 호스팅 서비스

- WWW 초기에는 각 회사가 자체적으로 컴퓨터 하드웨어, 컴퓨터 망, 웹 서버 소프트웨어를 관리했지만, 웹이 빠르게 대세가 되면서 전문적으로 관리하는 웹 호스팅 서비스를 제공하는 사업이 만들어 짐

### 18.1.1 간단한 예: 전용 호스팅

- 직접 자체 서버를 구매하고 서버를 유지보수 하는 대신 전용 웹 호스팅 서비스에 가입
    - 서비스에서 구매해 유지보수하는 전용 웹 서버를 임대하고, 필요시에 서버를 추가 임대 할 수 있음

## 18.2 가상 호스팅

- 가상 호스팅(=공유 호스팅): 컴퓨터 한 대를 여러 고객이 공유하게 함.
    - 트래픽이 높지 않은 사이트를 위해 전용 웹 서버를 제공하는 것은 낭비
    - 저렴한 웹 호스팅 서비스 제공 가능
- 최종 사용자 관점에서는 다른 서버에서 호스팅하는 것으로 보이지만, 물리적으로 같은 서버에서 호스팅
- 가상 호스팅은 비용, 공간, 관리에 이점이있어 한 서버에 수십~수천의 많은 웹 사이트를 호스팅
    - PC 한대에 수천개씩 구축한다는 뜻이아니라, 복제 서버 더미(=서버 팜)을 만들고 부하를 분산.
    - 팜에 있는 각 서버는 다른 서버를 복제한 것이며, 수많은 가상 웹 사이트를 호스팅하므로 관리가 편함

### 18.2.1 호스트 정보가 없는 가상 서버 요청

- HTTP/1.0에서는 가상 웹사이트에 누가 접속하는지 식별하는 기능이 없어서, 가상호스팅 업자에게 문제였음
    - URL의 경로 컴포넌트만 전송하여, 어떤 가상 웹사이트의 경로인지 알 수 없어 정보가 충분치 않음
        - 예: `http://joes-hardware.com/index.html` → `GET /index.html`
    - 완전히 다른 문서를 요청하더라도 요청 자체는 똑같을 수 있음

### 18.2.2 가상 호스팅 동작하게 하기

- 각 웹 서버가 정확히 한 웹 사이트만 호스팅할것이라고 잘못 예측한 HTTP 명세 실수.
    - 초기에는 웹 호스팅 업자가 가상 호스팅을 지원하기 위해 별도 컨벤션을 개발해야 했음
- HTTP/1.1은 완전한 URL을 포함하여 보내도록하여 간단하게 해결
- 이전 HTTP/1.0을 대응하는 4가지 기술
    1. URL 경로를 통한 가상 호스팅
        - 각 가상 사이트에 서로 다른 URL경로를 할당하여 각각을 강제로 구분
        - 예: `http://www.joes-hardware.com/joe/index.html` , 
        `http://www.marys-antiques.com/mary/index.html`
        - 불필요한 경로가 하나 더 추가되고, base URL로 접근시 문제가 생기므로 거의 사용하지 않음
    2. 포트 번호를 통한 가상 호스팅
        - 각 가상 사이트마다 포트번호를 할당.
        - 비표준 포트를 사용하므로 문제가 있음.
    3. IP 주소를 통한 가상 호스팅
        - 각 가상 사이트마다 가상 IP를 할당하여, 서버는 HTTP 커넥션의 IP 주소를 보고 어떤 사이트에 연결하려는지 확인
        - 잘 동작하지만 규모가 매우 큰 호스팅업자에게는 약간 문제가 발생.
            - 컴퓨터 시스템마다 연결할 수 있는 장비의 IP 개수 제한
            - IP주소는 희소상품이므로 원하는 만큼 할당 받을 수 없을 수 있음.
            - 용량을 늘리려고 서버를 복제하면, 복제 서버의 개수만큼 IP 개수가 추가적으로 필요하게 됨.
        - 단점에도 불구하고 많이 쓰였던 방식
    4. Host 헤더를 통한 가상 호스팅
        - 호스트명을 받아볼 수 있도록 HTTP를 확장하여 Host 확장헤더에 기술해서 전달
        - HTTP/1.0+에서 처음 소개되었으며, HTTP/1.1 명세에 추가. 현재 모든 브라우저가 지원

### 18.2.3 HTTP/1.1 Host 헤더

- Host 헤더는 [RFC 2068](https://tools.ietf.org/html/rfc2068)에 정의된 HTTP/1.1 요청 헤더.
    - 가상 서버가 매우 흔했으므로, HTTP/1.1과 호환되지 않아도 구현되어 있었음

***문법과 사용*** 

- Host 헤더에는 원본 URL에 있는 요청 리소스에 대한 인터넷 호스트와 포트 번호 기술

```
Host: <host>:<port>
```

- Host 헤더에는 다음과 같은 규칙이 있음
    - 포트가 기술되어있지 않으면, 해당 스킴의 기본 포트 사용
    - URL에 IP 주소가 있으면, Host 헤더는 같은 주소를 포함해야 함
    - URL에 호스트 명이 기술되어 있으면, Host 헤더는 IP 주소가 아닌 같은 호스트명을 포함해야 함.
    - 클라이언트가 특정 프락시 서버를 사용한다면, Host 헤더에는 프락시 서버가 아닌 원 서버의 호스트 명, 포트를 기술 해야 함. 과거에는 프락시 서버를 기술하는 버그가 있어 오작동했음.
    - 웹 클라이언트는 모든 요청 메시지에 Host 헤더를 기술해야 함
    - 웹 프락시는 요청을 전달하기 전에 요청 메시지에 Host 헤더를 추가해야 함
    - HTTP/1.1 웹 서버는 Host 헤더 필드가 없는 HTTP/1.1 요청을 받으면 400 상태코드로 응답해야 함

***Host 헤더의 누락***

- 아주 오래된 브라우저는 Host 헤더를 보내지 않으며, 이 경우 가상 호스팅 서버는 사용자를 기본 웹 페이지로 보내거나 브라우저를 업그레이드하라고 제안하는 에러 페이지를 반환할 수 있음

***Host 헤더 해석하기***

- HTTP/1.1을 통한 Host 헤더 해석에는 다음과 같은 규칙을 사용해야 함
    1. 요청 메시지에 전체 URL이 기술되어있으면, Host 헤더를 무시하고 URL 사용
    2. 요청 메시지의 URL에 호스트명이 없고, Host 헤더가 있으면 호스트명, 포트는 Host 헤더 것을 사용 
    3. 1, 2단계에서 호스트를 결정할 수 없다면 `400 Bad Request` 응답 반환 

***Host 헤더와 프락시***

- 어떤 브라우저는 부정확한 Host 헤더를 보내며, 특히 프락시와 사용하면 프락시 이름을 Host 에 담아 보내기도 함

## 18.3 안정적인 웹 사이트 만들기

- 웹 사이트에 장애가 생기는 몇가지 상황이 있으며, 이를 예측하고 대응하는 몇 가지 방법에 대해 안내
    - 서버 다운
    - 트래픽 폭증
    - 네트워크 장애나 손실

### 18.3.1 미러링 된 서버 팜

- 서버 팜: 서로 대신할 수 있고 식별할 수 있게 설정된 웹 서버들의 집합
    - 서버 팜의 서버에 있는 콘텐츠는 한 곳에 문제가 생기면 다른 한곳에서 대신 전달할 수 있게 미러링 가능
- 미러링 된 서버는 보통 계층적 관계에 있음
    - 원본 컨텐츠를 갖는 서버는 **마스터 원 서버**
    - 마스터 원 서버로부터 콘텐츠를 받은 미러링 된 서버는 **복제 원 서버**
- 서버 팜에 배포하는 간단한 방법 하나는 네트워크 스위치를 사용하여 서버에 분산 요청을 보내는 것
- 마스터 원 서버는 복제 원 서버에 콘텐츠를 보낼 책임이 있음
- 서버에 호스팅 되고 있는 각 웹 사이트의 IP 주소는 스위치 IP의 주소가 됨
    - = 외부에서 볼 때 콘텐츠를 가리키는 IP주소는 스위치의 주소
- 클라이언트의 요청이 특정 서버로 가는 방법
    1. HTTP 리다이렉션
        - 콘텐츠에 대한 URL은 마스터 서버의 IP를 가리키고, 마스터 서버는 요청을 복제 서버로 리다이렉트
    2. DNS 리다이렉션
        - 콘텐츠의 URL은 마스터 원 서버나 복제 원 서버를 가리킬 수 있고, DNS 서버는 클라이언트에게 전송할 IP 주소 선택.

### 18.3.2 콘텐츠 분산 네트워크

- 콘텐츠 분산 네트워크(CDN): 특정 콘텐츠의 분산을 목적으로 하는 단순한 네트워크
    - 네트워크의 노드는 서버, 대리 서버, 프락시 서버가 될 수 있음

### 18.3.3 CDN의 대리 캐시

- 대리 캐시는 복제 원 서버를 대신해 사용될 수 있음.
    - 리버스 프락시라고도 불리며, 미러링 된 웹 서버처럼 콘텐츠에 대한 요청을 받음
    - 특정 원서버 집합을 대신해 요청을 받음.
        - 콘텐츠에 대한 IP주소가 알려져있으며, 원 서버와 대리서버가 연결됨.
- 대리 서버는 미러링 된 서버의 차이점
    - 대리 서버는 수요에 따라 동작
        - 원 서버의 전체 콘텐츠를 복사하지 않고, 클라이언트가 요청하는 컨텐츠만 저장.
    - 원 서버는 그들의 콘텐츠를 업데이트할 의무가 없음
- 많은 요청이 있는 콘텐츠를 빠르게 제공하고, 사용자가 요청하기 전에 콘텐츠를 가져오는 '미리 가져오기(prefetching)' 기능을 가진 대리서버도 있음
- CDN이 대리 서버보다 캐시 계층화하기 어려움

### 18.3.4 CDN의 프락시 캐시

- 프락시 캐시로도 동일하게복제 원 서버를 대신하여 설정해 배포할 수 있음
    - 전통적인 프락시 캐시는 대리서버와 다르게 어떤 웹 서버 요청이든지 다 받을 수 있음.
    - 프락시 캐시와 원 서버간의 연동이나 IP 주소 합의 필요 없음
- 프락시 캐시도 대리 서버와 마찬가지로 콘텐츠 요청이 있을 때만 저장되고, 원본 서버 콘텐츠를 정확히 복제한다는 보장이 없음. 또한 어떤 프락시는 요청을 많이 받은 콘텐츠를 미리 로딩(preload)하기도 함
- 요청이 있을 때만 저장하는 프락시 캐시는 레이어 2나 3 장비(스위치 혹은 라우터)가 중간에서 웹 트래픽을 가로채 처리하기도 함
    - 가로채는 설정은 클라이언트와 서버 사이의 모든 HTTP 요청이 물리적으로 캐시를 거치게 네트워크를 설정할 수 있는지에 따라 달라짐.
    - 콘텐츠는 받는 요청에 따라 캐시에 분산됨

## 18.4 웹 사이트 빠르게 만들기

- 서버 팜, 분산 프락시 캐시, 대리 서버는 웹 사이트를 빠르게 하는데 도움이됨
    - 혼잡 조절하고 네트워크 트래픽을 분산시킴
    - 콘텐츠를 분산시키면 그 콘텐츠를 사용자에게 더 가깝게 만드므로 콘텐츠의 전송시간이 단축됨
- 웹 사이트 속도를 높이는 또 다른 방법은 콘텐츠 인코딩
    - 클라이언트가 받은 압축을 해제할 수 있다는 가정 하에, 콘텐츠를 압축하는 것