Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

[Book] HTTP 완벽 가이드: 웹은 어떻게 동작하는가

HTTP 완벽 가이드: 웹은 어떻게 동작하는가
책 표지 HTTP 완벽 가이드: 웹은 어떻게 동작하는가 - ⭐⭐⭐⭐⭐
데이빗 고울리, 브라이언 토티, 마조리 세이어, 세일루 레디, 안슈 아가왈 공저
이응준, 정상일 공역
인사이트

이 책의 원서인 HTTP: The Definitive Guide는 2009년에 나왔고 번역서는 2014년에 나왔다. HTTP/1.1은 1997년에 처음 발행되었고(물론 이 뒤에 몇 번의 개정이 되었지만 큰 내용이 바뀐 것은 아니다.) HTTP/2는 2015년에야 표준화가 되었기에 당연히 이 책도 HTTP/1.1까지만 다루고 있다. 번역서에서 2014년의 내용이 약간 반영되었지만, 원서가 10년 전에 쓰였기에 오래된 내용이 약간 보인다. 예를 들면 CGI에 대해서 설명한다거나 nginx에 대한 언급은 전혀 없다거나 하는 등이다.

그런데도 HTTP 자체가 그리 자주 바뀌지 않고 아직도 HTTP/1.1을 중심으로 사용하기 때문에 오래된 내용을 빼고 읽더라도 이 책의 가치는 여전히 높다. 웹 개발자는 물론 요즘 개발하면서 HTTP를 몰라도 되는 부분은 그리 많지 않다고 생각하기에 이 책을 읽어보기를 추천한다. 보통 도구나 라이브러리에 의존해서 사용하기에 세부 내용을 몰라도 개발하는 데 큰 문제가 없다고 느낄 수도 있지만, 개인적으로 HTTP에 대한 이해는 필수 지식으로 생각하는 편이다. 2014년에 번역서가 나왔기에 HTTP/2를 설명하는 장을 역자분들이 추가해 주셔서 최신 HTTP 스팩까지 다 다루고 있는 것과 마찬가지다.

왜 HTTP를 이해하는 것이 중요한가? 월드 와이드 웹을 지탱하는 가장 중요한 기술 두 가지는 HTML과 HTTP이다. 이 두 기술은 팀 버너스-리가 웹을 발명할 때 함께 만들어졌다. 이 둘 중 하나라도 빠지면 웹은 성립하지 않으며, 한편으론 이 둘만 있어도 어떻게든 웹은 성립할 수 있다.

대부분의 "완벽 가이드"라는 제목이 붙은 책이 그렇듯이 이 책도 상당히 두꺼워서 700페이지가 넘는다. 이런 책은 레퍼런스 북 느낌이 있어서 뭔가 지루할 것 같고 두꺼워서 시작하기 어려운 느낌이 있는 편이고 책의 주제가 HTTP이다 보니 각종 헤더와 상태 코드에 대한 설명만 너무 지루하지 않을까 하는 막연한 생각에 손에 잘 안 잡혔는데 읽고 나니 너무 좋은 책이라 왜 이제 읽었나 싶었다. 주제가 데모나 코드를 보여주기 어려우므로 지루하기 쉽다고 생각하는데 메시지, 커넥션, 웹서버, 캐시 등 이해하기 쉽게 분류가 되어 있고 정리가 잘 되어 있어서 재미있게 읽을 수 있었고 나중에 해당 부분만 참고해 보기 좋게 구성되어 있다. 한번 정독을 하고 나중에 HTTP에 대해서 찾아보고 싶은 내용이 있을 때 다시 보면 좋을 것 같다. 개발하면서 가끔 특정 헤더나 사용 관련해서 헷갈릴 때가 있는데 읽고 나니 나중에 이 책을 다시 참고해 보면 아주 좋아 보인다. 왜 이제야 읽었지 하는 생각을 하면서 읽었다.

HTTP/0.9부터 HTTP/1.0, HTTP/1.1까지 다 다루고 있다. 2019년에 HTTP/1.0과 HTTP/1.1의 호환성을 구분해서 개발하는 경우는 거의 없다고는 생각하지만, HTTP/1.1까지 오기까지 어떻게 달라졌는지를 이해하면 왜 지금의 HTTP/1.1이 이런 형태가 되어 있는지 이해할 수 있어서 나한테는 꽤 유용했다. HTTP 자체에 관해서 설명하다 보니 HTTP를 사용하는 개발자의 입장보다는 HTTP와 호환되는 서버 혹은 프락시를 구현하는 개발자를 대상으로 각 헤더는 어떤 의미가 있고 어떻게 다루어야 하는지, 실제 구현에서는 어떤 문제가 있는지 등을 설명한다. HTTP를 직접 구현해서 사용할 일은 거의 없다고 생각하지만 사용하는 처지에서도 각 의미를 명확히 이해하고 있는 것은 개발할 때 도움이 많이 된다.

책을 읽으면서도 아예 몰랐던 배경이나 의미 등도 알게 되었고 알고 있었으나 명확하게 알지 못했던 부분을 알게 되어 HTTP에 대한 지식을 정리하는 데 꽤 많은 도움이 되었다. 아래는 읽으면서 기억해 두려고 체크해 놓은 내용을 적어놓은 것이다.

HTTP는 웹에서 전송되는 객체 각각에 신중하게 MIME 타입이라는 데이터 포맷 라벨을 붙인다. MIME(Multipurpose Internet Mail Extensions, 다목적 인터넷 메일 확장)은 원래 각기 다른 전자메일 시스템 사이에서 메시지가 오갈 때 겪는 문제점을 해결하기 위해 설계되었다. MIME은 이메일에서 워낙 잘 동작했기 때문에, HTTP에서도 멀티미디어 콘텐츠를 기술하고 라벨을 붙이기 위해 채택되었다.

줄 바꿈 문자열은 'CRLF' 라고 쓴다. HTTP 명세에 따른다면 줄 바꿈 문자열은 CRLF이 지만 견고한 애플리케이션이라면 그냥 개행 문자도 받아들일 수 있어야 한다는 점을 언급할 필요가 있을 듯하다.

버전 번호는 분수로 다루어지지 않음에 주의하라. 버전의 각 숫자(예를 들어, HTTP/1.0의 '1'과 '0')는 각각 분리된 숫자로 다루어진다. 따라서 어느 쪽이 큰 지 HTTP 버전을 비교할 때 각 숫자는 반드시 따로따로 비교해야 한다. 예를 들어, HTTP/2.22는 HTTP/2.3보다 크다. 왜냐하면 22는 3보다 큰 숫자이기 때문이다.

서버 개발자들은 반드시 반환되는 헤더가 GET으로 얻는 것과 정확히 일치함을 보장해야 한다. 또한, HTTP/1.1 준수를 위해서는 HEAD 메서드가 반드시 구현되어 있어야 한다.

302, 303, 307 상태 코드 사이에서 중복되는 부분이 있음을 눈치챘을 것이다. 이 상태 코드들이 어떻게 사용되는가에 대해서는 약간 미묘한 차이가 있는데, 이는 주로 HTTP/1.0과 HTTP/1.1 애플리케이션이 이 상태 코드를 다루는 방식의 차이점에 기인한다. HTTP/1.0 클라이언트가 POST 요청을 보내고 302 리다이 렉트 상태 코드가 담긴 응답을 받으면, 클라이언트는 Location 헤더에 들어있는 리다이렉트 URL을 GET 요청으로(원래 요청에서 POST였던 것과는 달리) 따라갈 것이다. HTTP/1.0 서버가 HTTP/1.0 클라이언트로부터 POST 요청을 받은 뒤 302 상태 코드를 보내는 상황이라면, 서버는 클라이언트가 리다이렉션 URL에 대한 GET 요청으로 리다이렉트를 따라가길 기대한다. 그런데 HTTP/1.1이 혼란을 일으켰다. HTTP/1.1 명세는 그러한 리다이렉션을 위해 303 상태 코드를 사용한다(서버는 뒤이어 GET 요청이 오도록 POST 요청을 리다이렉션하기 위해 303 상태 코드를 보낼 수 있다). 이 혼란을 막기 위해, HTTP/1.1 명세는 HTTP/1.1 클라이언트의 일시적인 리다이렉트를 위해 302 상태 코드 대신 307 상태 코드를 사용하라고 한다. 그리고 서버는 302 상태 코드를 HTTP/1.0 클라이언트에게 사용하기 위해 남겨둘 수 있을 것이다.

두 가지 지속 커 넥션 타입이 있는데, HTTP/1.0+에는 'keep-alive' 커넥션이 있고 HTTP/1.1에는 '지속' 커넥션이 있다. keep-alive는 사용하지 않기로 결정되어 HTTP/1.1 명세에서 빠졌다. 하지만 아직도 브라우저와 서버 간에 keep-alive 핸드쉐이크가 널리 사용되고 있기 때문에, HTTP 애플리케이션은 그것을 처리할 수 있게 개발해야 한다.

HTTP/1.0 keep-alive 커넥션을 구현한 클라이언트는 커넥션을 유지하기 위해서 요청에 Connection:Keep-Alive 헤더를 포함시킨다. 이 요청을 받은 서버는 그다음 요청도 이 커넥션을 통해 받고자 한다면, 응답 메시지에 같은 헤더를 포함시켜 응답한다.

Keep-Alive 헤더는 커넥션을 유지하기를 바라는 요청일 뿐이다. 클라이언트나 서버가 keep-alive 요청을 받았다고 해서 무조건 그것을 따를 필요는 없다. 언제든지 현재의 keep-alive 커넥션을 끊을 수 있으며 keep-alive 커넥션에서 처리되는 트랜잭션의 수를 제한할 수도 있다. keep-alive의 동작은 Keep- Alive 헤더의 쉼표로 구분된 옵션들로 제어할 수 있다. timeout 파라미터는 Keep-Alive 응답 헤더를 통해 보낸다. 이는 커넥션이 얼마간 유지될 것인지를 의미한다. 하지만 이대로 동작한다는 보장은 없다.

HTTP/1.1에서는 keep-alive 커넥션을 지원하지 않는 대신, 설계가 더 개선된 지속 커넥션을 지원한다. 지속 커넥션의 목적은 keep-alive 커넥션과 같지만, 그에 비해 더 잘 동작한다. HTTP/1.1에서는 별도 설정을 하지 않는 한, 모든 커넥션을 지속 커넥션으로 취급한다. HTTP/1.1 애플리케이션은 트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection: close 헤더를 명시해야 한다.

한 번 혹은 여러 번 실행됐는지에 상관없이 같은 결과를 반환한다면 그 트랜잭션은 멱등(idempotent)하다고 한다. GET, HEAD, PUT, DELETE, TRACE 그리고 OPTIONS 메서드들은 멱등하다고 이해하면 된다.

프락시는 같은 프로토콜을 사용하는 둘 이상의 애플리케이션을 연결하고, 게이트웨이는 서로 다른 프로토콜을 사용하는 둘 이상을 연결한다. 실질적으로 프락시와 게이트웨이의 차이점은 모호하다.

Via 헤더 필드는 메시지가 지나는 각 중간 노드(프락시나 게이트웨이)의 정보를 나열한다. 메시지가 또 다른 노드를 지날 때마다, 중간 노드는 Via 목록의 끝에 반드시 추가되어야 한다. Via 헤더 필드는 쉼표로 구분된 경유지(waypoint)의 목록이다. 각 경유지는 개별 프락시 서버나 게이트웨이 흡을 나타내며 그들 중간 노드의 프로토콜과 주소에 대한 정보를 담고 있다.

캐시가 Date 헤더를 조정해서는 안 된다는 것에 주의하라. Date 헤더는 그 객체가 원서버에서 최초로 생겨난 일시를 표현하는 것이다.

HTTP/1.1은, 비록 콘텐츠가 조금 변경되었더라도 "그 정도면 같은 것"이라고 서버가 주장할 수 있도록 해주는 '약한 검사기(weak validator)'를 지원한다. 강한 검사기(strong validator)는 콘텐츠가 바뀔 때마다 바뀐다. 약한 검사기는 어느 정도 콘텐츠 변경을 허용하지만, 콘텐츠의 중요한 의미가 변경되면 함께 변경된다. 조건부 특정 범위 가져오기 같은 몇몇 동작은 약한 검사기로는 불가능하기 때문에, 서버는 'w/'' 접두사로 약한 검사기를 구분한다. ETag: w/"v2.6" If-None-Match: w/"v2.6"

HTTP/1.1 클라이언트는 만약 서버가 엔터티 태그를 반환했다면, 반드시 엔터티 태그 검사기를 사용해야 한다. 만약 서버가 Last-Modified 값만을 반환했다면, 클라이언트는 If-Modified-Since 검사를 사용할 수 있다. 만약 엔터티 태그와 최근 변경 일시가 모두 사용 가능하다면, HTTP/1.0과 HTTP/1.1 캐시 모두 적절히 응답할 수 있도록 클라이언트는 각각을 위해 두 가지의 재검사 정책을 모두 사용해야 한다.

Pragma: no-cache 헤더는 HTTP/1.0+와의 하위호환성을 위해 HTTP/1.1에 포함되어 었다. HTTP/1.1 애플리케이션은 Pragma: no-cache만 이해할 수 있는 HTTP/1.0 애플리케이션에 대응해야 하는 경우가 아니라면 Cache-Control: no-cache를 사용해야 한다.

'차셋(Charset)'은 형편없는 이름이다. 엄밀히 말해, MIME 차셋 태그(Content-Type charset 매개변수와 Accept-Charset 헤더에서 쓰이는)는 문자 집합을 의미하는 것이 결코 아니다. MIME 차셋 값은 데이터 비트를 고유한 문자의 코드로 매핑하는 알고리즘의 이름이다. 이것은 문자 인코딩 구조와 코딩된 문자 집합의 개념을 합친 것이다.

퓨니코드란 유니코드 문자열을 호스트 명에서 사용 가능한 문자만으로 이루어진 문자열로 변환하는 방법으로, RFC 3492로 정의되어 있다.

캐시는 반드시 캐시된 문서의 올바른 '최선의' 버전을 제공해주려 해야 하기 때문에, HTTP 프로토콜은 서버가 응답에 넣어 보낼 수 있는 Vary 헤더를 정의한다. Vary 헤더는 캐시에게(그리고 클라이언트나 그 외의 모든 다운스트림 프락시에게) 서버가 내 줄 응답의 최선의 버전을 결정하기 위해 어떤 요청 헤더를 참고하고 있는지 말해 준다.

Host 헤더는 관련 업체들이 HTTP/1.0을 확장해 만든 HTTP/1.0+에서 처음 소개되었다. HTTP/1.1 명세를 따르려면 Host 헤더를 반드시 기술해야 한다. Host 헤더는 브라우저와 서버 대부분이 지원하지만, 아직 몇몇 클라이언트와 서버(그리고 로봇)는 지원하지 않는다.

적중 계량(Hit Metering) 규약은 HTTP의 확장으로, 그 문제의 해결책으로 제시되었다. 적중 계량 규약에서는 캐시가 정기적으로 캐시 접근 통계를 원서버에 보고하도록 한다. RFC 2227은 적중 계량 규약을 상세히 정의한다.

벤더 트리는 상용 제품에서 사용되는 미디어 타입에 대한 것이다. 새로운 벤더 타입에 대한 공식적 검토는 장려되지만, 필수는 아니다. 벤더 트리 타입은 "vnd ."로 시작된다.

2019/02/28 04:31 2019/02/28 04:31