[SpringBoot] DispatcherServlet 완벽히 이해하기

Yu Seong Kim·2025년 1월 13일
0

SpringBoot

목록 보기
25/29
post-thumbnail

이번 포스팅은 DispatcherServlet에 대해 공부한 내용을 정리한 글입니다.
MVC패턴을 들어보고, 실제로 많이 사용하지만 왜 동작하는지, 어떠한 방식으로 동작하는지에 대한 내용을 생각하지 않고 사용하는 경우가 많습니다. 그래서 개념을 한번 더 숙지하면서 소스코드를 보며 어떤 방식으로 동작하는지 알아보겠습니다.

MVC패턴은 다양한 디자인패턴 중 하나입니다. 디자인패턴은 개발 중 많이 발생한 문제점을 정리하여 상황별로 간단히 적용 가능하도록 정리한 특정 '규약'을 바탕으로 사용할 수 있는 형태로 만든 것입니다.
그런 디자인 패턴은 옵저버 패턴, 스트래티지 패턴, 빌더 패턴 등 여러가지가 있지만 그 중 하나가 바로 MVC패턴입니다. MVC 패턴의 MVC는 Model, View, Controller의 약자 입니다. 한 프로젝트나 어플리케이션을 구성할 때 구성요소를 세 가지의 역할로 구분한 패턴입니다.

MVC의 구조


DispatcherServlet을 위 그림을 통해 간단히 설명하겠습니다.

  1. 모든 client의 요청은 DispatcherServlet으로 모입니다.

  2. DispatcherServlet은 client의 요청에 대한 핸들러 매핑, 핸들러 어댑터를 조회합니다. 스프링에서 RequestMapping, HandlerMapping은 스프링 빈(Bean) 중 @RequestMapping이 붙은 것을 매핑 정보로 인식합니다. 또한 RequestHandlerAdaper를 통하여 현재 찾은 핸들러가 client의 요청을 수행할 수 있는지 확인합니다.

  3. client 요청에 해당하는 컨트롤러를 찾으면, 컨트롤러에서 @Service를 이용하여 비즈니스 로직을 처리하여 View에 넘겨줄 정보를 모델(Model)에 담아서 돌려줍니다.

  4. 컨트롤러는 뷰(View)의 이름을 논리적인 스트링 형태로 리턴합니다. 따라서 스프링의 viewResolver를 통해 진짜 View 경로를 찾아갈 수 있게 매핑을 합니다. 예를 들어서, 컨트롤러에서 return "home"라고만 해도 viewResolver를 거치면 "/home.html"을 찾는 것처럼 View 경로를 찾도록 매핑합니다.

  5. 이제 뷰에 Model 객체가 함께 넘어갑니다. 그래서 Thymeleaf, JSP 같은 템플릿 엔진이 해당 모델을 받아서 동적인 웹페이지를 생성할 수 있게 하고, 이러한 템플릿 엔진이 아니어도 JSON 형태로 모델의 값을 매핑받아 페이지를 생성합니다.

DispatcherServlet 코드 살펴보기

인텔리제이에서 DispatcherServlet.class 들어갑니다.
1500줄이 넘는 소스 코드를 다 확인하지 않고, 부분적으로 핵심만 보도록 하겠습니다.

  1. servlet 호출되면 doService() 메서드가 호출됩니다.
  • 코드를 읽다보면 아래에서 doDispatch() 메서드가 호출되는 것을 확인할 수 있습니다.
  • 이 메서드는 메서드명을 보면 알 수 있듯이, 실제 dispatch를 실행하는 과정이 진행됩니다.
 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        Map<String, Object> attributesSnapshot = null;
        
        ...
        
        try {
           ** this.doDispatch(request, response);**
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

            if (this.parseRequestPath) {
                ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
            }

        }

    }

  1. doDispatch() 알아보기.

[1] 핸들러 조회, 핸들러 어댑터 조회

  • 맨 처음 doDispatch()에서는 클라이언트의 요청에 해당하는 핸들러를 조회하고, 해당 핸들러를 받기 위한 어댑터를 조회합니다.

[2] 위에서 찾은 핸들러 어댑터의 handle()메서드를 호출하여 ModelAndView를 반환합니다. 이어서 processDispatchResult() 메서드를 호출합니다.

[3] 뷰 렌더링 호출 - processDispatchResult() 메서드는 안에서 render() 메서드 호출합니다.

[4] 뷰 렌더링 - render() 메서드 안에서는 viewResolver를 통해 뷰를 찾고, 해당 view를 반환받습니다. 마지막으로 뷰를 실제로 렌더링을 진행합니다.

DispatcherServlet.class 코드가 굉장히 길어서 맨 위 그림에서 설명한 핵심 동작 원리가 어떤 코드들에 의해 실행되는지만 살펴봤습니다.

정리

이 코드를 따라가면

핸들러 조회 → 핸들러 어댑터 조회 → 핸들러 어댑터 실행 → 핸들러 실행 → ModelAndView 반환 → ViewResolver 호출 → View 반환 → 뷰 렌더링

의 순서대로 dispatcherServlet에 의해 동작하는 것을 확인할 수 있습니다.

마무리

평소에 사용하고 있는 class나 라이브러리 등에 대한 정확한 동작원리를 알면 개념들을 이해하는데 도움이 된다고 생각합니다. 또한 추후 에러가 발생하더라도 어떤 부분에서 에러가 발생했는지 쉽게 알 수 있을 것이라고 생각됩니다. 이 과정을 통해 아무 생각없이 쓴 다양한 어노테이션들이 어떠한 방식으로 동작하는지 생각하는 의미있는 시간이었습니다.

출처

profile
인생을 코딩하는 남자.

0개의 댓글