HTTP

HyperText Transfer Protocol (하이퍼텍스트 전송 프로토콜). 월드 와이드 웹의 기반이 되는 그 프로토콜로, 인터넷 주소창에서 허구한 날 나오는 그 http://의 원천이다(URI스킴 참고). 어떤 리소스(HTML 파일 등등)를 가리키는 URL을 줬을 때 그 URL에 대한 권한이 있는 호스트(authority)를 찾은 뒤(이 부분은 DNS) 그 호스트로부터 실제 리소스를 받아 오는 부분이 HTTP로 이루어진다. 최신 버전은 1999년에 나온 HTTP/1.1.

HTTP는 TCP IP 위에 위치한 어플리케이션 레이어에 속하며, 기본적으로는 TCP포트 80을 사용한다.1) 꼭 80을 써야 할 필요는 없으며, 따라서 종종 여는데 루트 권한이 필요 없는 8000, 8008이나 8080 포트를 대신 사용하는 경우도 볼 수 있다(뒤의 두 개는 IANA에도 "HTTP Alternative"로 등록되어 있다).

고수준 구조

HTTP는 기본적으로 (TCP이니만큼) 클라이언트서버에게 특정한 리소스에 대한 요청(request)을 보내면 그 리소스를 담은 응답(response)을 되돌려 보내는 프로토콜로, FTP 같은 류의 비슷한 프로토콜과는 달리 세션의 개념이 없다(stateless). 다른 말로 하면, 서버가 응답을 보냈을 때 클라이언트의 상태를 절대적으로 바꾸는 방법은 없으며, 단지 클라이언트가 상태가 바뀐 척 행동해 달라고 요청하는 것만 가능하다(대표적인 방법으로 HTTP쿠키 참고). 클라이언트가 이 요청을 무시하면(이를테면, 쿠키를 허용하지 않는 등의 방법으로) 서버는 클라이언트의 상태에 대해 어떤 예상도 할 수 없다. 이런 의미에서 HTTP는 클라이언트보다는 서버에게 더 큰 부담을 지우는 프로토콜로, 여기에는 몇 가지 중요한 장점이 있으니:

  • 보통 클라이언트보다는 서버가 더 컴퓨팅 파워가 더 높기 때문에 클라이언트가 열악해도 된다. (요즘은 그다지 아닌 것 같다만.)
  • HTTP에서 가정하는 "리소스"의 대부분은 변경되지 않는 것으로, 굳이 세션을 가정할 필요는 없다. 덤으로 서버측 개발자는 굳이 변경되지 않을 리소스에 대해서까지 세션에 민감하게 만들 이유를 못 느끼게 된다.
  • 결과적으로, 클라이언트는 특정한 종류의 접근을 제외하면 서버의 상태에 대해서 자유롭게 가정할 수 있다. 예를 들어 웹크롤러는 자동화된 접근이 서버 상태를 바꾸지 않을 것이라고 가정할 수 있다. 이런 제약은 메소드(method)의 형태로 확인할 수 있다.

물론 현재의 웹에서 이런 특성이 항상 장점인 것만은 아니다:

  • TCP/IP에서 연결을 처음 만드는 과정은 생각보다 많은 시간을 차지하며, 실제 데이터가 오가는 시간이 충분히 짧아지면 연결 시간이 상당한 부하로 작용한다. 특히 HTML처럼 한 번에 여러 리소스가 한꺼번에 넘어 가야 하는 경우 이는 매우 심각한 문제가 되는데, 때문에 HTTP/1.1에서는 Connection: Keep-Alive 헤더가 추가되어 연결을 재활용할 수 있도록 되었다. 최근의 브라우저는 또한 한 서버에 여러 개의 연결을 한꺼번에 만드는 파이프라이닝(pipelining)을 지원하기도 하며, 심지어 구글 크롬은 이 용도로 HTTP와 TCP/IP 사이에 중개 역할을 해 주는 SPDY 프로토콜을 만들기까지 했다!
  • 서버가 아무튼 상태를 유지시키려면 클라이언트에게 매 번 (보통 쿠키의 형태로) 상태를 넘겨 줘야 하는데, 클라이언트는 마음대로 행동할 수 있기 때문에 클라이언트로부터 다시 들어 오는 상태를 신뢰할 수 없다는 문제가 있다. 또한 상태의 크기가 클 경우 이 또한 상당한 부하로 작용한다. 이 때문에 대부분의 현대적인 웹사이트는 세션에 대한 키만을 클라이언트에게 노출시키는 서버측세션을 사용하고 있는데, 이는 클라이언트가 다른 세션의 키를 쉽게 예측할 수 없다는 성질에 기반한 것이다.
  • HTTP는 전통적으로 클라이언트가 연결을 시작하는(client-initiated connection) 프로토콜인데, 이 때문에 반대로, 뉴스 티커 같이 클라이언트는 가만히 있고 서버가 계속 데이터를 쏴 주는 경우 별도의 메커니즘이 필요하게 된다. 현재 여기에 대한 좋은 해결책은 아직 없으며 보통 클라이언트가 일정 시간마다 서버에게 요청을 다시 하면서 변경점을 확인하는 방법이 흔히 쓰이지만, 앞으로 WebSocket이 충분히 안정화되면 완전한 해결책이 나올 것으로 기대된다.

저수준 구조

HTTP는 텍스트 기반 프로토콜로 그 모습은 첫 줄을 제외하면 흡사 MIME과 유사하게 헤더와 데이터로 이루어져 있다. (사실은 MIME 컨텐트 타입도 함께 쓴다.) 첫 줄은 클라이언트와 서버가 서로 다르며, 클라이언트의 경우 요청하는 URL, 메소드(GET, POST 따위) 및 HTTP 버전이, 서버의 경우 상태 코드(200, 404 따위) 및 HTTP 버전이 들어 간다.

그 뒤에 따르는 HTTP 헤더는 해당 요청 및 응답에 부가적으로 따라 붙는 정보들을 담고 있으며, 이를테면 클라이언트가 보는 서버의 도메인(Host), 뒤에 따를 데이터의 종류 및 길이(Content-TypeContent-Length), 쿠키 정보(CookieSet-Cookie), 클라이언트가 서버 측에게 원하는 데이터의 구체적인 종류(Accept, Accept-Language 등), 클라이언트가 마지막으로 접근한 뒤 서버 쪽에 바뀐 게 있는지 확인하는 데 쓰는 헤더(If-Modified-Since 등), 서버 소프트웨어 정보와 서버 측 시각(ServerDate), 클라이언트 소프트웨어 정보(User-Agent)까지 온갖 것들이 다 들어 가 있다. 이 헤더 및 첫 줄의 내용에 따라서 뒤에 데이터가 올 수도 있고 안 올 수도 있는데, 이를테면 클라이언트가 GET 메소드로 요청을 보낼 경우나, 서버가 클라이언트의 마지막 접근 뒤로 아무 것도 바뀐 게 없음을 알릴 경우(상태 코드가 304인 경우) 등등의 경우에 데이터가 생략될 수 있다.

HTTP 메소드

HTTP 메소드는 크게 "안전한"(safe) 메소드와 안전하지 않은 메소드로, 또는 "반복 가능한"(idempotent) 메소드와 그렇지 않은 메소드로 분류할 수 있다. 전자는 일반적으로 서버의 상태를 변경하지 않는 메소드를 일컫으며, 후자는 말 그대로 여러 번 같은 요청을 보내도 서버의 상태를 바꾸지 않는(아예 안 보내는 것과는 별개이다) 메소드를 일컫는다. 실질적으로 이러한 특성이 제대로 지켜지는가는 미지수이지만(…) 많은 클라이언트가 이 특성에 의존하고 있으므로 웬만하면 서버측이 이 특성에 따르는 것이 후일의 이상한 버그를 방지할 수 있을 것이다.2)

OPTIONS (안전, 반복 가능)
해당 리소스에 적용 가능한 메소드 목록을 나열한다. URL 대신 *을 사용했다면 서버가 지원하는 메소드 목록을 얻을 수 있다.
HEAD (안전, 반복 가능)
해당 리소스를 요청은 하되 실제 데이터는 빼고 헤더만 받아 온다. 디버깅을 한다거나, 리소스의 변경 날짜만 알고 싶은 경우 등에 유용하다.
GET (안전, 반복 가능)
해당 리소스를 요청한다. 리소스는 해당 요청에 의해 크게 변경되어서는 안 된다(하지만 많은 사람들이 이 규칙을 무시하는 경향이 있다). GET 요청에는 데이터가 붙을 수 없으며, 만약 데이터를 넣고 싶다면 URL에만 추가할 수 있기 때문에 큰 데이터를 붙여 넣을 수는 없다(보통 서버 소프트웨어가 제한을 걸기 때문에).
POST
해당 리소스에게 주어진 데이터를 처리할 것을 요청한다. 데이터가 없을 수도 있는데 이 경우 URL만 가지고 무슨 일을 할 지를 결정하게 된다(이를테면 글 삭제라거나). GET과 더불어 매우 자주 쓰이며, 본래 PUT이나 DELETE 등의 메소드가 해야 할 일이 웹 브라우저들의 지원 부족 때문에 POST로 처리되는 경우도 흔히 볼 수 있다.
PUT (반복 가능)
주어진 데이터를 서버에 올릴 것을 요청한다. 해당 리소스가 이미 존재한다면 주어진 데이터로 그 리소스를 갱신한다. 현실에서는 POST에 묻혀서 거의 쓰이지 않는다.
DELETE (반복 가능)
해당 리소스를 삭제할 것을 요청한다. 해당 리소스가 이미 삭제되었든지 아예 원래 없었든지 아무튼 존재하지 않는다면 아무 일도 일어나지 않는다. 현실에서는 POST에 묻혀서 거의 쓰이지 않으며, 심지어 PUT보다도 안 쓰인다.
TRACE (안전, 반복 가능)
주어진 요청을 그대로 되돌려 보낸다. 중간에 프록시 서버가 끼어 있을 경우 중간에 무슨 변경이 일어나는지 확인하는 데 유용하다.
이 밖에도 다른 확장에 의해 HTTP 메소드가 추가되는 경우도 있는데, 예를 들어 HTTP프록시에서 연결을 변경하는 데 사용하는 CONNECT, WebDAV에서 쓰이는 PROPFIND 등등이 있다. 등록된 HTTP 확장 메소드의 목록은 별도의 문서에서 볼 수 있다.

상태 코드

상태 코드는 세 자리 숫자로, 그 범위에 따라서 크게 다섯 개로 나뉜다:

  • 100-199: 주어진 응답은 완전한 응답이 아니며, 뒤에 완전한 응답이 따라 올 수 있음을 나타낸다.
  • 200-299: 주어진 요청을 처리하는 데 성공했음을 나타낸다. 상태 코드는 요청의 처리 결과를 나타낸다.
  • 300-399: 주어진 요청을 처리하려면 클라이언트가 추가적인 동작(보통 또 다른 요청)을 해야 함을 나타낸다. 클라이언트에 따라서는 자동으로 그 다음 요청을 수행하지 않을 수도 있으며, 다음 요청을 수행한다 하더라도 무한루프를 방지하기 위해 5단계 이상은 진행하면 안 된다.
  • 400-499: 주어진 요청을 처리하는 데 실패했으며, 그 원인이 클라이언트에게 있음을 나타낸다. 여기에는 없는 리소스를 요청했다거나, 요청 형식이 틀렸거나, 인증을 안 했다거나 등등 여러 가지 이유가 포함될 수 있다.
  • 500-599: 주어진 요청을 처리하는 데 실패했으며, 그 원인이 서버에게 있음을 나타낸다.

주로 볼 수 있는 상태 코드는 다음과 같다:

200 OK
요청했던 리소스가 응답의 데이터 부분에 들어 있다.
206 Partial Content
요청했던 리소스의 "일부분"이 응답의 데이터 부분에 들어 있다. 이는 요청에 Content-Range 헤더가 들어 있을 경우에 해당하며, 분할다운로드를 할 때 흔히 볼 수 있다.
301 Moved Permanently
요청했던 리소스가 주어진 URL로 영구적으로 이동했으므로 다음부터는 그 URL을 써야 한다.
302 Found
요청했던 리소스가 주어진 URL로 임시로 이동했음을 나타낸다. 그러나 POST 메소드의 경우 대부분의 웹 브라우저들이 표준과는 다른 동작을 하기 때문에, 웬만하면 303이나 307을 대신 써야 한다.
303 See Other
요청했던 리소스가 주어진 URL로 임시로 이동했으며, 다음 요청에 대해서는 항상 GET 메소드를 써야 한다.
304 Not Modified
요청한 리소스는 마지막으로 클라이언트가 기억하고 있던 것에서 변경 사항이 없다.
307 Temporary Redirect
요청했던 리소스가 주어진 URL로 임시로 이동했으며, 다음 요청은 이번 요청과 같은 메소드를 써야 한다.
401 Unauthorized
요청을 수행하려면 인증이 필요하다. 응답에는 WWW-Authenticate 헤더가 포함되며, 후의 요청이 올바른 응답을 Authorization 헤더로 보내 주면 요청을 수행할 수 있다. 그러나 인터페이스를 마음대로 바꿀 수가 없기 때문에 대부분의 로그인 폼에서는 그냥 POST 메소드를 쓰는 경우가 많다(…).
403 Forbidden
요청을 거절한다. 보통 권한이 없어서 나는 오류인 경우가 많다.
404 Not Found
요청한 리소스는 존재하지 않는다. 아마도 가장 유명한(200이나 304보다도 더 유명한) 상태 코드일 것이다. 자세한 내용은 HTTP 404 참고.
500 Internal Server Error
서버 문제로 요청을 처리하지 못 했다. 말 그대로 어떤 서버 문제에 대해서도 쓸 수 있는 만능(…) 상태 코드로, 서버가 부하가 걸려서 사용할 수 없을 때 쓰는 코드 503이 있긴 하지만 보통 그런 상황에서는 그냥 응답이 안 오는 게 보통이다.

표준

HTTP는 크게 세 개의 주요 버전이 존재했다:

  • HTTP/0.9 (1991년): 표준이랄 게 없다.
  • HTTP/1.0 (1996년; RFC 1945): MIME과 비슷한 형태의 헤더가 추가되고 현재의 HTTP의 모습을 갖춘다. (이 표준에서 Simple-RequestSimple-Response를 찾아 보면 이전의 HTTP가 얼마나 썰렁했는지 알 수 있다.)
  • HTTP/1.1 (1999년; RFC 2616): 가상호스트(Host 헤더), 연결 재사용(Connection: Keep-Alive) 및 컨텐츠 협상(Accept 헤더 등등) 등등이 추가되었다.

현재 거의 모든 소프트웨어는 HTTP/1.0과 HTTP/1.1을 올바르게 처리할 수 있으며, 클라이언트 단에서는 HTTP/1.1이 보편화되었다. HTTP/2.0에 대한 논의는 한동안 존재했으나 대부분의 경우 HTTP/1.1에 대한 확장이나 HTTP의 탈을 쓴 전혀 다른 프로토콜(WebSocket, WAKA, SPDY 등)로 귀결되고 말았다. (그러나 앞으로 후자가 탄력을 얻으면 HTTP/2.0의 일부가 될 가능성도 배제할 수는 없다.)

바깥 링크

1) 인터넷할당번호관리기관(IANA)은 UDP포트 80도 할당해 놓고 있지만, 이건 TCP 포트를 할당하면 가능한한 같은 UDP 포트도 할당해야 한다는 원칙 때문이지 실제로 쓴다는 건 아니다…
2) lifthrasiir는 게시판에 GET 메소드로 글 삭제가 가능하게 만들어서 검색 엔진 봇이 한 번 긁어 갈 때마다 게시판의 모든 글이 삭제되는 사태가 발생했다는 전설을 들은 바가 있다…

도쿠위키DokuWiki-custom(rev 9085d92e02)을 씁니다.
마지막 수정 2011-11-29 06:17 | 작성자 lifthrasiir