과정을 즐기자

MVC 프레임워크 구현하기 - 2 본문

Spring

MVC 프레임워크 구현하기 - 2

320Hwany 2022. 10. 29. 18:24
  • Front Controller V3

1. 컨트롤러에 있는 HttpServletRequest, HttpServletResponse를 사용하지 않고 자바의 Map을 사용하여 

요청 파라미터를 넘길 수 있는 구조를 만들어보자. request 객체를 model로 사용하던 구조를 별도의 다른 Model 객체를 만들어서 반환하는 구조로 바꾸자

 

2. /WEB-INF, /.jsp와 같이 뷰 이름이 중복되는 부분을 제거하자

 

1번은 MyView를 반환하던 구조에서 ModelView로 반환하는 구조로 바꿔 해결한다. 2번은 ViewResolver를 사용해 논리 뷰 이름을 이용하여 해결한다.

 

먼저 viewName과 model을 갖는 ModelView클래스를 만든다

private String viewName;
private Map<String, Object> model = new HashMap<>();

 

 

프론트 컨트롤러에서 creatParamMap이라는 메소드로 request 객체 안에 있는 요청 파라미터 정보를 paramMap에 저장한다.

private static Map<String, String> creatParamMap(HttpServletRequest request) {
    Map<String, String> paramMap = new HashMap<>();
    request.getParameterNames().asIterator()
            .forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
    return paramMap;
}

 

요청 URL에 맞는 컨트롤러를 호출할 때 위에서 구한 paramMap을 넣어주고 컨트롤러는 ModelView 객체를 반환한다.

반환한 ModelView 객체를 viewResolver를 이용해 논리 뷰 이름에서 전체 이름으로 바꾼 MyView 객체를 반환한다.

MyView view = new MyView("/WEB-INF/views/" + viewName + ".jsp");

 

View에서 render을 호출해 JSP를 forward한다.

public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    modelToRequestAttribute(model, request);
    RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
    dispatcher.forward(request, response);
}

private static void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
    model.forEach((key, value) -> request.setAttribute(key, value));
}

이러한 과정으로 서블릿 종속성을 없애고 뷰 이름 중복을 제거하여 HTML 응답을 한다.

 

  • Front Controller V4

개발자는 컨트롤러 인터페이스를 구현한다. 이 과정에서 ModelView 객체를 생성하고 반환해야 하기 때문에 조금 번거롭다. 따라서 ModelView를 반환하던 구조에서 ViewName만 반환하는 구조로 바꿔보자

 

model 객체가 파라미터로 전달되기 때문에 다음과 같이 논리 이름만을 리턴해줘도 된다.

@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
    return "new-form";
}

 

프론트 컨트롤러가 담당하는 역할이 많아질수록 공통으로 처리하는 기능이 많다는 것이다. 따라서 개발자가 직접 구현해야 하는 구현부가 줄어들어 편리해지게 된다.

 

  • Front Controller V5

Front Controller V1, V2, V3, V4는 각각의 Controller interface만 사용할 수 있다. 이를 해결하기 위해서 어댑터 패턴을 사용해서 프론트 컨트롤러가 다양한 컨트롤러를 처리할 수 있도록 해보자. 여기서 핸들러는 컨트롤러의 더 넓은 범위의 이름이다. 

먼저 핸들러를 조회하고 그 핸들러를 바로 호출하는 것이 아닌 핸들러 어댑터 목록에서 그 핸들러를 처리할 수 있는 핸들러 어댑터를 먼저 찾는다. 그 후 핸들러 어댑터를 통해서 핸들러를 호출하고 핸들러 어댑터를 통해서 ModelView를 반환한다. 

 

프론트 컨트롤러에서는 요청한 URL에 맞는 핸들러를 반환할 수 있도록 handlerMappingMap을 정의하고 어떠한 핸들러를 처리할 수 있는 핸들러 어댑터들을 handlerAdapters로 만든다. 

private final Map<String, Object> handlerMappingMap = new HashMap<>();
private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();

 

여기서 핸들러 어댑터들은 MyHandlerAdapter interface를 구현한 것으로 나타낸다. 

public interface MyHandlerAdapter {

    boolean supports(Object handler);

    ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws ServletException, IOException;
}

supports 메소드는 어댑터가 들어온 handler를 처리할 수 있는지를 알려준다. 또한 핸들러 어댑터를 통해서 Modelview를 반환하는데 이를 위해 handle 메소드를 사용하였다.

 

이렇게 어댑터를 도입 함으로써 하나의 프론트 컨트롤러가 서로 다른 인터페이스로 구현한 핸들러들을 처리할 수 있게 되었다. 따라서 유연하고 확장성 있는 설계가 가능해졌다

 

  • 스프링 MVC

직접 만든 프레임워크 <-> 스프링 MVC 비교

 

FrontController -> DispatcherServlet

handlerMappingMap -> HandlerMapping

MyHandlerAdapter -> HandlerAdapter

ModelView -> ModelAndView

viewResolver -> ViewResolver

MyView -> View

 

'Spring' 카테고리의 다른 글

로그인 방식 개선 과정  (0) 2023.01.07
스프링을 사용하는 이유  (0) 2022.11.17
MVC 프레임워크 구현하기 - 1  (0) 2022.10.29
싱글톤 컨테이너 (모각소 4주차)  (0) 2022.07.24
웹 MVC  (0) 2022.07.18