5장

1. 웹 서버란 무엇인가?

웹 서버(이하 서버)는 클라이언트가 보내는 HTTP 요청을 처리하고 응답을 돌려준다. 웹 서버는 HTTP 및 그와 관련된 TCP 처리를 구현한 것으로 TCP 커넥션 관리에 대한 책임을 운영체제와 나눠 갖는다. 운영체제는 컴퓨터 시스템의 하드웨어를 관리하고 TCP/IP 네트워크 지원, 웹 리소스를 유지하기 위한 파일 시스템, 현재 연산 활동을 제어하기 위한 프로세스 관리를 제공한다.

2. 웹 서버의 동작 과정

웹 서버의 동작 과정은 다음과 같다.

  1. 커넥션 맺기: 클라이언트의 접속을 수락 혹은 거절

  2. 요청 받기: HTTP 요청 메세지를 네트워크에서 읽음

  3. 요청 처리: 요청 메세지를 해석하고 행동

  4. 리소스 매핑: 메세지에서 지정한 리소스에 접근

  5. 응답 만들기: 올바른 헤더를 포함한 HTTP 응답 메세지 생성

  6. 응답 보내기: 응답을 클라이언트에 돌려줌

  7. 로깅: 로그 파일에 트랜잭션 완료 기록을 남기기

2.1. 커넥션 맺기

4장에서 살펴본 것처럼 클라이언트와 서버는 HTTP 메세지를 주고 받기 위해 커넥션을 맺는다. 이미 커넥션을 맺은 경우 클라이언트는 기존 커넥션을 사용해 서버와 메세지를 주고 받는다. 커넥션 수락 단계에서 웹 서버가 하는 일은 다음과 같다.

  • IP 주소로 신규 커넥션 확인

  • 역방향 DNS로 클라이언트 호스트 명 식별

  • ident를 활용한 클라이언트 식별

웹 서버는 클라이언트가 요청한 TCP 커넥션에서 IP 주소를 추출해 클라이언트 정보를 확인한다. 만일 클라이언트의 IP 주소나 호스트 명이 인가되지 않았거나 악의적이라고 판단할 경우 커넥션이 거절될 수 있다. 웹 서버는 클라이언트와 커넥션을 맺은 후 새 커넥션을 커넥션 목록에 추가한다. 그리고 커넥션에서 오가는 데이터를 지켜본다. 웹 서버는 어떤 커넥션이든 마음대로 거절하거나 닫을 수 있음을 참고하자. 대부분의 웹 서버는 역방향 DNS(reverse DNS)를 사용해서 클라이언트의 IP 주소를 호스트 명으로 변환한다. 변환된 호스트 명은 접근 제어와 로깅을 위해 사용한다. 다만 호스트 명 룩업(hostname lookup)은 시간이 소모되므로 트랜잭션을 느리게 만든다. 따라서 일부 컨텐츠만 켜두는 등의 방법을 사용한다. IETF ident 프로토콜로 클라이언트 정보를 식별할 수 있다. ident 프로토콜은 서버에게 어떤 사용자 이름이 HTTP 커넥션을 초기화했는지 찾아낼 수 있게 도와준다. 이를 위해서는 클라이언트가 ident 프로토콜을 지원해야 하므로 실제로 자주 쓰이지는 않는다.

2.2. 요청 받기

커넥션에 데이터가 도착하면 웹 서버는 데이터를 읽어들이고 파싱해서 요청 메세지를 구성한다. 요청 메세지를 파싱할 때 웹 서버가 하는 일은 다음과 같다.

  • 요청줄을 파싱해서 요청 메서드, URI, 버전 번호를 탐색

    • 버전 번호는 HTTP/0.9에서는 지원하지 않음

    • 각 값은 스페이스 한 개로 분리

    • 요청 줄은 캐리지 리턴 줄바꿈(CRLF/LF) 문자열

  • 메세지 헤더를 읽음

    • 메세지 헤더는 CRLF로 끝남

  • 헤더의 끝을 의미하는 CRLF 탐색(존재한다면)

  • 요청 본문이 있다면 읽어 들임

    • 길이는 Content-Length 헤더로 정의

웹 서버가 커넥션을 통해 전달받는 데이터는 불규칙적이다. 또한 네트워크 커넥션은 언제든 무효화될 수 있다. 따라서 웹 서버는 파싱 가능한 분량을 확보하기 위해서 메세지 일부분을 메모리에 임시로 저장한다.

웹 서버는 요청 메세지를 내부의 자료 구조에 저장할 수 있다. 자료구조에 저장한 데이터는 신속하게 접근 가능하다.

고성능의 웹 서버는 수천 개의 커넥션을 동시에 열 수 있다. 수천 개의 요청을 처리하기 위해 웹 서버는 다양한 아키텍처를 활용한다. 단일 스레드 웹 서버는 한 번에 하나의 요청을 처리하고 해당 요청이 완료되면 다음 요청을 처리한다. 간단하게 구현할 수 있으나 처리 도중 다른 커넥션이 무시되므로 성능 문제가 발생한다. 따라서 로드가 적은 서버, type-o-serve와 같은 진단 도구에 사용한다. 멀티 프로세스와 멀티 스레드 웹 서버는 여러 개의 프로세스와 스레드를 할당해서 동시에 요청을 처리한다. 동시에 많은 요청을 처리할 수 있지만, 프로세스와 스레드가 시스템의 메모리나 리소스를 낭비한다는 문제가 발생한다. 따라서 스레드와 프로세스의 최대 개수를 제한해서 사용한다. 다중 I/O 서버는 커넥션을 열린 커넥션 목록(worker pool)에 집어넣고 커넥션 활동을 감시하며 상태가 바뀔 때마다 트랜잭션을 처리한다.실제로 요청이 발생할 때 작업을 수행하므로 효율적이다. 따라서 대부분의 웹 서버가 다중 I/O 아키텍처를 채택하고 있다. 다중 멀티스레드 웹 서버는 다중 I/O 서버를 멀티스레드로 구현한 것으로 여러 스레드가 각각 열려 있는 커넥션을 감시하고 작업을 수행한다.

2.5. 요청 처리

웹 서버는 요청 메세지에서 메서드, 리소스, 헤더, 본문을 읽고 작업을 시작한다.

2.6. 리소스 매핑

웹 서버는 클라이언트에게 콘텐츠를 전달하기 위해 리소스를 찾거나 생성한다. 웹 서버는 요청에 따라 여러 종류의 리소스 매핑을 지원한다. 또한 각각의 리소스에 대해 접근을 제한할 수 있다.

2.6.1. 정적 리소스 매핑

URI를 파일 이름으로 사용하는 건 가장 간단한 매핑 방식이다. 웹 서버는 docroot 뒤에 요청 메세지의 URI를 붙여서 파일을 찾을 수 있다. docroot(문서 루트)는 웹 서버의 파일 시스템 안에 있는 리소스 폴더 경로를 의미한다. 웹 서버 구현 시 URI가 docroot를 벗어나서 접근하지 않도록(노출되지 않도록) 주의해야 한다.

가상 호스팅 웹 서버로 분리된 문서 루트를 할당해 하나의 웹 서버에서 여러 개의 웹 사이트를 호스팅할 수 있다. 가상 호스팅 웹 서버는 URI나 Host 헤더에서 얻은 IP 주소나 호스트 명을 이용해 올바른 문서 루트를 식별한다. 혹은 슬래시(/)와 틸트(~)를 사용한 경로로 한 대의 웹 서버를 각자의 개인 웹 사이트로 활용할 수 있다. 웹 서버는 디렉터리 URI 요청을 받아 다음 작업 중 하나를 수행한다.

  • 에러 반환

  • 디렉터리 대신 특별한 색인 파일(index.html) 반환

  • 디렉터리를 탐색한 내용을 담은 HTML 페이지 반환

    • 발견할 수 없는 파일도 드러나게 되므로 주의 필요

2.6.2. 동적 리소스 매핑

SSI(Server-Side Includes, 서버 사이드 인클루드)는 서버가 리소스의 콘텐츠를 클라이언트에게 보내기 전에 처리하는 걸 의미한다. 서버는 콘텐츠에 변수 이름이나 내장된 스크립트가 될 수 있는 특별한 패턴이 있는지 검사한 뒤 변수 값이나 실행 가능한 스크립트 출력 값으로 치환한다.

2.7. 응답 만들기

웹 서버는 리소스를 식별하고 요청을 수행한 뒤 응답을 반환한다. 이 때 응답 메세지는 응답 상태 코드, 응답 헤더, 응답 본문을 포함한다.

2.7.1. 응답 엔티티

본문이 있는 경우 응답 메세지는 아래 내용을 포함한다.

  • 응답 본문의 MIME 타입을 서술하는 Content-Type 헤더

  • 응답 본문의 길이를 서술하는 Content-Length 헤더

  • 실제 응답 본문의 내용

2.7.2. MIME 타입 결정하기

웹 서버는 응답 본문의 MIME 타입을 결정한다. MIME 타입과 리소스를 연결하는 방법은 다음과 같다.

  • 매직 타이핑(Magic typing): 리소스의 내용을 검사해서 패턴 확인 후 MIME 연결

  • 유형 명시(Explicit typing): 파일 확장자나 내용과 상관없이 리소스의 MIME 결정

  • 유형 협상(Type negotiation): 리소스가 여러 형식을 가지고 웹 서버가 가장 좋은 형식을 결정

2.7.3. 리다이렉션

웹 서버는 다음과 같은 상황에서 리다이렉션을 반환할 수 있다.

  • 영구히 리소스가 옮겨진 경우

  • 임시로 리소스가 옮겨진 경우

  • URL 증강

  • 부하 균형

  • 친밀한 다른 서버가 있음

  • 디렉터리 이름 정규화

2.8. 응답 보내기

데이터를 받을 때와 마찬가지로 웹 서버는 데이터를 보낼 때 비슷한 문제를 겪는다. 서버는 커넥션 상태를 추적하고 있기 때문에 주의가 필요하다.

2.9. 로깅

트랜잭션이 완료되면 로그를 로그 파일에 기록한다. 대부분의 웹 서버는 로깅에 대한 설정 양식을 제공한다.

Last updated