Web Server, WAS, Servlet

채상엽·2022년 12월 10일
0

Spring

목록 보기
21/21
post-thumbnail

Servlet은 비교적으로 low level의 기술이라고 볼 수 있다. 때문에 최근에는 이 Servlet만을 단독으로 이용해 구현하는 경우는 드물다. 그러나 자바의 대부분의 프레임워크들(Spring REST, Spring WebMVC 등..)은 Servlet 기반 위에서 동작한다.

Web Server란 무엇일까?

Web Server는 정적인 컨텐츠를 다루기 위해 사용된다. 이미지나 HTML과 같은 정적인 자원들을 다루는데만 사용된다.

Apache Web Server가 대표적인 Web Server의 예이다.

사용자는 단순하게 어떤 자원을 요청하고, 서버는 정해져있는 컨텐츠를 응답으로 반환한다.

최근 많이 쓰이는 Web Server는 Nginx가 있다.

WAS(Web Application Server)란 무엇일까?

Web Server가 정적인 컨텐츠를 다루기 위한 서버라고 하면, WAS는 DB조회나 로직 처리를 요구하는 동적 컨텐츠를 제공하기 위해 만들어진 서버이다. WAS 자체를 Servlet Container라고 부르기도 한다.

가장 많이 쓰이는 WAS는 Tomcat이 있다.

Servlet Container(Web Container)란 무엇일까?

서블릿 컨테이너는 말 그대로 서블릿을 담고 관리해주는 컨테이너이다. HTTP 요청을 기준으로 웹 서비스 아키텍처의 요청 처리 과정을 정리해보면 다음과 같다.

  1. 웹 브라우저에서 Web Server에 Http 요청을 보낸다.
  2. Web Server가 받은 Http 요청을 WAS의 Web Server에 전달한다.
  3. WAS의 Web Server는 Http 요청을 Servlet Container에 전달한다.
  4. 서블릿 컨테이너는 해당 요청 url에 해당하는 서블릿 인스턴스가 힙 메모리 영역에 있는지 확인한다.
    • 존재하지 않는다면, 서블릿 인스턴스의 init() 메서드를 이용해 서블릿 인스턴스를 초기화한다.
    • 이미 존재한다면, 초기화를 진행하지 않고 바로 service()를 호출하는 스레드를 요청 수 만큼 생성한다.
  5. 서블릿 컨테이너는 서블릿 인스턴스의 service() 메서드를 호출하여 Http 요청을 처리하고, WAS의 Web Server에게 처리 결과를 전달한다.
  6. WAS의 Web Server가 Http 응답을 Web Server에 전달한다.
  7. Web Server가 웹 브라우저에 Http 응답을 전달한다.

우리가 대표적으로 알고 있는 WAS는 Tomcat이 있다. Tomcat에는 하나의 JVM이 할당되고, 이는 하나의 JVM에는 하나의 WAS가 할당됨을 의미한다. 또한 하나의 JVM에는 하나의 서블릿 컨테이너가 있음을 의미하기도 한다.

서블릿 컨테이너는 다음과 같은 역할을 수행한다.

  1. Web Server와의 통신을 지원한다.
  • 소켓을 만들고 listen(), accept() 하는 과정을 생략할 수 있도록 도와준다
  1. 서블릿 생명주기(Life cycle)관리
  • 로딩하여 인스턴스화하고 요청에 따른 적절한 서블릿 메서드를 호출하며, 생명을 다 한 순간에는 GC를 진행하여 편의를 제공한다.
  1. 멀티스레드 지원 및 관리
  • 서블릿 컨테이너는 요청이 들어올때 마다 새로운 스레드를 생성하는데, HTTP 서비스 메서드를 실행하고 난 뒤엔 해당 스레드는 죽게 된다. 원래라면 이러한 스레드들을 개발자가 관리해야 하지만, 서버가 다중 스레드를 생성하고 운영하고 소멸까지 담당해주니 스레드 안정성에 대해서 걱정할 부분이 줄어들게 된다.
    즉, 서블릿 컨테이너는 다수의 요청을 처리하기 위해 하나의 서블릿 인스턴스를 초기화하고, 다수의 스레드를 생성해 요청에 대응한다.
  1. 선언적인 보안 관리
  • 보안과 관련된 내용을 자바 클래스가 아닌, web.xml(XML 배포 서술자)에 기록하므로, 보안에 대해 수정할 사항이 생겨도 소스 코드를 수정할 일이 줄어들게 된다.

1개의 클라이언트당 1개의 스레드가 아닌, 1개의 요청당 스레드를 생성하는 이유가 무엇일까?

확장성에 유리하다. 자바의 스레드는 active 상태이건 idle 상태이건 일정 용량을 차지하도록 구현하게 되어있어 그 비용이 비싸다. 만약 클라이언트 연결 하나당 스레드를 붙인다면, 클라이언트가 요청을 보내고 있지 않을 때에도 클라이언트가 연결을 끊기 전까지 idle 상태로 스레드를 차지하고 있게 된다.
연결이 유지된 클라이언트가 많아질수록 더 이상 새로운 스레드(커넥션)을 만들 수 없게 된다. 이때 클라이언트당 스레드가 아닌 요청 당 스레드를 생성하도록 하면, 처리중인 요청에 대해서만 스레드를 유지하면 되기 때문에 확장성과 비용면에서 경제적이게 된다.

그럼에도 불구하고 HTTP 커넥션이 유지되어서 클라이언트가 지속적으로 요청을 날려야하는 상황이 생긴다면 HTTP Keep-alives 옵션을 사용해 한번 연결한 TCP를 재사용함으로써 커넥션 별로 스레드를 지속할 수 있도록 할 수 있다. 아래와 같은 상황이 그 예다.

1. 이미지를 4개를 보여주어야 한다.
2. 클라이언트는 한번에 2개의 이미지만 받아올 수 있다.
3. 이미지를 받아오는데 2초가 걸린다.
4. 포트를 여는데 1초가 걸린다.

Keep Alive : False
처음 포트를 열고(1초) + 이미지를 받고 클라이언트 소켓을 닫는다(2초)
다시 포트를 열고(1초) + 이미지를 받고 클라이언트 소켓을 닫는다(2초)
total = 6초

Keep Alive : True
처음 포트를 열고(1초) + 이미지를 받아온다(2초)
열어둔 포트로 다시 이미지를 받아온다(2초)

Servlet 이란 무엇일까?

서블릿이란 클라이언트의 요청을 처리하고 그 결과를 다시 클라이언트에게 전송하는 Servlet 클래스의 구현 규칙을 지킨 자바 프로그램을 서블릿이라고 말한다.

Servlet은 init(), service(), destory() 이 세가지의 method를 반드시 재정의해야만 한다.

  • init()
    Servlet 생성시 호출된다. Servlet을 초기화하고 Servlet이 이용하는 자원을 할당하는 동작을 수행한다.
  • service()
    이 메서드가 호출되기 위해서는 반드시 init() 메서드의 호출이 선행되어야 한다. 이후 Servlet 으로 요청이 전달 될때마다 호출된다. HTTP Method에 따른 Service 로직이 수행되는 부분이다.
  • destroy()
    Servlet이 삭제될 때 호출된다. Servlet에서 자원을 해지하는 동작을 수행한다.

스프링에서 Servlet 구조


Servlet 인터페이스에는 위에 설명했던것처럼 Servlet의 생명주기와 요청 처리와 관련된 메서드들이 선언되어 있다.

ServletConfig 인터페이스는 Servlet에서 호출된 init()메서드로 초기화된 서블릿에 대한 정보를 저장한다.

GenericServlet 추상클래스는 Servlet과 ServletConfig를 구현하며, init()destroy()를 알아서 처리해주어 개발자의 사용을 편리하게 도와준다. 때문에 GenericServlet을 사용한다면, service() 메서드만 재정의하여 사용하면 된다.

HttpServlet 추상클래스는 GenericServlet을 확장하며, init(), destroy()뿐 아니라 service()까지 구현이 되어 있다. 때문에 HTTP 요청이 들어오면 해당 요청의 Http Method와 일치하는 핸들링 메서드를 자동으로 호출해준다. 이러한 간편함을 제공하는 덕분에 현재 주로 사용하는 서블릿은 대부분 HttpServlet이다.

그런데 잘 보면 Http Method들 중에서 PATCH가 없는것을 확인할 수 있다. 뒤늦게 HTTP 스펙에 추가된 PATCHHttpServlet의 자식 클래스인 FrameworkServletservice()메서드에 재정의가 되어있는 것을 확인할 수 있다.

profile
프로게이머 연습생 출신 주니어 서버 개발자 채상엽입니다.

0개의 댓글