HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
👻
개발 기록
/
🐿️
HTTP 완벽 가이드
/
📘
5. 웹 서버
📘

5. 웹 서버

웹 서버란?

  • HTTP 요청을 처리하고 응답을 제공함.
  • HTTP 및 그와 관련된 TCP 처리를 구현한 것.
    • TCP 커넥션 관리에 대한 책임을 운영체제와 나눠 갖음.
    • 운영체제는 컴퓨터 시스템의 하드웨어를 관리하고 TCP/IP 네트워크 지원, 웹 리소스를 유지하기 위한 파일 시스템, 현재 연산활동을 제어하기 위한 프로세스 관리를 제공함.
  • HTTP 프로토콜 구현, 웹 리소스 관리, 웹 서버 관리.
  • 웹 서버 소프트웨어와 웹페이지 제공에 특화된 장비(ex 컴퓨터) 모두를 가리킴.
  • 기능, 형태, 크기가 다양함.
 

웹 서버 형태

  • 다목적 소프트웨어 웹 서버
    • 네트워크에 연결된 표준 컴퓨터 시스템에서 동작함.
    • 아파치, W3C의 직소 같은 오픈 소스 소프트웨어, 마이크로소프트나 아이플래닛의 웹 서버 같은 상용 소프트웨어를 사용할 수 있음.
  • 임베디드 웹 서버
    • notion image
    • 일반 소비자용 제품에 내장될 목적으로 만들어진 작은 웹 서버 (ex. 프린터, 가전제품)
    • 기기를 간편한 웹 브라우저 인터페이스로 관리할 수 있게 해줌.
  • Perl로 최소 기능 웹 서버를 구현해보자.
#!/usr/bin/perl use Socket; use Carp; use FileHandle; # (1) use port 8080 by default, unless overridden on command line $port = (@ARGV ? $ARGV[0] : 8080); # (2) create local TCP socket and set it to listen for connections $proto = getprotobyname('tcp'); socket(S, PF_INET, SOCK_STREAM, $proto) die; setsockopt(S, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) die; bind(S, sockaddr_in($port, INADDR_ANY)) die; listen(S, SOMAXCONN) die; # (3) print a startup message printf(" <<<Type-O-Serve Accepting on Port %d>>>\n\n",$port); while (1) { # (4) wait for a connection C $cport_caddr = accept(C, S); ($cport,$caddr) = sockaddr_in($cport_caddr); C->autoflush(1); # (5) print who the connection is from $cname = gethostbyaddr($caddr,AF_INET); printf(" <<<Request From '%s'>>>\n",$cname); # (6) read request msg until blank line, and print on screen while ($line = <C>) { print $line; if ($line =~ /^\r/) { last; } } # (7) prompt for response message, and input response lines, # sending response lines to client, until solitary "." printf(" <<<Type Response Followed by '.'>>>\n"); while ($line = <STDIN>) { $line =~ s/\r//; $line =~ s/\n//; if ($line =~ /^\./) { last; } print C $line . "\r\n"; } close(C); }
 
% type-o-serve.pl 8080
 
notion image
 

진짜 웹 서버가 하는 일

  • 최신식 상용 웹 서버는 앞선 예제보다 훨씬 복잡하지만, 공통적으로 몇 가지 일을 수행함.
    • notion image
      1. 커넥션을 맺는다. → 클라이언트의 접속을 받아들이거나, 원치 않는 클라이언트라면 닫는다.
      1. 요청을 받는다. → HTTP 요청 메시지를 네트워크로부터 읽어드린다.
      1. 요청을 처리한다. → 요청 메시지를 해석하고 행동을 취한다.
      1. 리소스에 접근한다. → 메시지에서 지정한 리소스에 접근한다.
      1. 응답을 만든다. → 올바른 헤더를 포함한 HTTP 응답 메시지를 생성한다.
      1. 응답을 보낸다. → 응답을 클라이언트에게 돌려준다.
      1. 트랜잭션을 로그로 남긴다. → 로그파일에 트랜잭션 완료에 대한 기록을 남긴다.
 

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

  • 클라이언트가 이미 서버에 대해 열려있는 지속적 커넥션을 갖고 있다면, 클라는 그 커넥션으로 요청을 보낼 수 있음.
  • 그렇지 않다면, 클라는 서버에 새 커넥션을 열어야 함.
  • 클라가 웹 서버에 TCP 커넥션을 요청하면, 웹 서버는 그 커넥션을 맺고 TCP 커넥션에서 IP 주소를 추출하여 커넥션 맞은편에 어떤 클라가 있는지 확인함.
  • 새 커넥션이 맺어지고 받아들여지면, 서버는 새 커넥션을 커넥션 목록에 추가하고 커넥션에서 오가는 데이터를 지켜보기 위한 준비를 함.
  • 웹 서버는 어떤 커넥션이든 마음대로 거절하거나 닫을 수 있음.
    • 클라의 IP 주소나 호스트 명이 인가되지 않았거나 악의적이라고 알려진 것일 경우 커넥션을 닫음.
  • 클라의 신원을 식별하는 기법은 다음같은 것들이 있음.
    • 클라 호스트 명 식별
      • hostname lookup으로 클라의 IP 주소를 클라의 호스트 명으로 변환하도록 설정함 (IP 로 Domain Name 을 찾는 기능).
      • 웹 서버는 클라의 호스트 명을 구체적인 접근 제어와 로깅을 위해 사용할 수 있음.
      • hostname lookup은 시간이 걸릴 수 있어 웹 트랜잭션을 느리게할 수 있음. 많은 대용량 웹 서버는 이를 꺼두거나 특정 콘텐츠에 대해서만 켜놓음.
    • ident로 클라 사용자 알아내기
      • ident 프로토콜은 서버에게 어떤 사용자 이름이 HTTP 커넥션을 초기화했는지 찾아낼 수 있게 해줌.
      • 만약 클라가 ident 프로토콜을 지원한다면, 클라는 ident를 보내기 위해 서버와 포트 113번으로 커넥션을 맺고 간단한 요청을 함.
      • 하지만 ident는 조직 내부에서 사용하는 경우만 있고, 공공 인터넷에서는 다음의 이유로 동작하지 않음.
        • 많은 클라 PC의 미지원, 가상 IP 주소 미지원
        • HTTP 트랜잭션 지연
        • 조작하기 쉬움, 클라 사용자 이름 노출로 인한 사생활 침해
 

2: 요청 메시지 수신

  • 커넥션에 데이터가 도착하면, 웹 서버는 네트워크 커넥션에서 그 데이터를 읽어 들이고 파싱하여 요청 메시지를 구성함.
  • 요청 메시지 파싱 시 다음과 같은 일을 함.
    • 요청 줄을 파싱 하여 요청 메서드, URI, HTTP 버전을 찾는다.
    • 메시지 헤더를 읽는다. 각 헤더는 CRLF(캐리지 리턴 줄바꿈)로 끝난다.
    • 헤더 끝을 의미하는 CRLF로 끝나는 빈 줄을 찾아낸다.
    • 요청 본문이 있으면 읽어 드린다.(길이는 Content-Length 헤더에 정의되어 있음)
💡
요청 메시지를 파싱 할 때 웹 서버는 입력 데이터를 네트워크로부터 불규칙으로 받고 그 커넥션은 언제라도 무효화될 수 있으므로 메모리에 임시 저장해둘 필요가 있음.
속도가 빠른 룩업 테이블에 저장되어 각 필드에 신속하게 접근함.
속도가 빠른 룩업 테이블에 저장되어 각 필드에 신속하게 접근함.
  • 웹 서버는 고성능일 경우 수천 개의 커넥션을 동시에 열 수 있도록 지원하는 등 커넥션 입출력 처리 아키텍처에 따라 요청을 처리하는 방식이 달라짐.
    • notion image
    • 단일 스레드 웹서버 방식:한 번에 한 번의 요청을 처리하므로 트랜잭션이 완료되면 다음 커넥션을 처리하므로 처리 도중에 온 커넥션은 무시된다. 그러므로 로드가 작은 서버나 type-o-serve와 같은 진단 도구에서만 적당하다.
    • 멀티 프로세스와 멀티스레드 웹서버:여러 개의 프로세스나 고효율 스레드를 할당한다. 커넥션 마다 스레드를 할당하면 너무 많은 메모리나 시스템 리소스를 소비하므로 최대 개수에 제한을 건다.
    • 다중 I/O 서버:대량의 커넥션 처리를 위해 많은 웹서버가 선택한 방식이다. 모든 커넥션은 하나의 프로세스에 감시당하며 실제로 해야 할 일이 있을 때만 커넥션을 열어준다.
    • 다중 멀티스레드 웹 서버:멀티스레딩과 다중화를 결합한 방식
💡
프로세스 : 어떤 프로그램의 자신만의 변수 집합을 갖는 하나의 독립된 제어 흐름. 스레드 : 프로세스의 더 빠르고 효율적인 버전. → 이 둘 모두 하나의 프로그램이 여러 작업을 동시에 할 수 있게 해줌.
 

3: 요청 처리

  • 웹 서버가 요청을 받으면, 서버는 요청으로부터 메서드, 리소스, 헤더, 본문을 얻어내어 처리함.
 

4: 리소스의 매핑과 접근

  • 웹 서버가 클라에 콘텐츠 등의 리소스를 전달하려면, 요청 메시지의 URI에 대응하는 알맞은 콘텐츠나 콘텐츠 생성기를 웹 서버에서 찾아 그 콘텐츠의 원천을 식별해야 함.
  • docroot
    • 일반적으로 웹 콘텐츠를 위해 웹 서버 파일 시스템의 폴더를 예약하는 데 이 문서를 의미함.
    • 웹 서버는 요청 메시지에서 URI를 가져와 문서 루트 뒤에 붙임.
    • 요청 URI : /specials/saw-blade.gif docroot : /user/local/httpd/files 서버 리소스 : /user/local/httpd/files/specials/saw-blade.gif
      이때 ../와 같이 문서 루트 위의 파일을 보려고 하는 URI는 허용하지 말아야 함.
    • 가상 호스팅된 docroot를 만들어 두 웹 사이트를 HTTP host 헤더나 서로 다른 IP 주소를 이용해 구분할 수 있음.
    • 사용자 홈 디렉터리 docroots로 사용자들이 한 대의 웹 서버에서 각자의 개인 웹 사이트를 만들도록 할 수 있음. 슬래쉬(/)와 물결(~) 다음에 사용자 이름이 오는 것으로 시작함. ex) /~lillie/index.html
  • 디렉터리 목록
    • 웹서버는 경로가 파일이 아닌 디렉터리를 가리키는 URL 요청을 받을 수 있음.
    • 그 경우 보통 다음과 같은 행동을 취한다.
      • 에러를 반환함.
      • 디렉터리 대신 특별한 '색인 파일'을 반환함.
      • 디렉터리를 탐색해서 그 내용을 담은 HTML 페이지를 반환함.
      • 보통 그 디렉터리 안의 index.html을 반환함.
      • DirectoryIndex index.html index.htm home.html home.htm과 같이 아파치 웹 서버에서 아파치가 디렉터리 URL 요청에 대한 응답으로 나열된 파일 중 하나를 찾게 함.
      • Options -Indexes를 통해 디렉터리 색인 파일 자동 생성을 끌 수도 있음. 열거한 HTML이 반환됐을 때 발견할 수 없는 파일도 드러나게 된다는 단점이 있음.
  • 동적 콘텐츠 리소스 매핑
    • 웹 서버는 요청에 맞게 콘텐츠를 생성하는 프로그램에 URL를 매핑할 수 있음 (동적 리소스 매핑).
    • 예를 들어, ScriptAlias /cgi-bin/ /usr/local/etc/httpd/cgi-programs/는 URI 경로가 /cgi-bin/으로 시작한다면 /usr/local/etc/httpd/cgi-programs/에서 프로그램을 찾아 실행하라는 의미임.
    • AddHandlercgi-script .cgi처럼 특정 확장자의 파일만 실행하도록 설정할 수 있음.
    • 오늘날은 마이크로소프트의 액티브 서버 페이지와 자바 서블릿과 같은 한층 더 강력하고 효과적인 서버사이드 동적 콘텐츠 지원 기능을 갖고 있음.
  • Server-Side Includes (SSI)
    • 만약 어떤 리소스가 SSI를 포함하고 있으면 서버는 그 리소스의 콘텐츠를 클라이언트에게 보내기 전에 처리함.
  • 접근 제어
    • 웹 서버는 각각의 리소스에 접근 제어를 할당할 수 있음.
    • 접근이 제어된 리소스에 대한 요청이 도착했을 때 웹 서버는 클라의 IP주소에 근거해 접근을 제어할 수 있고 혹은 리소스에 접근하기 위한 비밀번호를 물어볼 수 있음.
 

5: 응답 만들기

  • 한번 서버가 리소스를 식별하면, 서버는 요청 메서드로 서술되는 동작을 수행한 뒤 응답 메시지를 반환함.
    • 응답 상태 코드, 응답 헤더, (생성될 경우) 응답 본문
  • 만약 트랜잭션이 응답 본문을 생성한다면, 그 내용을 다음을 포함한 응답 메시지와 함께 돌려보냄.
    • 응답 본문의 MIME 타입을 서술하는 Content-Type 헤더.
    • 응답 본문의 길이를 서술하는 Content-Length 헤더.
    • 실제 응답 본문 내용.
  • 웹 서버는 응답 본문의 MIME 타입을 결정해야 함.
MIME 타입 테이블 예시.
MIME 타입 테이블 예시.
  • 이 외에도 매직 타이핑, 유형 명시, 유형 협상 등 특정한 MIME 타입을 갖도록 설정할 수 있음.
 
  • 웹 서버는 종종 성공 메시지 대신 리다이렉션 응답을 반환함.
  • 리다이렉션 응답은 3XX 상태 코드로 지칭됨.
  • Location 응답 헤더는 콘텐츠의 새로운 혹은 선호하는 위치에 대한 URI를 포함함.
  • 유용하게 사용되는 경우
    • 영구히 리소스가 옮겨진 경우: 301(Moved Permanently) 상태 코드를 사용
    • 임시로 리소스가 옮겨진 경우: 임시적이므로 서버는 클라이언트가 나중에 원래 URL로 찾아올 수 있도록 북마크를 갱신하지 않기를 원한다. 303(See Other), 307(Temporary Redirect) 상태 코드가 주로 이럴 때 사용된다.
    • URL 증강: 303, 307 상태 코드를 사용하며 보통 상태 정보를 내포한 채 새 URL을 생성함. 트랜잭션 간 상태를 유지하는 유용한 방법.
    • 부하 균형: 과부하된 서버 요청을 받으면 부하가 적은 서버로 리다이렉트 시켜주는 것으로 303, 307을 사용한다.
    • 친밀한 다른 서버가 있을 때: 303, 307을 주로 사용하며 클라에 대한 정보를 갖고 있는 다른 서버로 리다이렉트할 수 있음.
    • 디렉터리 이름 정규화: 클라이언트가 / 를 빠뜨렸다면 대부분 웹서버는 정상적으로 동작할 수 있도록 /를 붙인 URL로 리다이렉트 함.
 

6: 응답 보내기

  • 웹 서버는 여러 클라이언트의 커넥션을 가질 수 있음. 그들 중 일부는 아무것도 하지 않고, 일부는 데이터를 요청하고 있고, 일부는 응답 데이터를 전송하고 있을 것임.
  • 그러므로 서버는 커넥션 상태를 추적해야 하며 지속적인 커넥션은 특별히 주의해야 함.
    • 비지속적인 커넥션이라면, 서버는 모든 메시지를 전송했을 때 자신 쪽의 커넥션을 닫을 것이다.
    • 지속적인 커넥션이라면, 서버가 Content-Length 헤더를 바르게 계산하기 위해 특별한 주의를 필요로 하는 경우나, 클라가 응답이 언제 끝나는지 알 수 없는 경우, 커넥션은 열린 상태를 유지함.
 

7: 로깅

  • 마지막으로 트랜잭션이 완료되었을 때 웹 서버는 트랜잭션이 어떻게 수행되었는지에 대한 로그를 로그파일에 기록함.