Search

5장 웹 서버

발표자
Participant
Presentation Date
2021/04/29

시작하며

웹 서버는 매일 수십억 개의 웹페이지를 쏟아 낸다. 웹 서버는 우리에게 날씨를 알려주고, 온라인 쇼핑 카트에 물건을 싣고, 오랫동안 만나지 못한 고등학교 친구를 찾을 수 있게 해준다. 웹 서버는 월드 와이드 웹(WWW)의 일꾼이다.

이 장에서 다룰 것들

여러 종류의 소프트웨어 및 하드웨어 웹 서버에 대해 조사한다.
HTTP 통신을 진단해주는 간단한 웹 서버를 펄(Perl)로 작성해본다. → 생략
어떻게 웹 서버가 HTTP 트랜잭션을 처리하는지 단계별로 설명한다. → 가장 중요

5.1 다채로운 웹 서버

웹 서버라는 용어는 웹 서버 소프트웨어와 웹페이지 제공에 특화된 장비(컴퓨터와 같은) 양쪽 모두를 가리킨다.
모든 웹 서버는 리소스에 대한 HTTP 요청을 받아서 콘텐츠를 클라이언트에게 돌려 준다.

5.1.1 웹 서버 구현

웹 서버는 HTTP 및 그와 관련된 TCP 처리를 구현한 것이다. 리소스를 관리하고 웹 서버를 설정, 통제, 확장하기 위한 관리 기능을 제공한다.
웹 서버는 HTTP 프로토콜을 구현하고, 웹 리소스를 관리하고, 웹 서버 관리 기능을 제공한다.
웹 서버의 형태
다목적 소프트웨어 웹 서버를 표준 컴퓨터 시스템에 설치하고 실행할 수 있다.
마이크로프로세서의 기적으로, 어떤 회사들은 사용자에게 판매할 전자기기 안에 몇개의 컴퓨터 칩만으로 구현된 웹 서버를 내장시켜서 완전한 관리 콘솔로 제공 한다.(예를 들면 몇몇 공유기들은 웹브라우저로 접근 가능한 관리 기능을 제공한다.)

5.1.2 다목적 소프트웨어 웹 서버

네트워크에 연결된 표준 컴퓨터 시스템에서 동작한다. 아파치나 W3C의 직소 같은 오픈 소스 소프트웨어를 사용할 수도 있고, 마이크로소프트나 아이플래닛의 웹 서버 같은 상용 소프트웨어를 사용할 수도 있다. 웹 서버나 소프트웨어는 거의 모든 컴퓨터와 운영체제에서 동작한다.
책에서 나온 내용보다 2021년 기준으로는 nginx가 점유율 1위를 나타낸다.

5.1.3 임베디드 웹 서버

임베디드 웹 서버는 일반 소비자용 제품에 내장될 목적으로 만들어진 작은 웹서버이다.(예: 프린터나 가전제품)

5.2 간단한 펄 웹 서버

최소한으로 기능하는 HTTP 서버라면 30줄 이하의 펄(Perl) 코드로도 만들 수 있다.
→ 궁금하신 분은 책 P.127 ~ P.130 참고

5.3 진짜 웹 서버가 하는 일

최신식 웹 서버는 보다 복잡하지만 공통적으로 그림 5-3에서 보이는 몇가지 일들을 수행한다.
1.
커넥션을 맺는다 — 클라이언트의 접속을 받아들이거나, 원치 않는 클라이언트라면 닫느다.
2.
요청을 받는다 — HTTP 요청 메시지를 네트워크로부터 읽어 들인다.
3.
요청을 처리한다 — 요청 메시지를 해석하고 행동을 취한다.
4.
리소스에 접근한다 — 메시지에서 지정한 리소스에 접근한다.
5.
응답을 만든다 — 올바른 헤더를 포함한 HTTP 응답 메시지를 생성한다.
6.
응답을 보낸다 — 응답을 클라이언트에게 돌려준다.
7.
트랜잭션을 로그로 남긴다 — 로그파일에 트랜잭션 완료에 대한 기록을 남긴다.
다음의 일곱 개의 절은 어떻게 웹 서버가 이러한 기본 작업을 수행하는지 보여준다.

5.4 단계 1: 클라이언트 커넥션 수락

클라이언트가 이미 서버에 대해 열려있는 지속적 커넥션을 갖고 있다면, 클라이언트는 요청을 보내기 위해 그 커넥션을 사용할 수 있다. 그렇지 않다면 클라이언트는 서버에 새 커넥션을 열 필요가 있다.

5.4.1 새 커넥션 다루기

클라이언트가 웹 서버에 TCP 커넥션을 요청하면, 웹 서버는 그 커넥션을 맺고 TCP 커넥션에서 IP 주소를 추출하여 커넥션 맞은 편에 어떤 클라이언트가 있는지 확인한다.
→ TCP 커넥션을 조작하는 인터페이스와 자료구조는 운영체제마다 다르다.
웹 서버는 어떤 커넥션이든 마음대로 거절하거나 즉시 닫을 수 있다.

5.4.2 클라이언트 호스트 명 식별

대부분의 웹 서버는 '역방향 DNS(reverse DNS)'를 사용해서 클라이언트의 IP 주소를 클라이언트의 호스트 명으로 변환하도록 설정되어 있다.
호스트 명 룩업(hostname lookup)은, 꽤 시간이 많이 걸릴 수 있어 웹 트랜잭션을 느려지게할 수 있음을 미리 경고해두겠다. 많은 대용량 웹 서버는 호스트 명 분석(hostname resolution)을 꺼두거나 특정 컨텐츠에 대해서만 켜놓는다.
예 5-2의 아파치 설정 지시자는 HTML과 CGI 리소스만을 위해 호스트명 분석을 켠다.

5.4.3 ident를 통해 클라이언트 사용자 알아내기

몇몇 웹 서버는 또한 IETF ident 프로토콜을 지원한다. ident 프로토콜은 서버에게 어떤 사용자 이름이 HTTP 커넥션을 초기화했는지 찾아낼 수 있게 해준다.(이 정보는 웹 서버 로깅에서 유용)
클라이언트가 ident 프로토콜을 지원한다면, 클라이언트는 ident 결과를 위해 TCP 포트 113번을 listen한다.
위 그림은 어떻게 ident 프로토콜이 도작하는지 보여준다.
(a) 클라이언트는 HTTP 커넥션을 연다.
(b) 서버는 자신의 커넥션을 클라이언트의 ident 서버 포트(113)을 향해 연다.
(c) 서버는 새 커넥션에 대응하는 사용자 이름을 묻는 간단한 요청을 보낸다.
(d) 클라이언트는 ident 응답을 반환한다.
ident는 조직 내부에서는 잘 사용할 수 있지만, 공공 인터넷에서는 다음을 포함한 여러가지 이유로 잘 동작하지 않는다.
많은 클라이언트 PC는 identd 신원확인 프로토콜 데몬 소프트웨어를 실행하지 않는다.
ident 프로토콜은 HTTP 트랜잭션을 유의미하게 지연
방화벽이 ident 트래픽이 들어오는 것을 막는 경우가 많음
ident 프로토콜은 안전하지 않고 조작하기 쉬움
ident 프로토콜은 가상 IP 주소를 잘 지원하지 않음
클라이언트 사용자 이름의 노출로 인한 프라이버시 침해의 우려

5.5 단계 2: 요청 메시지 수신

커넥션에 데이터가 도착하면, 웹 서버는 네트워크 커넥션에서 그 데이터를 읽어 들이고 파싱하여 요청 메시지를 구성한다.
요청 메시지를 파싱할 때, 웹 서버는 다음과 같은 일을 한다.
요청줄 파싱하여, 요청 메서드, 리소스의 식별자(URI), 버전 번호를 찾는다.(각 값은 스페이스 한 개로 분리되어 있으며, 요청줄은 캐리지 리턴 줄바꿈(CRLF) 문자열로 끝난다.
메시지 헤더들을 읽는다. 각 메시지 헤더는 CRLF로 끝난다.
헤더의 끝을 의미하는 CRLF로 끝나는 빈 줄을 찾아낸다.(존재한다면)
요청 본문이 있다면, 읽어 들인다.(길이는 Content-Length 헤더로 정의된다.)
HTTP의 초기 버전인 HTTP/0.9는 버전 번호를 지원하지 않는다. 버전 번호가 빠진 메시지도 지원하는 몇몇 웹 서버는 그러한 메시지를 HTTP/0.9 요청으로 해석한다.
많은 웹 서버들이 LF와 CRLF 모두를 줄바꿈 문자로 인식한다. 줄의 끝을 표현하기 위해 실수로 LF를 보내는 클라이언트들도 종종 있기 때문이다.
웹 서버는 입력 데이터를 네트워크로부터 불규칙적으로 받는다. 파싱해서 이해하는 것이 가능한 수준의 분량을 확보할 때까지 데이터를 네트워크로부터 읽어서 메시지 일부분을 메모리에 임시로 저장해 둘 필요가 있다.

5.5.1 메시지의 내부 표현

몇몇 웹 서버는 요청 메시지를 쉽게 다룰 수 있도록 내부의 자료 구조에 저장한다.
예를 들어, 그 자료 구조는 요청 메시지의 각 조각에 대한 포인터와 길이를 담을 수 있을 것이고, 헤더는 속도가 빠른 룩업 테이블에 저장되어 각 필드에 신속하게 접근할 수 있을 것이다.

5.5.2 커넥션 입력/출력 처리 아키텍처

고성능 웹 서버는 수천 개의 커넥션을 동시에 열 수 있도록 지원한다.
1.
어떤 커넥션들로부터는 요청이 느리게 혹은 드물게 흘러 들어옴
2.
또 어떤 것들은 나중에 일어날 활동을 위해 조용히 대기
3.
일부 커넥션들은 웹 서버로 급속히 요청을 보냄
웹 서버들은 항상 새 요청을 주시하고 있다. 그림 5-7에 그려진 것과 같이, 웹 서버 아키텍처의 차이에 따라 요청을 처리하는 방식도 달라진다.
단일 스레드 웹 서버(그림 5-7 a )
단일 스레드 웹 서버는 한 번에 하나씩 요청을 처리한다. 트랜잭션이 완료되면, 다음 커넥션이 처리된다.
이 방식은 심각한 성능 문제를 만들어내므로 오직 로드가 적은 서버나 type-o-server와 같은 진단도구에서만 적당하다.
멀티프로세스와 멀티스레드 웹 서버(그림 5-7 b)
멀티프로세스와 멀티스레드 웹 서버는 여러 요청을 동시에 처리하기 위해 여러 개의 프로세스 혹은 고효율 스레드를 할당한다.
스레드/프로세스는 필요할 때마다 만들어질 수도 있고 미리 만들어질 수 있다.
스레드가 미리 생성되는 시스템을 '작업자 풀(worker pool)' 시스템이라고 부르는데, 왜냐하면 스레드들의 집합이 수행할 작업을 풀(pool)에서 기다리기 때문이다.
몇몇 서버는 매 커넥션마다 스레드/프로세스 하나를 할당하지만, 서버가 수백, 수천, 수만 개의 동시 커넥션을 처리할 때 그로 인해 만들어진 수많은 프로세스나 스레드는 너무 많은 메모리나 시스템 리소스를 소비한다.
그러므로 많은 멀티스레드 웹 서비스가 스레드/프로세스의 최대 개수에 제한을 건다.
다중 I/O 서버(그림 5-7 c)
대량의 커넥션을 지원하기 위해, 많은 웹 서버는 다중 아키텍처를 채택했다. 다중 아키텍처에서는 모든 커넥션은 동시에 그 활동을 감시당한다.
커넥션의 상태가 바뀌면, 그 커넥션에 대해 작은 양의 처리가 수행된다. 그 처리가 완료되면, 커넥션은 다음번 상태 변경을 위해 열린 커넥션 목록으로 돌아간다.
어떤 커넥션에 대해 작업을 수행하는 것은 그 커넥션에 실제로 해야 할 일이 있을 때뿐이다.
스레드와 프로세스는 유휴 상태의 커넥션에 매여 기다리느라 리소스를 낭비하지 않는다.
다중 멀티스레드 웹 서버(그림 5-7 d)
몇몇 시스템읜 자신의 컴퓨터에 올라와 있는 CPU 여러 개의 이점을 살리기 위해 멀티스레딩과 다중화(multiplexing)를 결합한다. 여러 개의 스레드는 각각 열려있는 커넥션을 감시하고 각 커넥션에 대해 조금씩 작업을 수행한다.

5.6 단계 3: 요청 처리

웹 서버가 요청을 받으면, 서버는 요청으로부터 메서드, 리소스, 헤더, 본문을 얻어내어 처리한다.
POST를 비롯한 몇몇의 메서드는 요청 메시지에 엔터티 본문이 있을 것을 요구한다. 그 외 OPTIONS를 비롯한 다수의 메서드는 요청에 본문이 있는 것을 요구하지 않는다.
많지는 않지만 GET과 같이 요청 메시지에 엔터티 본문이 있는 것을 금지하는 메서드도 있다.
요청 처리에 대해서는 이야기하지 않는다. 왜냐하면 그건 이책 나머지 대부분의 주제이기 때문!!

5.7 단계 4: 리소스의 매핑과 접근

웹 서버는 리소스 서버다. HTML 페이지나 JPEG 이미지 같은 미리 만들어진 콘텐츠를 제공하며, 서버 위에서 동작하는 리소스 생성 애플리케이션을 통해 만들어진 동적 콘텐츠도 제공한다.
웹 서버가 클라이언트에 콘텐츠를 전달하려면, 요청 메시지의 URI에 대응하는 알맞은 컨텐츠나 콘텐츠 생성기를 웹 서버에서 찾아서 그 콘텐츠의 원천을 식별해야 한다.

5.7.1 Docroot

웹 서버는 여러 종류의 리소스 매핑을 지원한다. 리소스 매핑의 가장 단순한 형태는 요청 URI를 웹 서버의 파일 시스템 안에 있는 파일 이름으로 사용하는 것이다.
웹 서버 파일 시스템의 특별한 폴더를 웹 콘텐츠를 위해 예약해 둔다. 이 폴더는 문서 루트 혹은 docroot로 불린다.
/specials/saw-blade.gif에 대한 요청이 도착했다.
웹 서버는 문서 루트 /usr/local/httpd/files를 갖고 있다.
웹 서버는 /usr/local/httpd/files/specials/saw-blade.gif 파일을 반환한다.
아래와 같이 httpd.conf 설정 파일에 DocumentRoot 줄을 추가하여 아파치 웹 서버의 문서 루트를 설정할 수 있다.
DocumentRoot /usr/local/httpd/files
Shell
복사
서버는 상대적인 url이 docroot를 벗어나서 파일시스템의 docroot 이외 부분이 노출되는 일이 생기지 않도록 주의해야 한다.
http://www.joes-hardware.com/../
Shell
복사
문서 루트 위의 파일을 보려고 하는 이와 같은 URI를 허용하지 않는다.
가상 호스팅된 docroot
가상 호스팅 웹 서버는, 각 사이트에 그들만의 분리된 문서 루트를 주는 방법으로 한 웹 서버에서 여러 개의 웹 사이트를 호스팅 한다.
하나의 웹 서버 위에서 두 개의 사이트가 완전히 분리된 콘텐츠를 갖고 호스팅 되도록 할 수 있다.
위 그림에서는 두 사이트를 호스팅 한다. 서버는 두 웹 사이트를 HTTP Host 헤더나 서로 다른 IP 주소를 이용해 구분할 수 있다.
요청 A가 도착했을 때, 서버는 /docs/joe/index.html 파일을 가져온다.
요청 B가 도착했을 때, 서버는 /docs/mary/index.html 파일을 가져온다.
가상으로 호스팅 되는 docroot 설정은 대부분의 웹 서버에서 간단하다.
가상 호스팅에 대해 더 자세히 알고 싶다면 18장의 '가상 호스팅'을 보라.
사용자 홈 디렉토리 docroots
docroot의 또 다른 대표적인 활용은, 사용자들이 한 대의 웹 서버에서 각자의 개인 웹 사이트를 만들 수 있도록 해주는 것이다.
보통 빗금(/)과 물결표(~) 다음에 사용자 이름이 오는 것으로 시작하는 URI는 그 사용자의 개인 문서 루트를 가리킨다.
개인 docroot는 주로 사용자 홈 디렉터리 안에 있는 public_html로 불리는 디렉터리지만, 설정에 따라 다르다.

5.7.2 디렉토리 목록

웹 서버는 경로가 파일이 아닌 디렉터리를 가리키는, 디렉터리 URL에 대한 요청을 받을 수 있다. 대부분의 웹 서버는 클라이언트가 디렉터리 URL을 요청했을 때 다음과 같이 몇 가지 행동을 취하도록 설정할 수 있다.
에러를 반환한다.
디렉터리 대신 특별한 '색인 파일'을 반환한다.
디렉터리를 탐색해서 그 내용을 담은 HTML 페이지를 반환한다.
대부분의 웹 서버는 요청한 URL에 대응되는 디렉터리 안에서 index.html 혹은 index.htm으로 이름 붙은 파일을 찾는다.
아파치 웹 서버에서, DirectoryIndex 설정 지시자를 사용해서 기본 디렉터리 파일로 사용될 이름의 집합을 설정할 수 있다.
다음 설정은 아파치가 디렉터리 URL 요청에 대한 응답으로 나열된 파일 중 하나를 찾게 만든다.
DirectoryIndex index.html index.htm home.html home,htm index.cgi
Shell
복사

5.7.3 동적 콘텐츠 리소스 매핑

웹 서버는 URI를 동적 리소스에 매핑할 수도 있다.
웹 서버들 중에서 애플리케이션 서버라고 불리는 것들은 웹 서버를 복잡한 백엔드 애플리케이션과 연결하는 일을 한다.
어떤 리소스가 동적 리소스라면, 애플리케이션 서버는 그에 대한 동적 컨텐츠 생성 프로그램이 어디에 있는지, 그리고 어떻게 그 프로그램을 실행하는지 알려줄 수 있어야 한다.

5.7.4 서버사이드 인클루드(Server-Side Includes, SSI)

만약 어떤 리소스가 서버사이드 인클루드를 포함하고 있는 것으로 설정되어 있다면, 서버는 그 리소스의 콘텐츠를 클라이언트에게 보내기 전에 처리한다.

5.7.5 접근 제어

웹 서버는 각각의 리소스에 접근 제어를 할당할 수 있다. 접근 제어되는 리소스에 대한 요청이 도착했을 때 웹 서버는 클라이언트의 IP 주소에 근거하여 접근을 제어할 수 있고 혹은 리소스에 접근하기 위한 비밀번호를 물어볼 수도 있다.

5.8 단계 5: 응답 만들기

한번 서버가 리소스를 식별하면, 서버는 요청 메서드로 서술되는 동작을 수행한 뒤 응답 메시지를 반환한다.
응답 메시지는 응답 상태 코드, 응답 헤더, 응답 본문(생성되었다면)을 포함한다.

5.8.1 응답 엔터티

만약 트랜잭션이 응답 본문을 생성한다면, 그 내용을 응답 메시지와 함께 돌려보낸다.
응답 본문의 MIME 타입을 서술하는 Content-Type 헤더
응답 본문의 길이를 서술하는 Content-Length 헤더
실제 응답 본문의 내용

5.8.2 MIME 타입 결정하기

웹 서버에게는 응답 본문의 MIME 타입을 결정해야 하는 책임이 있다.
mime.types : 웹 서버는 MIME 타입을 나타내기 위해 파일 이름의 확장자를 사용할 수 있다.(그림 5-12)
매직 타이핑(Magic typing) : 아파치 웹 서버는 각 파일의 MIME 타입을 알아내기 위해 파일의 내용을 검사해서 알려진 패턴에 대한 테이블(매직 파일이라 불린다)에 해당하는 패턴이 있는지 찾아볼 수 있다.
유형 명시(Explicit typing) : 특정 파일이나 디렉터리 안의 파일들이 파일 확장자나 내용에 상관없이 어떤 MIME 타입을 갖도록 웹 서버를 설정할 수 있다.
유형 협상(Type negotiation) : 한 리소스가 여러 종류의 문서 형식에 속하도록 설정할 수 있다.

5.8.3 리다이렉션

웹 서버는 종종 성공 메시지 대신 리다이렉션 응답을 반환한다. 리다이렉션 응답은 3XX 상태 코드로 지칭된다.
Location 응답 헤더는 콘텐츠의 새로운 혹은 선호하는 위치에 대한 URI를 포함한다.
영구히 리소스가 옮겨진 경우
리소스는 새 URL이 부여되어 새로운 위치로 옮겨졌거나 이름이 바뀌었을 수 있다. 웹 서버는 클라이언트에게 북마크를 갱신하거나 할 수 있다고 말해줄 수 있다. 301 Moved Permanently 상태 코드는 이런 종류의 리다이렉트를 위해 사용된다.
임시로 리소스가 옮겨진 경우
서버는 클라이언트를 새 위치로 리다이렉트하길 원할 것이다. 그러나 이름 변경이 임시적이기 때문에, 서버는 클라이언트가 나중에는 원래 URL로 찾아오고 북마크도 갱신하지 않기를 원한다.
303 See Other와 307 Temporary Redirect 상태 코드는 이런 종류의 리다이렉트를 위해 사용된다.
URL 증강
문맥 정보를 포함시키기 위해 재 작성된 URL로 리다이렉트한다.
상태 정보를 내포한 새 URL을 생성하고 사용자를 이 새 URL로 리다이렉트 한다.
클라이언트는 리다이렉트를 따라가서, 이번엔 상태정보가 추가된 완전한 URL을 포함한 요청을 다시 보낸다.
303 See Other와 307 Temporary Redirect 상태 코드는 이런 종류의 리다이렉트를 위해 사용된다.
부하 균형
만약 과부화된 서버가 요청을 받으면, 서버는 클라이언트를 좀 덜 부하가 걸린 서버로 리다이렉트할 수 있다.
303 See Other와 307 Temporary Redirect 상태 코드는 이런 종류의 리다이렉트를 위해 사용된다.
친밀한 다른 서버가 있을 때
웹 서버는 어떤 사용자에 대한 정보를 가질 수 있다. 서버는 클라이언트를 그 클라이언트에 대한 정보를 갖고 있는 다른 서버로 리다이렉트할 수 있다.
303 See Other와 307 Temporary Redirect 상태 코드는 이런 종류의 리다이렉트를 위해 사용된다.
디렉터리 이름 정규화
클라이언트가 디렉터리 이름에 대한 URI를 요청하는데 끝에 빗금(/)을 빠뜨렸다면, 대부분의 웹 서버는 상대경로가 정상적으로 동작할 수 있도록 클라이언트를 슬래시를 추가한 URI로 리다이렉트한다.

5.9 단계 6: 응답 보내기

웹 서버는 받을 때와 마찬가지로 커넥션 너머로 데이터를 보낼 때도 비슷한 이슈에 직면한다.
서버는 여러 클라이언트에 대한 많은 커넥션을 가질 수 있는데 그들 중 일부는 아무것도 안하고 있는 상태이고, 일부는 서버로 데이터를 보내고 있으며, 또 다른 일부는 클라이언트로 돌려줄 응답 데이터를 실어 나르고 있을 것이다.
서버는 커넥션 상태를 추적해야 하며 지속적인 커넥션은 특별히 주의해서 다룰 필요가 있다.

5.10 단계 7: 로깅

마지막으로, 트랜잭션이 완료되었을 때 웹 서버는 트랜잭션이 어떻게 수행되었는지에 대한 로그를 로그파일에 기록한다.
대부분의 웹 서버는 로깅에 대한 여러가지 설정 양식을 제공한다. 더 자세한 것은 21장을 보자.

5.11 추가 정보

아파치, 직소, ident에 대한 더 자세한 정보가 필요하면 다음을 조사해보라.
Apache: The Definitive Guide
Ben Laurie and Peter Laurie, O'Reilly & Associates, Inc.
Professional Apache
Peter Wainwright, Wrox Press.
http://www.w3c.org/Jicsaw/
Jigsaw-WeC's Server W3C Consortium Web Site.
http://www.ietf.org/rfc/rfc1413.txt
RFC 1413, "Identification Protocol", by M. St. Johns.