본문 바로가기
스프링

[스프링] MVC 프레임워크

by moonstal 2022. 6. 9.

MVC 프레임워크

프론트 컨트롤러 패턴

  • 특징
    • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청 받음
    • 프론트 컨트롤러가 요청에 맞는 컨트롤러 찾아 호출
    • 공통 처리
    • 프론트 컨트롤러 제외한 나머지 컨트롤러 서블릿 사용x

프론트 컨트롤러 도입1

  • 컨트롤러 인터페이스-> - 회원 등록 컨트롤러, 회원 저장 컨트롤러, 회원 목록 컨트롤러
    void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
  • 프론트 컨트롤러

    @WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
    public class FrontControllerServletV1 extends HttpServlet {
    
    private Map<String, ControllerV1> controllerMap = new HashMap<>();
      public FrontControllerServletV1() {
      controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
      controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
      controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
    }
    
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
      String requestURI = request.getRequestURI();
      ControllerV1 controller = controllerMap.get(requestURI);
    
      if (controller == null) {
      response.setStatus(HttpServletResponse.SC_NOT_FOUND);
      return;
      }
    
      controller.process(request, response);
    }
    }

View 분리2

  • MyView

    private String viewPath;
    
    public MyView(String viewPath) {
    this.viewPath = viewPath;
    }
    public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
    dispatcher.forward(request, response);
    }
  • 컨트롤러 인터페이스
    MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
    구현
    Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      return new MyView("/WEB-INF/views/new-form.jsp");
    }
  • 프론트 컨트롤러
    MyView view = controller.process(request, response);
    view.render(request, response);

Model 추가3

  • 서블릿 종속성 제거: 요청 파라미터 정보-> 자바의 Map, request 객체-> 별도의 Model 객체
  • 뷰 이름 중복 제거: 컨트롤러는 뷰의 논리 이름 반환
  • ModelView

    private String viewName;
    private Map<String, Object> model = new HashMap<>();
    
    public ModelView(String viewName) {
    this.viewName = viewName;
    }
  • 컨트롤러 인터페이스

    ModelView process(Map<String, String> paramMap);

    구현

    @Override
    public ModelView process(Map<String, String> paramMap) {
      //paramMap: request통해 받은 파라미터 정보
      String username = paramMap.get("username");
      int age = Integer.parseInt(paramMap.get("age"));
    
      Member member = new Member(username, age);
      memberRepository.save(member);
    
      ModelView mv = new ModelView("save-result");
      mv.getModel().put("member", member);
      return mv;
    }
  • 프론트 컨트롤러
    //createParamMap: request통해 받은 파라미터 정보 담기
    Map<String, String> paramMap = createParamMap(request); 
    ModelView mv = controller.process(paramMap);
    String viewName = mv.getViewName();
    //viewResolver: 논리 뷰 이름(members)-> 물리 뷰 경로로 변경(/WEB-INF/views/members.jsp)
    MyView view = viewResolver(viewName);
    view.render(mv.getModel(), request, response);
  • MyView

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

단순하고 실용적인 컨트롤러4

  • 컨트롤러 인터페이스
    //model 만들지 않고 사용하면된다, String 반환
    String process(Map<String, String> paramMap, Map<String, Object> model);
    구현
    model.put("member", member);
    return "save-result";
  • 프론트 컨트롤러

    모델 객체 프론트 컨트롤러에서 생성해서 넘김
    Map<String, Object> model = new HashMap<>();
    
    //논리 이름 직접 반환
    String viewName = controller.process(paramMap, model);
    


### 유연한 컨트롤러5


- 컨트롤러 인터페이스
//v3
ModelView process(Map<String, String> paramMap);

//v4
String process(Map<String, String> paramMap, Map<String, Object> model);
둘다 사용하려면? 어댑터 패턴


- 어댑터 인터페이스
    ```
    //handler는 컨트롤러
    boolean supports(Object handler);

    //어댑터 통해서 실제 컨트롤러 호출
    ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException;

구현v3

  @Override
   public boolean supports(Object handler) {
      return (handler instanceof ControllerV3);
   }

   @Override
   public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
      ControllerV3 controller = (ControllerV3) handler;
      Map<String, String> paramMap = createParamMap(request);
      ModelView mv = controller.process(paramMap);
      return mv;
   }

구현v4

   ControllerV4 controller = (ControllerV4) handler;
   Map<String, String> paramMap = createParamMap(request);
   Map<String, Object> model = new HashMap<>();
   String viewName = controller.process(paramMap, model);

   //형식맞추기
   ModelView mv = new ModelView(viewName);
   mv.setModel(model);

   return mv;
  • 프론트 컨트롤러

    private final Map<String, Object> handlerMappingMap = new HashMap<>();
     private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();
     public FrontControllerServletV5() {
        initHandlerMappingMap();
        initHandlerAdapters();
     }
     private void initHandlerMappingMap() {
        handlerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3());
        handlerMappingMap.put("/front-controller/v5/v3/members/save", new MemberSaveControllerV3());
        handlerMappingMap.put("/front-controller/v5/v3/members", new MemberListControllerV3());
    
        handlerMappingMap.put("/front-controller/v5/v4/members/new-form", new MemberFormControllerV4());
        handlerMappingMap.put("/front-controller/v5/v4/members/save", new MemberSaveControllerV4());
        handlerMappingMap.put("/front-controller/v5/v4/members", newMemberListControllerV4());
     }
     private void initHandlerAdapters() {
        handlerAdapters.add(new ControllerV3HandlerAdapter());
        handlerAdapters.add(new ControllerV4HandlerAdapter());
     }
     @Override
     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object handler = getHandler(request);
        if (handler == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        MyHandlerAdapter adapter = getHandlerAdapter(handler);
        ModelView mv = adapter.handle(request, response, handler);
        MyView view = viewResolver(mv.getViewName());
        view.render(mv.getModel(), request, response);
     }

링크
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1

'스프링' 카테고리의 다른 글

타임리프  (0) 2022.06.16
[스프링] 스프링 MVC  (0) 2022.06.09
[스프링] 서블릿, JSP, MVC 패턴  (0) 2022.06.09
[스프링] 웹 애플리케이션 이해, 서블릿  (0) 2022.06.09
스프링 핵심원리  (0) 2022.06.01