들어가며

  • 저번 포스트에서는 웹 시스템의 전체적인 구조에 대해 간략하게 알아보았다.
  • 이번 포스트에서는 WAS를 직접 구현하려면 어떤 면이 필요하고 서블릿이라는 것이 어떻게 이를 도와줄 수 있는지를 알아본다.

 

 

 

WAS 직접 구현의 어려움과 서블릿

  • WAS에서는 앱 로직이 돌아가야 한다. 그리고 이것은 대부분은 사용자의 요청에 의해 로직이 수행되고 처리된다.
  • 그리고 사용자의 요청은 HTTP 메시지를 통하여 들어오게 된다. 그렇다면 우리가 로직을 수행하기 위해서는 해당  HTTP 메시지에서 데이터를 가져와야 한다는 것이다.
  • 따라서 WAS를 직접 구현하기 위해서는 다음과 같은 기능들이 필요해진다.
    • TCP/IP를 통한 클라이언트 - 서버의 연결 설정
    • 클라이언트에게서 들어온 요청 메시지 파싱
    • 요청된 HTTP 메서드와 ContentType 확인
    • HTTP 메시지 바디 파싱
      • 이때 쿼리 파라미터나 바디 내부에 있는 파라미터 데이터를 사용할 수 있도록 파싱한다.
    • 해당 데이터들을 저장
    • 비즈니스 로직 실행
      • 데이터베이스에 데이터 저장
    • HTTP 응답 메시지 생성
    • TCP/IP에 응답 HTTP 메시지 전달 뒤 연결 종료
  • 세부적인 단계들을 제외하고 큰 단락에서만 보았다. 그럼에도 불구하고 직접 구현하는데에 수많은 기능들이 들어가야 한다. 심지어 이 기능들의 거의 전부(비즈니스 로직 부분을 제외한 부분)가 오직 HTTP 메시지를 수신하거나 응답을 전송해야 할 때만 사용된다.
  • 이는 개발자가 맡은 비즈니스 로직을 구현하는데 어려움이 따르게 된다. 보면 알겠지만 배보다 배꼽이 더 큰 상황이 만들어 졌기 때문이다. 

서블릿(Servlet)

  • 매우 다행스럽게도 WAS는 서블릿이라는 것을 지원한다. 이 서블릿은 TCP/IP를 통한 연결 수립부터 시작하여 HTTP 메시지 파싱, 응답 메시지 생성과 전송까지 HTTP 메시지 통신 부분들을 모두 도맡아 처리하는 역할을 수행한다.
  • 따라서 개발자는 해당 기능들에 대해 신경쓸 필요가 없다. 해당 기능들은 모두 서블릿이 알아서 처리하도록 위임하고, 핵심 비즈니스 로직에 대한 개발에만 충실하면 된다. 자바에서 지원하는 서블릿 코드는 다음과 같다.
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet{

    @override
    protected void service(HttpServletRequest request, HttpServletResponse response){
        // main logic
    }
}
  • 이 웹 서블릿은 다음과 같이 동작한다.
    • @WebServlet에 있는 urlPatterns에 있는 url 경로로 요청이 들어올 시 해당 서블릿이 동작하게 된다.
    • 서블릿은 HttpServletRequest를 통해 사용자가 요청으로 보내온 HTTP 메시지 데이터를 편리하게 가저올 수 있도록 하며 동시에 HttpServletResponse를 통해 응답 메시지도 보낼 수 있도록 한다.
  • 이를 통해 개발자는 request, response에 있는 기능들을 통해 HTTP 메시지 스펙을 매우 편리하게 사용할 수 있다.

 

 

 

서블릿의 동작 원리

  • 위 그림을 보면서 알아보자.
    • 먼저 클라이언트가 localhost:8080/hello URL을 사용하여 서버에 요청을 보낸다.
    • 서버는 해당 요청을 받은 뒤에 해당 요청을 기반으로 Request 객체를 생성한다.(그림의 request) Response 객체도 같이 생성하는데 이 객체는 이후에 서블릿이 종료된 뒤에 이를 기반으로 응답 메시지를 생성하게 된다.
    • 이후 WAS는 서블릿 컨테이너(Servlet Contatiner)라는 곳에 해당 URL과 동일한 패턴을 가지고 있는 서블릿이 있는지를 조회한다. 존재한다면 생성한 request, response를 해당 서블릿에 넣어주고 해당 서블릿을 실행시킨다.
    • 서블릿 내부에서는 실제 비즈니스 로직이 존재한다. 이 로직을 모두 수행한 뒤, response 객체를 개발자가 세팅한다(상태 코드나 응답 메시지 바디 데이터 등)
    • 이후 이 Response 객체를 기반으로 하여 HTTP 응답 메시지를 서블릿에서 생성하여 클라이언트에게 반환한다.
  • 이때 이 서블릿 컨테이너는 서블릿 객체들을 생성하고 호출하고 관리하는 기능을 가진다. 비슷한 것이 떠오르지 않는가? 바로 스프링 컨테이너와 비슷한 기능을 가진다.
  • 심지어 객체들을 싱글톤으로 관리하는 것까지도 비슷하다. 이는 스프링 컨테이너가 왜 객체를 싱글톤으로 관리하는지에 대한 이유와 동일한 이유이다.
    • 각 요청에 대해 객체를 만들어 반환하는것은 많은 코스트가 소모되기 때문에 시작 시점에 객체를 하나 만들고 해당 객체를 반환하는 방식으로 사용하는 것이다.
    • 대신 싱글톤으로 관리하는 단점도 같이 가져간다. 즉 공유 변수에 대한 사용을 매우 조심스럽게 해야 한다는 것이다.
  • 이후에 한번 볼 JSP 역시 앱이 실행되면 내부적으로 서블릿으로 변환되어 실행된다.
복사했습니다!