[Spring] Dispatcher Servlet

Hood·2025년 6월 20일
0

Spring Boot

목록 보기
11/14
post-thumbnail

✍ Back-End 지식을 늘리자!

백엔드 개발자를 준비하며 생기는 의문들을 정리한 포스트입니다.


들어가기 전

Spring 을 사용하면서 기본적인 Annotation을 사용하여
기본적인 API를 만들 수 있었습니다. 그것은 스프링 프레임워크의 편리함, 편의성이겠지요.
하지만 간단히 어노테이션을 가져옴으로써 조금 더 수월하게 코드를 생성하지만
구체적으로 어떤 방식으로 코드에 주입되는지에 대한 동작과정 이해가 부족한 것 같아
이에 최초로 요청을 받고 처리하는 Spring MVC의 Dispatcher Servlet을 정리해보려고 합니다.


서블릿(Servlet)이란?

Servlet 이란
자바 어플리케이션에서 클라이언트의 요청을 처리하고 응답을 반환하는 역할을 하는 객체이다.
스프링 부트에서는 HttpServlet 클래스를 상속받아 사용하며
Servlet Container 에 의해 실행됩니다.

또한 서블릿의 생명 주기를 위한 세가지 필수적인 메소드가 있는데
아래 세 가지 메소드들은 모든 서블릿(SDK에서 정의되거나 자체적으로 정의)에 의해 특정
시간에 서버에 의해 호출됩니다.

  • init() : 초기화 단계
    이를 통해 서블릿이 웹 애플리케이션에서 초기화 매개변수에 접근할 수 있도록 한다.
  • service() : 초기화 이후 각 요청 들에 의해 호출
    요청의 종류를 판단하고 요청을 처리할 적절한 메소드로 전달한다.
  • destroy() : 서블릿 객체가 파괴될 때 호출
    해당 서블릿이 가지고 있던 자원을 release(해체) 한다.

커스텀 서블릿 구현

위 사진에서 doGet, doPut, doDelete 등의 메소드를 사용하여 요청을 처리하고
스프링 부트에서는 어노테이션과 HttpServlet 상속을 이용하여 간단하게 생성할 수 있습니다.

//커스텀 서블릿 정의
class MyServlet : HttpServlet() {

    override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
        resp.writer.write("GET 요청 처리됨")
    }

    override fun doPost(req: HttpServletRequest, resp: HttpServletResponse) {
        resp.writer.write("POST 요청 처리됨")
    }

    override fun doPut(req: HttpServletRequest, resp: HttpServletResponse) {
        resp.writer.write("PUT 요청 처리됨")
    }

    override fun doDelete(req: HttpServletRequest, resp: HttpServletResponse) {
        resp.writer.write("DELETE 요청 처리됨")
    }
}
//Spring Boot에서 서블릿 등록
@Configuration
class ServletConfig {

    @Bean
    fun myServletRegistration(): ServletRegistrationBean<MyServlet> {
        return ServletRegistrationBean(MyServlet(), "/myServlet/*")
    }
}
//Test Controller
@RestController
@RequestMapping("/api")
class MyController {

    @GetMapping
    fun doGet() = "GET 요청 처리됨"

    @PostMapping
    fun doPost() = "POST 요청 처리됨"

    @PutMapping
    fun doPut() = "PUT 요청 처리됨"

    @DeleteMapping
    fun doDlete() = "DELETE 요청 처리됨"
}

Servlet 동작 방식

사진 출처

1. 사용자(Client)가 URL을 클릭하면 HTTP Request를 Servlet Container로 전송한다.
2. HTTP Request를 전송받은 Servlet Container는 HttpServletRequest, HttpServletRequest
두 객체를 생성한다.
3. web.xml은 사용자가 요청한 URL을 분석하여 어느 서블릿에 대해 요청을 할 것인지를 찾습니다.
4. 해당 서블릿에서 service 메소드를 호출한 후 클라이언트의 POST, GET 여부에 따른
doGet() 또는 doPost() 를 호출한다.
5. doGet() or doPost() 메소드는 동적 페이지를 생성한 후 HttpServletResponse 객체에 응답을 보냅니다.
6. 응답이 끝나면 HttpServletRequest, HttpServletResponse 두 객체를 소멸시킨다.

서블릿 컨테이너란?

서버에서 만들어진 서블릿이 소스로 동작하는 것이 아닌
서블릿을 관리해주는 것이 필요한데 이러한 역할을 하는 것이 바로 서블릿 컨테이너 입니다.

즉, 서블릿을 요구사항 명세서라고 표현하면
서블릿 컨테이너는 그 명세서를 보고 개발하는 개발자 입니다.

서블릿 컨테이너는 Clinet의 Request를 받아주고 Response를 할 수 있게
웹 서버와 소켓을 만들어 통신합니다.

대표적인 서비스는 Tomcat(톰켓) 이 있습니다.

톰캣은 웹 서버와 소켓을 만들어 JSP(java server page)와 Servlet이 동작할 수 있는 환경을
제공합니다.

웹 서버와 서블릿 컨테이너의 요청 처리 방법

사진 출처

다음은 톰캣에서 동작하는 서블릿의 Life Cycle입니다.

  1. 웹서버가 HTTP 요청을 받는다.
  2. 웹서버는 요청을 서블릿 컨테이너로 전달한다.
  3. 서블릿이 컨테이너에 없다면, 서블릿을 동적으로 검색하여 컨테이너의 주소 공간에 로드한다.
  4. 컨테이너가 서블릿의 init() 메소드를 호출하면, 서블릿이 초기화된다.
    (서블릿이 처음 로드됬을 때 한번만 호출)
  5. 컨테이너가 서블릿의 service() 메소드를 호출하여 HTTP 요청을 처리한다.
    (요청의 데이터를 읽고, 응답을 만들어낸다)
  6. 서블릿은 컨테이너 주소에 남아있고, 다른 HTTP 요청들을 처리할 수 있습니다.
    웹서버는 동적으로 생성된 결과를 올바른 위치에 반환한다.

디스패쳐 서블릿이란?

서블릿과 함께 스프링을 사용하다보면 항상 듣게되는 게 디스패쳐 서블릿입니다.
서블릿 컨테이너 앞에서 최초로 요청을 받고 처리하기에 Front Controller라고 부르기도 합니다.

디스패쳐 서블릿을 이해하기 위해서는 스프링 MVC 패턴에 대한 이해가 먼저 필요한데
간단하게 정리하자면 아래 3가지 엔티티를 사용하여
만들어내는 스프링의 구조화된 패턴 방식입니다.

Model : 어플리케이션의 비즈니스 로직 및 데이터
View : Model의 데이터를 사용하여 클라이언트에게 보여줄 사용자 UI
Controller : HTTP 요청을 받아들이고 모델을 생성 및 가공하여 적절한 뷰를 반환

디스패처 서블릿의 동작과정

  1. 빵을 만드는 요리사가 주문 받기, 토핑 올리기, 빵 만들기, 서빙하기 등의 일을 모두 처리하고
    있었는데 요리사가 힘들어져서 사람을 고용해 일을 위임을 하게 됩니다.
  2. 서빙알바를 고용해 서빙하기이라는 테스크 를 분담하게 되었는데
    이제 토핑이 많아져서 토핑 올리기 테스크를 담당할 알바생도 고용하게 됩니다.
  3. 그럼 요리사가 담당할 거는 주문 받기빵 만들기가 됩니다.
    하지만 사람들이 많아져서 주문 받기가 힘들어졌습니다.
  4. 그래서 주문 받기 테스크를 해줄 캐셔를 고용해 일을 모두 분담하게 되죠
    정리해보면 주문 받기를 하는 캐셔가 요리사에게 정보를 전달하고 요리사는 빵 만들기
    진행합니다. 이후 빵이 만들어지면 토핑 올리기 테스크가 진행되고 만들어진 결과를
    서빙하기 의 알바생에게 주어 결과가 내보내지게 됩니다.

그럼 여기서 주문 받기를 하는 캐셔는 Front Controller 즉 디스패처 서블릿이 됩니다.
그리고 빵 만드는 요리사는 Controller가 되며 빵 만드는 알바생은 Model
서빙을 해서 결과물을 보여주는 알바생은 View가 됩니다.

Spring MVC로 돌아오면


사진 출처

Spring MVC는 프론트 컨트롤러가 외부에서 요청을 받아서
컨트롤러에게 위임을하고 컨트롤러는 모델을 생성하고 뷰에 전달하고 있습니다.
여기서 Front Controller가 Dispatcher Servlet이 됩니다.
여기서 디스패처 서블릿은 웹 요청에 의해서 응답을 처리하기 위해 무언가를 보내는 역할을 합니다.

DispatcherServlet의 역할

1. 클라이언트의 요청을 받아 공통적인 작업을 수행

HTTP 요청을 받으면 Front Controller에는 컨트롤러에 요청을 보냅니다.
그럼 컨트롤러는 모델을 생성에 뷰에게 전달하게 됩니다.

이 과정에서 요청을 받았을 때 HttpServletRequest라는 객체로 변환을 합니다.
그런데 어떤 컨트롤러에 전달할지 몰라 여기서 HandlerMapping을 통해
컨트롤러를 찾게됩니다.

2. 컨트롤러로 세부 작업을 위임

컨트롤러에 반환 객체가 MemberSaveRequest인데 그러면 객체가 서로 다르게 됩니다.
이 때 변환해 주는 친구가 HanderAdapter가 됩니다.

그럼 컨트롤러는 제약없이 메서드를 생성하기만 하면 외부에서 자동으로 변환해줘서
전달을 해주기 때문에 확장성이 아주 좋다는 장점이 있습니다.

3. 뷰에게 모델을 전달하여 최종 결과물을 생성

컨트롤러에게 받은 ViewName을 ViewResolver를 통해 View를 생성해줍니다.
그래서 이 뷰에게 모델을 전달을 받았을 때 뷰에서 자동으로 모델에 전달받은 값을 넣어줌으로써
최종 결과물을 생성합니다.
그래서 뷰는 변화가 되더라도 값만 변경하면 되서 유연성이 아주 뛰어납니다.

4. 여기서 정적 자원과 동적 자원을 분할 처리한다

디스패처 서블릿을 통해 얻는 장점은 정적 자원과 동적 자원을 따로 처리하는 점입니다.

요청을 처리할 컨트롤러를 먼저 찾고, 컨트롤러가 없을땐 2차로 설정된 정적 자원을 탐색합니다.
-> 이를 통해 효율적인 리소스 관리가 가능해집니다.


📌 결론

스프링의 유연함과 확장성을 시작으로 기존의 불편함을 해소하기 위한
Spring MVC의 확장성 덕분이었습니다.
그리고 그 중간에는 DispatcherServlet은 프레임워크 속 굉장히 중요한 엔진 역할을 한다는
것을 알 수 있었습니다.

참고

스프링 서블릿 servlet에 대해 알아보자
[10분 테코톡] 안나의 스프링 MVC와 DispatcherServlet

profile
달을 향해 쏴라, 빗나가도 별이 될 테니 👊

0개의 댓글