Dispatcher Servlet

ppparkta·4일 전
0

Spring

목록 보기
6/7

Spring MVC

MVC → Model, View, Controller

스프링 MVC는 Servlet API 기반의 웹 프레임워크이다.

프론트 컨트롤러 패턴을 중심으로 설계되었다.

  • 따라서 중앙 서블릿인 Diapatcher Servlet프론트 컨트롤러
  • 실제 작업은 위임을 받는 여러 Handler들이 수행한다.

📌 MVC Pattern의 핵심은 Model ↔ View, View ↔ Controller 분리이다.

  • Model ↔ View
    • model: 비즈니스 로직이나 DB와의 상호작용 가능
    • view: UI의 매커니즘을 분리
    → 이로 인해 관심사 분리 가능. 그리고 일반적으로 뷰에 얽매이지 않는 로직이 테스트하기 용이하므로 테스트 용이성도 챙길 수 있음.
  • View ↔ Controller
    • 사실 중요도는 model ↔ view 보다 낮지만, 웹 요청을 사용하면서 view↔controller의 사용도 많아짐
    → 마찬가지로 관심사 분리 가능

Controller

  • MVC의 컨트롤러 두 종류
    • PageController
    • FrontController

Page Controller

페이지 컨트롤러는 웹 사이트의 특정 페이지 또는 작업에 대한 요청을 처리하는 개체이다.

  • 추론 @WebServlet 은 Spring 어노테이션 이전에 웹 요청을 처리하는 방식인 듯. HttpServlet을 상속받아서 사용함.
  • 실제
    • HttpServlet: 요청 처리의 기본 로직을 제공하는 추상 클래스
    • @WebServlet("/hello"): 이 클래스를 /hello 요청에 매핑
@WebServlet(
  name = "StudentServlet", 
  urlPatterns = "/student-record")
public class StudentServlet extends HttpServlet {

    private StudentService studentService = new StudentService();

    private void processRequest(
      HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {

        String studentID = request.getParameter("id");
        if (studentID != null) {
            int id = Integer.parseInt(studentID);
            studentService.getStudent(id)
              .ifPresent(s -> request.setAttribute("studentRecord", s));
        }

        RequestDispatcher dispatcher = request.getRequestDispatcher(
          "/WEB-INF/jsp/student-record.jsp");
        dispatcher.forward(request, response);
    }

    @Override
    protected void doGet(
      HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {

        processRequest(request, response);
    }

    @Override
    protected void doPost(
      HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {

        processRequest(request, response);
    }
}

Front Controller

프론트 컨트롤러는 모든 요청을 단일 처리기 객체로 집중하는 방법으로 자주 수행하는 공통적인 작업을 한대 모아 처리할 수 있는 장점이 있다.

아래 코드는 FrontController 패턴의 예시인데,
다음과 같이 FrontController + Strategy 패턴으로 작성됨.

이를 Spring MVC에 대입해보면 FrontController는 Dispatcher Servlet이 되는 거고 Front Command 등은 AdaptorHandler, PageController 등이 되는 것.

public class FrontControllerServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, 
      HttpServletResponse response) {
        FrontCommand command = getCommand(request);
        command.init(getServletContext(), request, response);
        command.process();
    }

    private FrontCommand getCommand(HttpServletRequest request) {
        try {
            Class type = Class.forName(String.format(
              "com.baeldung.enterprise.patterns.front." 
              + "controller.commands.%sCommand",
              request.getParameter("command")));
            return (FrontCommand) type
              .asSubclass(FrontCommand.class)
              .newInstance();
        } catch (Exception e) {
            return new UnknownCommand();
        }
    }
}
public abstract class FrontCommand {
    protected ServletContext context;
    protected HttpServletRequest request;
    protected HttpServletResponse response;

    public void init(
      ServletContext servletContext,
      HttpServletRequest servletRequest,
      HttpServletResponse servletResponse) {
        this.context = servletContext;
        this.request = servletRequest;
        this.response = servletResponse;
    }

    public abstract void process() throws ServletException, IOException;

    protected void forward(String target) throws ServletException, IOException {
        target = String.format("/WEB-INF/jsp/%s.jsp", target);
        RequestDispatcher dispatcher = context.getRequestDispatcher(target);
        dispatcher.forward(request, response);
    }
}
public class SearchCommand extends FrontCommand {
    @Override
    public void process() throws ServletException, IOException {
        Book book = new BookshelfImpl().getInstance()
          .findByTitle(request.getParameter("title"));
        if (book != null) {
            request.setAttribute("book", book);
            forward("book-found");
        } else {
            forward("book-notfound");
        }
    }
}

이해한 내용 바탕으로 지피티에게 요약 부탁해봄.

Servlet 기반 Front Controller vs Spring MVC 구조 비교

전통 서블릿 구조Spring MVC 구조
FrontControllerServletDispatcherServlet
FrontCommand (추상 명령)HandlerAdapter + Controller (또는 @RequestMapping 메서드)
SearchCommand, DeleteCommand@Controller 안의 메서드들
forward("xxx.jsp")ViewResolver가 JSP 혹은 템플릿으로 포워딩

평소 어렵게 느꼈던 개념들이 모두 나왔다. 이에 대해 하나씩 정리하다보면 Spring MVC의 Front Controller를 개략적으로 이해 가능할 것 같다.

Dispatcher Servlet

Process HTTP Request in Spring Web MVC

  1. Http Request
  2. Dispatcher Servlet
    • LocaleResolver, ThemeResolver, MultipartResolver 등으로 웹 요청 분석
  3. Handler Mapping
    • 요청에 알맞은 Handler 탐색하여 반환
  4. Handler Adaptor
    • 반환받은 Handler(=Controller)를 실행할 수 있는 Handler Adaptor 탐색
    • Handler 실행
  5. 실제 Handler 실행
    • 개발자가 제어하는 business login 실행
  6. View Resolver
  7. View Rendering

HandlerMapping

요청 URL에 해당하는 핸들러(Controller)를 찾아준다.

HandlerAdapter

찾아낸 핸들러를 적절한 방식으로 실행해주는 어댑터.

HandlerMapping으로 어떤 Handler가 실행될 지 알았다면, 이를 HandlerAdapter에 위임하여 실행시킨다.

📌 HandlerMapping이 Handler를 찾아주는데, 왜 HandlerMapping에서 바로 실행하지 않고 HandlerAdaptor의 변환을 기다릴까?
→ 확장성 때문이다!

// 옛날 방식
public class MyController implements Controller {
    public ModelAndView handleRequest(...) { ... }
}

// 요즘 방식 (애노테이션 기반)
@Controller
public class BookController {
    @GetMapping("/search")
    public String search(...) { ... }
}

다음과 같이 여러 타입의 Handler가 존재할 수 있는데, 이런 여러 타입의 Handler를 모두 지원하기 위해서 중간 객체인 HandlerAdaptor를 두는 것.

왜냐하면 Handler는 타입마다 호출 방식이 모두 제각각이기 때문이다.

Handler Type호출 방식
Controller 인터페이스.handleRequest(req, resp)
@RequestMapping리플렉션으로 메서드 찾아 호출
HttpRequestHandler.handleRequest()
사용자 정의 핸들러사용자 방식대로 호출해야 함

ViewResolver

Controller가 반환한 뷰 이름(String)을 실제 뷰 경로(JSP, Thymeleaf 등)로 변환해준다.

Dispatcher Servlet의 내용이 방대한 이유는 말 그대로 FrontController이기 때문이다.

각각의 개념이 무엇인지 얕게 이해했으므로 실제로 사용하면서 추가로 정리해보자.

profile
겉촉속촉

0개의 댓글