SimpleIsBest.NET

유경상의 닷넷 블로그

HTTP 압축과 웹 액세스 API

by 블로그쥔장 | 작성일자: 2005-09-26 오후 1:30:00
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.
지난 포스트들에서 HTTP 압축에 대해 노가리를 푼 적이 있습니다. 이 포스트들은 주로 서버 측 관점에서 어떻게 컨텐츠를 압축할 것인가에 대해서만 다루었는데, 이번 포스트는 클라이언트 관점에서 HTTP 압축을 살펴보려고 합니다.

Web Access API Stack & HTTP Compression

클라이언트에서 웹 서버에 접근하는 방법은 다양하다. IE와 같은 웹 브라우저도 클라이언트이며 웹 페이지에서 XMLHTTP (MSXML2.XMLHTTP 로도 알려져 있죠?) 객체를 사용하는 자바 스크립트 역시 클라이언트로 볼 수 있으며, 닷넷 프로그램에서 HttpWebRequest 클래스와 HttpWebResponse 클래스를 사용하는 것 역시 웹 클라이언트라고 볼 수 있겠다.

윈도우 기반 플랫폼에서 웹 액세스, 즉 http 프로토콜을 통해 HTML, PDF, XML 등의 컨텐츠를 액세스할 때는 윈도우가 제공하는 다양한 API 들 중 하나를 사용할 수 있다. 이들 API들이 어떤 것들이 있으며 이들 API를 사용하는 대표적인 프로그램들을 살펴보고, 이들 API가 HTTP 압축을 지원하는지 여부도 함 까보도록 하자.

WinInet

WinInet은 Windows Internet 이란 이름의 클라이언트용 인터넷 액세스 API 집합을 말한다. WinInet은 인터넷 클라이언트들이 HTTP, FTP, Gopher 와 같은 인터넷 프로토콜을 손쉽게 사용할 수 있도록 해준다. 핵심적인 라이브러리는 WinInet.dll 이며 아주 오래 전부터 IE와 함께 배포되다가 IE가 Windows 운영체제에 포함되면서 운영체제의 일부로 간주되고 있다.

WinInet은 말 그대로 IE를 위한 API라고 보면 된다. 오로지 C/C++ API 들만을 제공하며 강력한 API 세트를 가지고 있다. IE가 사용하는 웹 액세스 API가 WinInet 임은 두말하면 잔소리가 되겠다. IE 외에도 MFC도 WinInet을 위한 클래스를 제공하며, 많은 인터넷 기반 어플리케이션들이 이 API를 사용하여 작성되었다. 또한, 자바 스크립트에서 많이 사용되는 XMLHttp 객체(MSXML2.XMLHttp) 역시 WinInet API를 사용하고 있음을 꼭 기억해 두자.

그리고 제어판의 "인터넷 옵션" 패널이나 IE의 도구 메뉴의 "인터넷 옵션"은 모두 이 WinInet에 대한 옵션을 말한다. 아주 많은 어플리케이션들이 WinInet에 의존하고 있으므로 이 "인터넷 옵션"은 매우 중요하게 간주되고 있다.

WinInet은 HTTP 프로토콜의 모든 기능을 구현하고 있다. 즉, 풍부한 기능을 제공한다는 말이다. 웹 Request를 수행하고 그 결과를 캐시하며, 쿠키 설정 역시 적절히 수행할 뿐 더러, 클라이언트의 디스크 쿠키(디스크에 기록되는 쿠키)를 하드 디스크에 기록하기도 한다. 쉽게 말해, 이 API를 통해 IE가 수행하는 것은 거의 대부분 똑같이 수행할 수 있다는 말이다. C/C++ API 세트만을 제공함이 아쉬울 따름이다.

HTTP 압축 역시 예외는 아니다. WinInet을 사용하면 HTTP 압축을 위해 Accept-Encoding 헤더의 값을 gzip, deflate로 설정한다. 또한 HTTP Response가 압축되면, 즉 Response의 Content-Encoding 헤더의 값이 gzip 혹은 deflate로 설정되어 있다면, 압축을 적절히 해제한다(필자의 HTTP 압축 원리를 읽어 보기 바란다). 친절한 WinInet 씨가 되겠다. 고로 WinInet API를 사용하는 웹 클라이언트는 모두 HTTP 압축을 지원한다고 보면 된다. IE를 비롯하여 자바 스크립트에서 사용하는 XMLHttp 도 압축된 컨텐츠를 해제할 수 있으므로 이들을 기반으로 하는 어플리케이션은 클라이언트 측에서 압축이 풀리는 것에 대해서는 전혀 걱정을 하지 않아도 된다.

WinInet의 단점은, 클라이언트를 위한 API 이므로 몇 가지 제약사항이 있다는 점이다.  이 제약 사항 중 하나는 KB 183110 에서 다루고 있는 내용으로 한 서버에 2개 혹은 4개의 TCP 연결만을 허용한다는 점이다. HTTP 1.1 프로토콜의 경우에 2개의 연결을, HTTP 1.0 의 경우에 4개의 연결만을 허용한다. 이 제약 사항은 HTTP 표준과 다른 다양한 웹 브라우저들이 사용하는 제약으로 한 클라이언트가 서버 자원을 독점하는 현상을 줄이기 위함이다.

WinHTTP

WinHTTP는 WinInet과는 달리 오로지 HTTP 프로토콜만을 지원하는 클라이언트용 API 이다. WinInet이 IE와 같은 순수 클라이언트를 지향한 API라면 WinHTTP는 높은 성능과 높은 동시성(concurrency)을 요구하는 클라이언트를 위한 API이다. 즉, 앞서 언급한 WinInet의 서버 연결 개수 제약을 극복하고, HTTP 프로토콜의 모든 스펙과 캐시까지 수행하는 무거운 WinInet의 기능을 축소하여 빠른 성능을 올리기 위해 설계된 API인 셈이다. WinHTTP는 C/C++를 위한 API와 COM 및 스크립트 클라이언트를 위한 API 도 제공한다. 쉽게 말해 자바 스크립트나 ASP에서 이 API를 쓸 수 있다는 것이다. WinHTTP는 IE 5.01 & MSXML 3.0부터 추가되었지만 이제는 Windows 2003, Windows XP SP1, Windows 2000 SP3 부터 운영체제의 일부로 간주되고 있다(winhttp5.dll).

그렇다면 이 API를 사용하는 웹 클라이언트는 누가 될까? 아이러니하게도 서버용 어플리케이션들이 이 API를 사용하곤 한다. 서버가 다른 웹 서버의 웹 컨텐츠를 액세스하는 경우는 매우 흔하다. 예를 들어 웹 서버가 어플리케이션 서버를 액세스할 때 웹 서비스를 사용한다면 웹 서버는 어플리케이션 서버의 입장에서 보았을 때 클라이언트이기 때문이다. 이런 경우에 WinInet의 제약을 떠올려보자. -_-;; 동시에 10명의 사용자가 웹 서버를 호출했고 이 웹 서버가 어플리케이션 서버를 동시에 호출할 때, 서버 접속이 2개로 제약된다면 2개의 동시 호출만이 어플리케이션 서버로 날아갈 것이고 나머지 8개는 앞선 2개의 호출을 기다려야 하는 현상이 발생한다. 대략 난감한 것이다.

이러한 시나리오를 위해 등장한 것이 WinHTTP 이다. WinHTTP는 서버 접속의 제약도 없으며 API 도 매우 간략하게 구성되어 WinInet에 비하면 매우 빠르다고 한다. (직접 써보지 않아서...) 하지만 단점은 있다. WinHTTP는 WinInet에 구현된 여러 기능이 빠져있다(구체적으로 어떤 기능이 제공되지 않는지는 MSDN에서 WinHTTP를 찾아 보기 바란다. 그런 수고도 하기 싫은 사람은 대략... 뷁 !!!). HTTP 압축 역시 WinHTTP 가 지원하지 않는 기능 중 하나이다. WinHTTP가 압축을 지원하지 않는다고 해서 문제가 발생하는 것은 아니다. 단지 WinHTTP 를 사용하면 Accept-Encoding 헤더가 설정되지 않을 뿐더러, Content-Encoding 헤더의 내용도 무시된다는 것이다. 서버는 Accept-Encoding 헤더가 설정된 경우에만 컨텐츠를 압축할 것이므로 WinHTTP 가 압축을 지원하지 않는 것이 웹 컨텐츠를 액세스하는 것 자체에는 문제가 없다는 말이다.

WinHTTP와 더불어 소개할 것은 ServerXMLHTTP 객체이다. 이 객체는 MSXML 3.0부터 추가된 객체로서 우리가 익히 알고 있는 XMLHTTP 객체와 동등하며 사용법 마저 거의 동일하다. 그런데 ServerXMLHTTP는 WinInet 이 아닌 WinHTTP API를 호출한다. 왜 이름 앞에 Server가 붙었는지 짐작이 갈 것이다. 서버측 어플리케이션이 HTTP를 통해 XML을 액세스할 때 사용하라고 만들어진 객체인 것이다. ASP 라면 당연 빤쓰로 ServerXMLHTTP 객체를 써야 할 것이며, 브라우저의 자바 스크립트라면 XMLHTTP를 사용해야 할 것이다. 이유는 간단하다. XMLHTTP는 WinInet을 지원하고 WinInet은 Windows 95 부터 모든 윈도우 플랫폼이 지원하고 있다. 하지만 WinHTTP는 그렇지 않다는 점이다.

Socket

WinInet도 아니고 WinHTTP도 아니면 직접 저 수준의 소켓(socket) 프로그래밍으로도 HTTP 프로토콜로 웹 액세스를 할 수 있다. 무식하게 생각하겠지만, 압축도 필요 없고, 캐시도 필요 없고 단순한 컨텐츠 다운로드를 위한 것이라면 소켓을 직접 사용하는 것이 가장 빠르다. 에이~ 아무리 그래도 그렇지 직접 소켓을 액세스 해서 HTTP 웹 액세스를 하는 것이 있겠어?

있다...... OTL !!!!!!

닷넷 프레임워크의 웹 액세스 관련 클래스인 HttpWebReqeust/HttpWebResponse 클래스는 WinInet도 WinHTTP도 사용하지 않는다. Socket을 직접 액세스해서 웹 컨텐츠를 액세스한다. 오 쉣~~ 증말인가? 그렇다. 거짓말 같지만... 정말이다... HttpWebRequest/HttpWebResponse 클래스는 소켓을 통해 HTTP 프로토콜을 구현하고 있다. 그래서 인지 제공하는 기능은 대단히 미약하다고 볼 수 있다. 예를 들어 WinInet은 지가 알아서 디스크 상의 쿠키(존재한다면)를 읽어 Request에 쿠키 설정을 해준다. 하지만 HttpWebRequest 에게는 씨알도 안먹히는 소리이다. 쿠키 설정? 개발자가 직접 코드로 쿠키를 설정해 주어야 한다. 또, WinInet 은 서버가 쿠키를 설정하라고 Set-Cookie 헤더를 내려 보내면 자기가 알아서 메모리 혹은 디스크에 쿠키 파일을 만든다. HttpWebResponse 는 개발자가 쿠키를 읽어서 메모리에 보관하던 파일에 보관하던 알아서 보관해야 한다. 다행이도 Transfer-Encoding 헤더를 인식하여 조각난 chunken을 합쳐주긴 한다... (베타 시절인가에는 이것을 지원하지 않아서 필자가 직접 구현준 기억이 난다...)

기능은 적지만 HttpWebRequest는 갖추어야 할 것들은 대부분 갖추고 있다. HTTP 인증, 프록시 지원, SSL 등등... 필자의 견해로 System.Net 네임스페이스에 포함된 HttpWebRequest/HttpWebResponse 클래스는 WinHTTP 를 닷넷용으로 다시 작성한 것이 아닌가 싶을 정도로 둘은 유사한 점을 갖고 있다. 물론 닷넷이 보다 멋지고 사용하기 쉬운 API를 제공하고 있긴 하지만 말이다.

어찌 되었건 닷넷에서 웹 액세스를 할 때는 개발자가 약간 수고스럽게 된다. HttpWebRequest/HttpWebResponse를 사용하는 닷넷 클라이언트가  HTTP 압축을 지원하지 않음은 뻔할 뻔데기 이다. HTTP 압축 원리 포스트에서 압축을 해제하는 C# 코드를 예제로 이미 보였으니 참고하기 바란다.

닷넷 웹 서비스 클라이언트는 어떻게 될까? 압축을 사용할까? 닷넷 웹 서비스 클라이언트, 소위 웹 서비스 프록시에 의한 웹 서비스 호출은 HttpWebRequest 와 HttpWebResponse 클래스를 사용한다. 따라서... HTTP 압축은 기본적으로 지원되지 않는다. 웹 서비스 호출을 압축하거나 압축되어 온 웹 서비스 호출 결과를 압축 해제 하기 위해서는 별도의 코딩이 필요함을 반드시 기억해 두자.

또 한 가지 언급하고 싶은 것은, 닷넷의 HttpWebRequest 및 HttpWebResponse 의 기본 설정 중 일부는 WinInet의 "인터넷 옵션"의 값을 읽어서 사용한다. 대표적인 것이 프록시 설정인데, WinInet의 프록시 설정을 바꾸면 이 설정 값이 HttpWebRequest 에도 적용된다. 이는 닷넷 프레임워크의 클래스가 이 설정을 읽어서 사용하기 때문이다. 그렇다고 WinInet을 사용하는 것이 아님은 여전하다. 단지 일부 설정만을 WinInet과 공유할 뿐이다.

Summary

웹 액세스를 수행하는 세가지 대표적인 API를 정리해 보면 다음 그림과 같다.


<<웹 액세스 API 스택>>

IE 와 같은 웹 브라우저와 HTML 내에서 AJAX 와 같은 자바 스크립트들이 많이 사용하는 XMLHTTP 객체 등 많은 웹 클라이언트들은 WinInet API를 사용하고 ServerXMLHTTP 와 ASP 같은 서버 어플리케이션들은 WinHTTP를 사용한다. 반면에 웹 서비스 클라이언트를 포함한 닷넷 기반 어플리케이션들은 HttpWebRequest 클래스와 HttpWebResponse 클래스를 사용하는데 이들은 모두 System.Net.Socket 네임스페이스의 소켓을 사용한다. 너무도 당연하게 WinInet, WinHTTP, 그리고 System.Net.Socket 네임스페이스의 클래스들은 모두 Windows 의 Window Socket 계층을 통해 네트워크를 액세스 한다.

이들 세 API 중, HTTP 압축을 지원하는 API는 WinInet 이며, WinHTTP와 닷넷 환경은 HTTP 압축을 지원하지 않는다. 이들 HTTP 압축을 지원하지 않는 API를 사용하는 경우에 HTTP 압축은 별도의 압축 해제 솔루션(이런 솔루션이 있는지 필자도 잘 모르겠다)을 사용하던가 아니면 합축 해제를 직접 구현해야 한다.

마지막으로 가장 많이 사용되는 빈도적으로 요약하자면... 서버 측에 HTTP 압축을 적용하고자 한다면 다음 두 가지만 기억해 두자. 북치기... 박치기... -_-;;

  • IE 나 XMLHTTP는 HTTP 압축이 WinInet에 의해 지원되므로 별다른 걱정을 하지 않아도 된다.
  • 닷넷 기반의 어플리케이션들은 HTTP 압축을 별도로 구현해 주어야 한다.

닷넷에서 HTTP 압축을 구현하는 구체적인 방법에 대해서는... 언제가 될지 몰라도... 다음 기회에 써볼란다... 뭐... 팬들의 열화와 같은 성원이 있으면 곧바로 시작하겠지만... -_-;;



Comments (read-only)
#궁금했던 내용들인데 재미있네요! / arang100 / 2007-03-26 오후 5:08:00
클라이언트에서 웹에 접근하는 방법에 대해서 잘 나누어서 설명해 주어서 정리가 잘 되네요!
XMLHTTP, SERVERXMLHTTP, HttpWebRequest, HttpWebResponse 등 여러가지 요소들이 왜 있나 싶었더니 다 이유가 있군요.
강좌고맙습니다.