caffeine-library / pro-spring-5

🌱 전문가를 위한 스프링5를 읽는 스터디
5 stars 0 forks source link

[question] Springboot에서 servlet을 resolving 하는 원리가 궁금합니다 #36

Closed emiling closed 2 years ago

emiling commented 3 years ago

질문

Springboot에서 servlet을 resolving 하는 원리가 궁금합니다

상세 내용

추가적으로 Servlet 컨테이너가 하는 역할은 무엇인지와 Servlet의 작동 원리와 관련하여 한 번 생각해보면 좋을 듯 합니다

연관 챕터

참고

35

wooyounggggg commented 3 years ago

Servlet?

서블릿은 JavaEE에 종속된 기술로, JavaEE docs에서 그 정의를 살펴보면 다음과 같습니다.

A servlet is a Java programming language class that is used to extend the capabilities of servers that host applications accessed by means of a request-response programming model.

서버의 기능을 확장한다는 말이 조금 난해한데, 추측상 다양한 요청을 처리하는 것을 서블릿이 제공하는 인터페이스를 통해 선언적인 방식으로 구현할 수 있다는 말로 이해했습니다.

서블릿은 모든 타입의 요청에 응답할 수 있지만, 일반적으로 웹 서버에서 호스팅하는 애플리케이션을 확장하는 데 사용된다고 합니다. 그래서 서블릿을 좁은 의미로 설명할 때, 자바 기반의 동적 웹 페이지를 제공하기 위한 기술이라고 설명하기도 합니다.

javax.servlet과 javax.servlet.http

서블릿을 작성하기 위해서, javax.servlet과 javax.servlet.http에서 제공하는 클래스나 인터페이스를 상속해야 합니다. HTTP 기반이 아닌 일반 서비스 구현을 위해서는 GenericServlet 클래스를 확장하고, HTTP 기반의 서비스를 구현하기 위해서는 GenericServlet을 확장한 HttpServlet 클래스를 확장합니다. HttpServlet은 서블릿의 기능에 HTTP와 관련한 메서드, 이를테면 doGet, doPost의 메소드 등을 제공합니다.

/* GenericServlet.java */
/* 주로 서블릿의 생명주기 관리 메서드를 작성 */
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {

    ...
    public void destroy() {
    }
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
/* HttpServlet.java */
/* Http 관련 기능을 확장한 Servlet */
public abstract class HttpServlet extends GenericServlet {
    ...
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_get_not_supported");
        this.sendMethodNotAllowed(req, resp, msg);
    }
    ...
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_post_not_supported");
        this.sendMethodNotAllowed(req, resp, msg);
    }

    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_put_not_supported");
        this.sendMethodNotAllowed(req, resp, msg);
    }
    ...
}

HTTP 서블릿 동작 과정

서블릿을 통해, 웹 서버가 클라이언트 요청을 처리하는 과정을 요약하면 다음과 같습니다.

  1. 클라이언트로부터 HTTP Request 전송
  2. 서블릿 컨테이너(ex. Tomcat)는 요청을 전송받아, HttpServletRequest, HttpServletResponse 객체 생성
  3. web.xml에 정의된 url pattern을 확인하고, 요청을 처리할 서블릿 검색 or 생성
  4. 서블릿 컨테이너는 요청을 처리할 Thread 객체를 생성하고, 해당 서블릿 객체의 service() 메서드 호출
  5. service 메서드 호출 후, Http 요청 메서드에 따라 doGet(), doPost() 등의 메서드 호출
  6. 요청 처리가 완료되면, HttpServletRequest 및 HttpServletResponse 객체 소멸

요약하면, HTTP 요청마다 요청을 처리할 서블릿을 생성하고, 생성한 서블릿을 요청에 맵핑하여 HTTP Request를 처리하는 Actor로 사용합니다.

Servlet Container와 Spring Container를 연결하는 Dispatcher Servlet


앞서 말했듯, Servlet Container는 Http 요청과 Servlet을 맵핑하여 처리합니다. 그래서 JavaEE등의 레거시에서는 Servlet 인터페이스의 구현체를 만들어 사용했으나, 스프링 MVC에서는 Dispatcher Servlet이라는 서블릿을 통해 요청을 수신하고, 적당한 Controller Layer(ex. Controller- Service - DAO)를 검색하여 비즈니스 로직을 처리한 후 결과를 반환하는 형태로 요청을 처리합니다. 이러한 방식을, FrontController 패턴이라고 부릅니다.


스프링 MVC의 DispatcherServlet이 Servlet 구현의 책임을 가져가고, 개발자에게는 서블릿이 아닌 스프링 컨텍스트 작성의 책임을 주었기 때문에, 기존 JavaEE에 종속된 프로그램을 만들어야 했던 패턴을 벗어나 지금의 POJO 지향 프로그램 개발이 가능했다고 생각되네요.

DispatcherServlet은 Servlet Container와 Spring Container를 어떻게 연결할까?

그럼, DispatcherServlet이 Servlet Container와 Spring Container를 어떻게 연결하는지 궁금해집니다. DispatcherServlet은 Servlet Container에 포함된 개념이기 때문에, DispatcherServlet에서 Spring Container를 참조할 수 있기만 한다면, 두 개의 연결이 성립한다고 볼 수 있겠습니다.

Spring Container란, 사실상 ApplicationContext를 의미한다고 볼 수 있죠. 다시 말해, Spring Container를 참조할 수 있다는 것은 ApplicationContext를 알고 있다는 것이고, 이러한 맥락에서 이전에 보았던 ApplicationContextAware를 떠올릴 수 있습니다.

코드 수준에서 이를 살펴보도록 하겠습니다.

public class DispatcherServlet extends FrameworkServlet {
    ...
    public DispatcherServlet(WebApplicationContext webApplicationContext) {
        super(webApplicationContext);
        this.setDispatchOptionsRequest(true);
    }
    ...
}

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    ...
}

DispatcherServlet의 계층구조를 보면, DispatcherServlet ApplicationContextAware를 구현하고 있고, 생성자 주입을 통해 WebApplicationContext를 주입받는 것을 알 수 있습니다.

AWARE

이러한 방식으로, Servlet Container와 Spring Container는 연결되어 작동합니다.

JasonYoo1995 commented 2 years ago

90 #93 참고