Spring

코드로 살펴보는 DispatcherServlet과 Servlet 인터페이스 (2)

천방지축 개발노트 2024. 10. 6. 23:44

이전 글에서 DispatcherServlet이 Aware 인터페이스를 활용해 어떤 역할을 하는지 알아봤다.

이번에는 Servlet 인터페이스로부터 파헤쳐 보자.

 

Servlet 인터페이스로부터 시작하는 DispatcherServlet

먼저 서블릿(Servlet)이라는 개념을 찾아보면 '자바 웹 애플리케이션에서 HTTP 요청을 처리하기 위한 서버 측 컴포넌트'라고 정의되어 있다. 그러니까 Java에서 웹 요청을 처리하는 기술이자 구성 요소(오브젝트)라는 말인데.. 주로 Spring으로 웹 개발을 했었던 나에겐 당연한듯 익숙했던 Servlet이라는 대명사의 의미에 왜 Java라는 특정 언어에 종속된 단어가 포함되어 있는 거지? 싶어서 더 찾아봤다.

결론적으로 Servlet이라는 개념은 Java에만 해당하는 개념으로, 다른 프로그래밍 언어나 각 언어의 웹 프레임워크에서는 비슷한 역할을 수행하는 별도의 구조 or 패턴이 구현돼있다고 한다.

예) ASP.NET MVC에서는 라우팅(routing) 시스템이 Client Request을 받고 해당 로직을 수행할 컨트롤러를 결정하는 방식으로 웹 요청을 처리한다고 함.

 

어쨌든 이 Servlet은 Client 요청/응답을 처리하는 역할을 수행하도록 약속된 Interface로써, Java의 모든 종류의 Servlet 오브젝트들은 이 Interface를 구현하는 것이 기본 규약이다. 그리고 Spring에서는 이 Servlet 기술을 구현한 DispatcherServlet(org.springframework.web.servlet)이라는 오브젝트를 제공하고 있다. 즉, DispatcherServlet은 Servlet의 일종으로 Spring MVC에서 HTTP 요청을 처리하는 중요한 객체이다.

Client 요청을 처리하는 Servlet의 생명주기는 크게 생성, 요청 처리, 소멸이라고 할 수 있는데, 이에 해당하는 각각의 기본 메서드가 존재한다. 주요 메서드들의 상세 내용을 정리해 봤다.

Method 기능
init() Servlet Container에 의해 Servlet 인스턴스가 생성될 때 최초에 한번 init()이 호출되며, 서블릿 동작에 필요한 설정 등의 리소스 초기화 작업 수행한다.
service() HTTP 요청을 처리하는 메서드로. 매 요청이 들어올 때마다 service() 메서드가 호출된다.
destroy() Servlet Container에 의해 Servlet이 종료될 때 호출되며, Servlet이 사용한 리소스를 해제하는 등의 정리 작업을 수행한다.
getServletConfig() Servlet 설정 정보를 반환하는 기능을 수행한다.
getServletInfo() 버전, 저작권 등의 Servlet 정보를 반환. 기본적으로 빈 문자열을 반환하도록 되어있으며, 의미 있는 값을 반환하려면 해당 메서드를 오버라이드하여 사용하면 된다.

 

 

HttpServlet의 구조와 역할

HttpServlet은 Servlet을 구현한 자바 EE 표준 추상 클래스로, 이름에서도 알 수 있듯이 HTTP 프로토콜 기반의 웹 요청을 처리하기 위한 서블릿 오브젝트이다. 정확히는 Servlet 인터페이스를 바로 구현하는 것은 아니고 Servlet을 구현한 추상 클래스인 GenericServlet을 상속받고 있는 구조이다.

GenericServlet의 내부 소스를 간략히 말하자면 환경 설정(ServletConfig) 등의 아주 기본적인 공통 기능을 담고 있는데, 프로토콜에 독립적인(HTTP 프로토콜에 종속되지 않는) Servlet을 개발할 때 사용할 수 있도록 설계된 것으로 확인된다.

그리고 핵심인 HttpServlet은 service()를 오버라이드하여 HTTP 요청을 처리하기 위한 doGet(), doPost(), doPut(), doDelete() 와 같은 메서드를 추가적으로 제공하고 있다. service()는 웹 요청이 들어올 때마다 호출되기 때문에, 내부적으로 각 HTTP Method 별로 유연하게 처리할 수 있도록 구현하고 있는 것이다. 만약 웹 애플리케이션 개발을 위해 어떤 Servlet을 개발해야 한다면, 이 HttpServlet을 상속하여 구현하면 되겠다.

 

클라이언트 요청을 처리하는 핵심 메서드인 service()는 아래 두 파라미터를 받고 있다.

종류 용도
HttpServletRequest 1) 클라이언트가 서버로 보낸 요청 정보를 담고 있는 객체.
2) URL, 쿠키, 세션 등의 클라이언트가 보낸 다양한 정보를 얻을 수 있다.
HttpServletResponse 1) 서버가 클라이언트에게 전송할 응답 정보를 다루는 객체.
2) 해당 객체를 통해 응답 코드, HTML, JSON, 파일 등을 클라이언트에게 전송할 수 있다.

 

 

DispatcherServlet은 어디에서 관리되는가?

전통적으로 boot가 아닌 Spring에서는 web.xml이라는 설정 파일에 애플리케이션이 시작될 때 어떤 서블릿이 요청을 처리할지를 정의한다.

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/dispatcher-servlet.xml</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

 

하지만 오해하면 안 되는 것은 DispatcherServlet이 Spring MVC에서 가장 중요한 오브젝트인 것은 맞지만 Spring이 아닌 Servlet Container에서 관리되며 구동된다는 점이다. 즉, spring에서 web.xml에 위와 같은 코드를 작성하는 것은 DispatcherServlet을 Bean으로 등록하는 행위가 아니라 서블릿 컨테이너에서 요청을 처리할 Servlet을 지정해 주기 위함이다. 다시 말해, web.xml은 WAS가 참고하는 설정 파일인 것이다. Spring은 애플리케이션 내의 객체들의 생명주기를 관리하며, HTTP 요청 처리와는 직접적인 관련은 없다.

정리하자면 DispatcherServlet은 웹 애플리케이션이 시작될 때 Servlet Container(Tomcat 등)에 의해 생성되고, 웹 요청이 들어오면 Spring과 상호작용하여 이를 처리하는 역할을 담당한다.