🔗 참고자료

 

✍ 공부하게 된 계기

면접을 준비하면서 자주 나왔던 내용입니다.

질문에 대한 답을 그냥 이론만 거의 외워서 대답은 했었습니다.

정확하게 어떻게 DispatcherSevlet이 구현되어 있는지 알지 못했습니다.

단순히 이론을 외우면 계속 까먹고 제대로 이해하기가 힘들다고 생각했습니다.

그래서 이렇게 직접 구현을 해보면서 좀 더 깊게 공부하기로 했습니다.

 

 

 

깃허브 링크

  • 구현중인 깃허브 레포지토리 => 링크
  • 우아한테크코스에서 구현한 레포지토리 => 링크

전부 혼자서 구현하기에는 힘들기 때문에 다양한 자료들을 바탕으로 만들예정입니다.

 

 

1. DispatcherSevlet이란?

  • Dispatch는 "보내다" 라는 뜻을 가지고 있다.
  • HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러이다.
    * 프론트 컨트롤러(Front Contoller)
  • 클라이언틀로부터 어떠한 요청이 오면 톰캣(Tomcat)과 같은 서블릿 컨테이너가 요청을 받게 된다.
    그리고 이 모든 요청을 프론트 컨트롤러인 디스패처 서블릿이 가장 먼저 받게 된다.
    그 후 공통적인 작업을 먼저 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아서 작업을 위임한다.
  • 프론트 컨트롤러는 MVC 구조에서 함께 사용되는 디자인 패턴이다.

 

 

 

2. DispatcherServlet의 동작 과정

  1. DispatcherServlet으로 클라이언트의 웹 요청(HttpServletRequest)가 들어온다.
  2. 웹 요청을 LocaleResolver, ThemeResolver, MultipartResolver 인터페이스 구현체에서 분석한다.
  3. 웹 요청을 HandlerMapping에 위임하여 해당 요청을 처리할 Handler(Controller)를 탐색한다.
  4. 찾은 Handler를 실행할 수 있는 HandlerAdapter를 탐색한다.
  5. ④, ⑤ 찾은 HandlerAdapter를 사용해서 Handler의 메소드를 싱행한다. 이때, Handler의 반환값은 ModelView이다.
  6. ⑥ View 이름을 ViewResolver에게 전달하고, ViewResolder는 해당하는 View 객체를 반환한다.
  7. ⑦ DispatcherServlet은 View에게 Model을 전달하고 화면 표시를 요청한다. 이때, Model이 null이면 View를 그대로 사용한다. 반면, 값이 있으면 View에 Model 데이터를 렌더링한다.
  8. ⑧ 최종적으로 DispatcherServlet은 View 결과(HttpServletResponse)를 클라이언트에게 반환한다.

=> @Controller를 기준으로 작성한 것이고 @RestContoller의 경우 ⑥, ⑦ 과정이 생략된다. 즉 ViewResolver를 타지 않고 반환값에 알맞는 MessageConverter를 찾아 응답 본문을 작성한다.

 

 

 

3. 구현해야할 것들

  • DispatcherServlet 클래스
  • Controller 인터페이스
  • HandlerMapping 클래스
  • ViewResolver 클래스

 

 

4. Dependencies(gradle)

dependencies {
	// jakarta servlet api 라이브러리
    implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0'

	// 아파치 톰캣
    implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.1.0-M16'
    implementation 'org.apache.tomcat.embed:tomcat-embed-jasper:10.1.0-M16'
}

 

 

 

 

5. 소스코드

 

 

DispatcherServlet 클래스

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class DispatcherServlet  extends HttpServlet {
    private HandlerMapping handlerMapping;
    private ViewResolver viewResolver;

    @Override
    public void init() throws ServletException {
        handlerMapping = new HandlerMapping();
        viewResolver = new ViewResolver();
        viewResolver.setPrefix("./");
        viewResolver.setSuffix(".jsp");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        service(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        service(req, resp);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        String path = uri.substring(uri.lastIndexOf("/"));

        Controller controller = handlerMapping.getController(path);

        String viewName = controller.handleRequest(req, resp);

        String view = null;
        if (!viewName.contains(".do")) {
            view = viewResolver.getView(viewName);
        } else {
            view = viewName;
        }
    }
}
  • 코드의 동작 과정
    1. 사용자의 요청을 최전선에서 DispatherServlet이 받는다.
    2. URI를 Parsing해서 Path를 HandlerMapping에게 넘긴다.
    3. HandlerMapping은 사용자 요청을 처리할 수 있는 올바른 Controller를 리턴한다.
    4. Controller는 내부의 HandlerRequest를 통해서 요청을 처리한다.
    5. 요청을 처리한 뒤 이동할 화면을 ViewResolver를 통해 JSP(혹은 View)파일의 이름과 경로를 리턴받는다.
    6. 사용자에게 올바른 뷰를 보여준다.

 

Controller 인터페이스

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public interface Controller {
    String execute(HttpServletRequest req, HttpServletResponse res) throws Exception;
}
  • 클라이언트의 요청을 받은 DispatcherServlet은 HanlderMapping을 통해 Controller 객체를 검색하여 실행한다.
    이때 어떤 Controller 객체가 검색되더라도 같은 코드로 실행하려면, 모든 Controller의 최상위 인터페이스가 필요하다.

 

 

 

6. 용어 정리

 

❓ Handler란

  • Spring MVC에서는 자동차의 핸들과 마찬가지로 클라이언트의 요청을 처리하는 처리자를 Handler라고 한다.
  • Spring MVC에서 Handler는 Controller 클래스를 의미한다.
    Controller 클래스에 있는 '@GetMapping, @PostMapping' 같은 어노테이션이 붙어 있는 메서드들을
    핸들러 메소드라고 한다

.

❓ HandlerMapping이란

  • 클라이언트의 요청과 이 요청을 처리하는 Handler를 매핑해주는 역할을 하는 것이다.
  • @GetMapping("/login") 처럼 HTTP request Method(GET, POST등)와 Mapping URL을 기준으로 해당 Handler와 
    매핑이 되는데 Spring 에서는 여러가지 유형의 HandlerMapping 클래스를 제공하고 있다.

 

 HandlerAdapter란

  • 컨트롤러(핸들러)가 처리한 결과값을 받아서 ModelAndView 형태로 바꿔 DispatcherServlet에 보내준다.
    이런 기능 덕분에 어떤 형태의 객체로 결과값을 받던지간에 상관없이 웹요청을 처리할 수 있게 된다.
  • ex) 컨트롤러에서 String 값을 리턴해주어도 그 값을 ModelAndView로 변환해서 DispatcherServlet으로 보내준다.

 

 

❓ ViewResolver란

  • Resolver 는 무언가를 해석하고, 해결해주다라는 뜻이 있다.
  • ViewResolver는 DispatcherServlet에서 '이런 이름을 가진 View를 줘'라고 요청하면 DispatcherServlet에서 전달한
    View 이름을 해석한 뒤 적절한 View 객체를 리턴해주는 역할을 합니다.
반응형

+ Recent posts