kakasoo

[HTTP] 13. 웹 서버 (Web server) 본문

프로그래밍/HTTP

[HTTP] 13. 웹 서버 (Web server)

카카수(kakasoo) 2020. 10. 31. 17:57
반응형

아래와 같은 내용을 다룬다. 아래 내용은 책에서 설명한 것을 그대로 따라, 이 장에서 무엇을 배울 수 있는지를 옮겨 적은 것이다.

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

또한 이해를 돕기 위해 아파치 웹 서버를 이용하는 (필요에 따라 설정 옵션도 변경해가면서) 예를 보여줄 것이다.

별도 정리 및 의문점

  • 동일한 호스트의 반복적인 접속이 있을 경우 (클라이언트 호스트 명 식별로 파악했을 때) 차단하는 기능을 구현해보자!

    1. 리눅스에서는 이런 것도 가능하다.

       var child_process = require("child_process");
      
       child_process.exec("hostname -f", function(err, stdout, stderr) {
         var hostname = stdout.trim();
       });

5.1 다채로운 웹 서버

웹 서버는 HTTP 요청을 처리한다. 여기서 웹 서버는 대게 웹 서버 소프트웨어와 웹 페이지 제공에 특화된 장비 양쪽 모두를 가리킨다.

크기는 모두 제각각이니 넘어간다. (공유기를 웹에서 관리할 수 있게 된 것은, 마이크로프로세서의 발전 덕분에, 기기 안에 작은 서버 칩셋을 넣을 수 있게 된 덕분이다.)

5.1.1 웹 서버 구현

웹 서버는 HTTP와 TCP 처리를 구현한 것이다. 웹 서버는 자신이 제공하는 리소스를 관리하고 웹 서버의 설정, 통제 , 확장을 위한 관리 기능을 제공한다.

또한 웹 서버는 TCP 커넥션 관리에 대한 책임을 나눠 갖는다.

운영체제는 컴퓨터 시스템인 하드웨어를 관리하고 TCP/IP 네트워크 지원, 리소스를 유지하기 위한 파일 시스템, 현재 연산 활동을 제어하기 위한 프로세스 관리를 제공한다.

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

아파치나 W3C 직소와 같은 오픈 소스 소프트웨어 ( 또는 Express도 포함될 것으로 보인다.)

5.1.3 임베디드 웹 서버

소비자용 제품에 내장될 목적으로 만들어진 작은 웹 서버 (프린터나 가전제품, 공유기 등에 들어간다.)

5.2 간단한 펄 웹 서버

수만 줄 짜리 서버가 아니더라도 몇십 줄 짜리 간단한 서버를 작성할 수 있다. (나는 C언어로 해본 적이 있는데, 여기서 말하는 펄(perl)도 C언어처럼, 그러한 언어 중 하나이다.)

실제 코드는 생략한다. (원한다면 C언어 코드를 둘러보고 가시길.)

5.3 진짜 웹 서버가 하는 일

최신식 상용 서버는 매우 복잡하지만, 공통적으로 하는 일은 아래의 7가지이다.

  1. 커넥션을 맺는다 ; 클라이언트의 접속을 받아들이거나, 원치 않는 클라이언트의 접속을 거부한다.
  2. 요청을 받는다 ; HTTP 요청 메시지를 네트워크로부터 읽어 들인다.
  3. 리소스에 접근한다 ; 메시지에서 지정한 리소스에 접근한다.
  4. 응답을 만든다 ; 올바른 헤더를 포함한 HTTP 응답 메시지를 생성한다.
  5. 응답을 보낸다 ; 응답을 클라이언트에게 돌려준다.
  6. 트랙잭션을 로그로 남긴다 ; 로그 파일에 트랜잭션 완료에 대한 기록을 남긴다.

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

클라이언트와 서버 사이에 지속 커넥션이 없다면 클라이언트는 서버에 대한 새 커넥션을 열 필요가 있다. 이 내용은 4장에서 설명하였다.

5.4.1 새 커넥션 다루기

클라이언트가 TCP 커넥션을 요청하면 서버는 커넥션을 맺고, TCP 커넥션에서 IP를 추출하여 커넥션 맞은 편에 어떤 클라이언트가 있는지 확인한다.

일단 연결이 되면 서버는 커넥션 목록에 클라이언트를 넣고 커넥션에 오가는 데이터를 지켜본다.

웹 서버는 어떤 커넥션이든 마음대로 거절하거나 즉시 닫을 수 있다. 예컨대 악의적인 클라이언트.

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

대부분의 웹 서버는역방향 DNS (reverse DNS)를 사용하여 클라이언트의 IP 주소를 클라이언트 호스트 명으로 변환하도록 설정되어 있다.

웹 서버는 클라이언트 호스트 명을 구체적인 접근 제어와 로깅을 위해 사용할 수 있다. 다만 이러한 호스트 명 룩업 과정은 시간이 꽤 걸려서 전체 웹 트랙잭션을 느리게 할 우려가 있다.

많은 대용량 웹 서버는 식별을 구태여 하지 않거나 특정 콘텐츠에 대해서만 사용한다.

node js 에서는 dns.reverse 라는 함수가 있다.

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

서비스를 느리게 할 뿐더러 클라이언트가 지원하지 않는 경우, 또는 차단한 경우가 많아서 흔히 사용되지 않는다. 생략한다.

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

커넥션에 데이터가 도착하면 웹 서버는 그 데이터를 읽어 들이고 파싱하여 요청 메시지를 구성한다.

  • 요청 줄을 파싱하여 오청 메서드, 지정된 리소스의 식별자(URI), 버전 번호를 찾는다. 각 값은 스페이스 한 개로 구분되어 있으며, 요청 줄은 캐리지 리턴 줄바꿈으로 끝난다.
  • 메시지 헤더들을 읽는다. 각 헤더는 캐리지 리턴으로 끝난다.
  • 헤더의 끝을 의미하는 캐리지 리턴으로 끝나는 빈 줄을 찾아낸다.
  • 요청 본문이 있다면 읽어들인다. 길이는 Content-Length 헤더로 정의된다.

웹 서버는 메시지가 파싱이 가능한 수준이 될 때까지 메모리에 저장해둔다.

5.5.1 메시지 내부 표현

몇몇 웹 서버는 요청 메시지를 쉽게 다루기 위해 내부의 자료구조에 저장한다. (이것은 Express도 해당하는 것으로 보인다. 다양한 메서드와 프로퍼티로 접근이 가능하다.)

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

고성능 웹 서버는 수천 개의 커넥션을 동시에 열 수 있도록 지원한다. 이 커넥션은 웹 서버가 전 세계 클라이언트들과 각각 한 개 이상의 커넥션을 통해 통신할 수 있게 해준다.

어떤 커넥션들로부터는 요청이 느리게 혹은, 드물게 흘러 들어오고 어떤 것들은 나중에 일어날 활동을 위해 기다리고 있는 데 비해 일부 커넥션은 급속히 요청을 보내고 있을 것이다.

(설명이 좀 중구난방한데, 간단히 설명하자면, 차례대로 줄 세우면 맨 앞에 무거운 트랜잭션을 모두가 기다려야 하니, 다른 방법이 필요하다는 것을 말하는 중이다.)

아래는 웹 서버들을 처리 방식에 따라 구분한 것이다.

  • 단일 스레드 웹 서버 : 한 번에 하나의 요청을 처리한다. 순서가 밀리면 모두 대기해야 하기 때문에 간단한 진단 도구에서만 사용한다.
  • 멀티 프로세스와 멀티 스레드 웹 서버 : 미리 만들거나, 필요할 때마다 만든 여러 개의 프로세스, 혹은 고효율 스레드를 할당한다. 많은 메모리와 시스템 리소스가 필요하다. (개수 제한을 걸기도 한다.)
  • 다중 I/O 서버 : 다중 커넥션을 지원한다. 커넥션의 상태가 바뀌면 (데이터를 사용할 수 있게 되거나 에러가 발생하면) 작은 양의 처리를 실행한다.
  • 다중 멀티 스레드 웹 서버 : CPU가 여러 개인 서버. (하드웨드 레벨에서 서버인 경우)

5.6 단계 3: 요청 처리

웹 서버는 요청을 받으면 응답을 만든다. 이 과정을 요청 처리라고 하는데, 여기서는 설명하지 않는다. 왜냐하면 요청 처리가, 이 책의 나머지 전부이기 때문이다. (앞으로 계속 설명될 것이다.)

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

웹 서버는 요청에 맞는 리소스를 찾아서 클라이언트에게 주어야 한다.

5.7.1 Docroot

웹 서버는 여러 종류의 리소스를 매핑한다. 가장 단순한 형태는 요청 URL를 웹 서버의 파일 시스템 안에 있는 파일 이름으로 사용하는 것이다.

일반적으로 웹 서버 파일 시스템의 특별한 폴더를 웹 콘텐츠를 위해 예약해둔다. 이 폴더는 문서 루트 (Docroot)라고 부른다. 웹 서버는 요청 메시지에서 URI를 가져와서 문서루트 뒤에 붙인다.

(Express에서는 public에 해당하는 부분, static, 즉 정적 폴더를 의미하는 것 같다.)

서버는 상대적인 URI가 Docroot를 벗어나서 파일 시스템의 Docroot 이외 부분이 노출되지 않도록 해야 한다. 예컨대 이런 경우를 거부해야 한다. www.naver.com/../여기는_뭐가_있을까.jpg

가상 호스팅된 Docroot

웹 서버는 각 사이트마다 그들만의 분리된 문서 루트를 줌으로써 웹 서버에 여러 개의 웹 사이트를 호스팅한다. 이 방법으로 두 사이트 이상이 완전히 분리된 콘텐츠를 가지게 할 수 있다.

사용자 홈 디렉터리 Docroots

빗금(/)과 물결표(~) 다음에 사용자 이름이 오는 것으로 시작하는 URI는 보통 그 사용자의 개인 문서 루트를 가리킨다.

5.7.2 디렉터리 목록

웹 서버는 경로가 파일이 아닌 디렉터리를 가리키는, 디렉터리 URL에 대한 요청을 받을 수 있다. 웹 서버는 이런 요청에 대해 아래처럼 대응할 수 있다.

  1. 에러를 반환한다.
  2. 디렉터리 대신 특별한 '색인 파일'을 반환한다.
  3. 디렉터리를 탐색하여 그 내용을 담은 HTML 페이지를 반환한다.

대부분의 웹 서버는 요청한 URL에 대응되는 디렉터리 안에서 index.html 혹은 index.htm으로 이름 붙은 파일을 찾는다.

5.7.3 동적 콘텐츠 리소스 매핑

웹 서버는 URI를 동적 리소스에 매핑할 수도 있다. 즉 요청에 맞게 콘텐츠를 생성하는 프로그램에 URI를 매핑하는 것이다. 웹 서버 중 애플리케이션 서버들은 웹 서버를 백엔드 애플리케이션과 연결한다.

만약 어떤 리소스가 동적 리소스라면 애플리케이션 서버는 그에 대한 동적 콘텐츠 생성 프로그램이 어디에 있는지, 어떻게 그 프로그램을 실행할 수 있는지 알려주어야 한다.

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

많은 웹 서버가 서버사이트 인클루드도 지원한다. 이는, 서버가 리소스의 콘텐츠를 클라이언트에게 보내기 전에 처리하는 것을 의미한다.

서버는 콘텐츠에 변수 이름이나 내장된 스크립트가 될 수 있는 어떤 특별한 패턴이 있는지 검사를 받는다. 이것은 동적 콘텐츠를 만드는 쉬운 방법이다.

5.7.5 접근 제어

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

5.8 단계 5: 응답 만들기

한 번 서버가 리소스를 식별하면, 서버는 응답 메시지를 반환한다. 응답 메시지는 응답 상태코드, 응답 헤더, 응답 본문을 포함한다고 이야기한 바 있다.

5.8.1 응답 엔터티

만약 트랜잭션이 응답 본문을 생성한다면, 본문은 아래를 포함한다.

  • 응답 본문의 MIME (Muti purpose Internet Mail Extensions) 타입을 서술하는 Content-Type 헤더
  • 응답 본문의 길이를 서술하는 Content-Length 헤더
  • 실제 응답 본문의 내용

5.8.2 MIME 타입 결정하기

웹 서버에게는 응답 본문의 MIME 타입을 결정해야 하는 책임이 있다.

mime.types

웹 서버는 MIME 타입을 나타내기 위해서 파일 이름의 확장자를 사용할 수 있다. 웹 서버는 확장자별 MIME 타입이 담겨있는 파일을 탐색한다.

매직 타이핑 (Magic typing)

파일의 내용을 검사하여 알려진 패턴에 대한 테이블에 해당하는 패턴이 있는지를 찾는다. 느리지만 표준 확장자가 없는 경우에 유용하다.

유형 명시 (Explicit typing)

특정 파일이나 디렉터리 안의 파일들이 파일 확장자나 내용에 관계 없이 어떤 MIME 타입을 갖도록 웹 서버를 설정할 수 있다.

유형 협상 (Type negotiation)

어떤 웹 서버는 한 리소스가 여러 종류의 문서 형식에 속하도록 설정할 수 있다. 이 때 웹 서버가 사용자와의 협상 과정을 통해 파일 형식을 직접 결정할 수 있게 설정할 수도 있다.

5.8.3 리다이렉션

웹 서버는 종종 성공 메시지 대신에 리다이렉션 응답을 반환하기도 한다. 웹 서버는 요청을 수행하기 위해 브라우저가 다른 곳에 가도록 리다이렉트할 수 있다.

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: 로깅

웹 서버는 트랜잭션이 어떻게 수행되었는지에 대한 로그를 로그 파일에 기록한다.

반응형