MVC + Front Controller Pattern

기존의 구현한 mvc framework에서 controller pattern을 구현하기 위해 점진적으로 controller을 개선해나게 된다.

Front Controller?

기존의 controller들은 아래의 그림과 같이 url-mapping, jsp forward, 등의 공통 기능들을 개별적으로 수행했다. 하지만, 이렇게 되면 코드 자체도 중복되는 부분도 증가하게 되고, 유지보수가 어렵게 된다.

before_front_controller

그런 공통 기능들을 묶어서 수행하기 위해 아래와 같이 front-controller을 둬서 처리하게끔 한다.

after_front_controller

front-controller 패턴을 도입하게 되면서, 모든 http request가 front controller을 거쳐서 오게 되고, front controller는 맞는 controller만 호출하면 된다.

controller들은 servlet에 종속적이지 않게 되어, 자바 코드로 실행하는 것이 가능하다.

V1

front_controller_v1

우선 기본적인 기능을 수행하는 front-controller을 구현해보자. front controller는 url_mapping 수행 및 controller 호출을 수행하게 된다.

ControllerV1 Interface

public interface ControllerV1 {
void process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}

ControllerV1 라는 인터페이스를 둬서 controller들을 일괄적으로 관리할 수 있도록 한다.

Controllers

controller 들은 ControllerV1의 process 메소드를 오버라이드 하면 된다.

MemberFormControllerV1

public class MemberFormControllerV1 implements ControllerV1 {
    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String viewPath="/WEB-INF/views/new-form.jsp";
        RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
        requestDispatcher.forward(request,response);
    }
}

MemberSaveControllerV1

public class MemberSaveControllerV1 implements ControllerV1 {
    private MemberRepository memberRepository=MemberRepository.getInstance();
    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member newMember=new Member();
        newMember.setUsername(username);
        newMember.setAge(age);

        memberRepository.save(newMember);

        //Model에 데이터 보관
        request.setAttribute("member",newMember);

        //View로 이동동
        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);

    }
}

MemberListControllerV1

public class MemberListControllerV1 implements ControllerV1 {
    private MemberRepository memberRepository=MemberRepository.getInstance();
    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        List<Member> members=memberRepository.findAll();

        //Model에 데이터 보관
        request.setAttribute("members",members);

        //View로 이동동
        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);

    }
}

FrontControllerV1

front controller에서는 url mapping 정보와 http request에 맞는 controller을 호출해야한다. front controller는 http request를 받아야 하므로 servlet으로 생성하게 된다.

Url Mapping

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());
      }

Map을 이용해서 각각의 url 요청에 대한 controller 정보를 등록해놓는다.

Calling Controller

String requestUrl=request.getRequestURI();
System.out.println("requestUrl = " + requestUrl);

ControllerV1 controller=controllerMap.get(requestUrl);

if(controller==null){
    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
    return;
}

controller.process(request,response);

request로 들어온 url을 분석해서 그에 맞는 controller을 호출한다. 없으면 404 에러 코드를 출렧한다.

전체 코드

@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{
        System.out.println("FrontControllerServletV1.service");
        
        String requestUrl=request.getRequestURI();
        System.out.println("requestUrl = " + requestUrl);

        ControllerV1 controller=controllerMap.get(requestUrl);

        if(controller==null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        controller.process(request,response);
    }

}

V2

V1 방식을 통해 기본적인 front controller의 형태를 구성하였다. 이번에 개선할 부분은 중복되는 forward 호출부분이다. 각각의 controller에서 직접 forward 메소드를 호출하게 되면서 이 부분이 중복된다.

front_controller_v2

이제는 이 부분을 위 그림처럼 MyView 클래스를 이용해서 front controller에서 통제하도록 구현하겠다.

MyView Class

public class 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(this.viewPath);
        dispatcher.forward(request, response);
    }
}

Controller는 MyView를 통해 viewPath를 전달하게 되고, Front Controller는 각각의 controller들로부터 받은 MyView 객체를 이용해서 render 메소드 호출을 통해 jsp 파일로 forwarding 해준다.

ControllerV2 Interface

public interface ControllerV2 {
    MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}

각각의 controller들이 MyView 객체를 반환할 수 있도록 Interface를 수정해준다.

Controllers

MemberFormControllerV2

public class MemberFormControllerV2 implements ControllerV2 {
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        return new MyView("/WEB-INF/views/new-form.jsp");
    }
}

위와 같이 viewPath를 MyView 객체에 담아서 반환한다.

MemberSaveControllerV2

public class MemberSaveControllerV2 implements ControllerV2 {
    private MemberRepository memberRepository=MemberRepository.getInstance();
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member newMember=new Member();
        newMember.setUsername(username);
        newMember.setAge(age);

        memberRepository.save(newMember);

        //Model에 데이터 보관
        request.setAttribute("member",newMember);

        return new MyView("/WEB-INF/views/save-result.jsp");
    }
}

MemberListControllerV2

public class MemberListControllerV2 implements ControllerV2 {
    private MemberRepository memberRepository=MemberRepository.getInstance();
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        List<Member> members=memberRepository.findAll();

        //Model에 데이터 보관
        request.setAttribute("members",members);


        return new MyView("/WEB-INF/views/members.jsp");
    }
}

FrontControllerV3

front controller에서는 전달받은 MyView 객체의 render 메소드를 호출해야 한다.

calling render method

MyView myView=controller.process(request,response);
myView.render(request,response);

url_mapping 이나 controller을 호출하는 부분은 이전과 큰 차이가 없다.

전체 코드

@WebServlet(name="frontControllerServletV2",urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV2 extends HttpServlet {
    private Map<String, ControllerV2> controllerMap=new HashMap<>();

      public FrontControllerServletV2() {
          controllerMap.put("/front-controller/v2/members/new-form", new
                  MemberFormControllerV2());
          controllerMap.put("/front-controller/v2/members/save", new
                  MemberSaveControllerV2());
          controllerMap.put("/front-controller/v2/members", new
                  MemberListControllerV2());
      }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        System.out.println("FrontControllerServletV2.service");
        
        String requestUrl=request.getRequestURI();
        System.out.println("requestUrl = " + requestUrl);

        ControllerV2 controller=controllerMap.get(requestUrl);

        if(controller==null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        MyView myView=controller.process(request,response);
        myView.render(request,response);
    }

}

V3

확실히 forward 호출 부분을 front-controller에서 처리하니, 중복되는 부분이 줄어들었다. 이번에 개선할 부분은 controller들을 servlet 종속적이지 않도록 이용하는 것이다. controller들은 servlet을 이용하지 않아도 비즈니스 로직이 수행가능하도록 하기 위해 Model을 이용해서 데이터를 주고받을 수 있도록 한다.

기존의 request.getAttribute/setAttribute를 이용하는 것이 아니라, Model 클래스를 이용해서 데이터를 주고 받을 수 있도록 한다.

또한 viewPath가 “/WEB-INF/views/new-form.jsp” 와 같은 형태인데, 이는 물리적 위치와 논리적 파일 이름이 결합되어 사용되는 형태로, 사용자는 논리적 파일 이름만 제시하고 나머지는 부분은 viewResolver을 통해 완전한 경로를 만들 수 있도록 개선한다.

front_controller_v3

위의 그림을 보면 controller들은 비즈니스 로직을 수행하고, 이때 발생한 데이터, viewPath 정보를 ModelView 객체를 이용해서 front controller에 전달하게 되고, front controller는 ModelView의 viewPath을 이용해서 viewResolver을 통해 MyView 객체를 반환받게 된다. 최종적으로 MyView 객체의 render 메소드에 controller에서 전달받은 model을 넘겨 view를 출력한다.

ModelView Class

해당 클래스는 viewPath(논리 파일이름)과 데이터를 저장할 수 있는 클래스이다.

public class ModelView {
    private String viewName;
    private Map<String,Object> model=new HashMap<>();

    public ModelView(String viewName) {
        this.viewName = viewName;
    }
    public String getViewName(){
        return viewName;
    }
    public Map<String, Object> getModel() {
        return model;
    }
}

ControllerV3

이제는 controller가 ModelView를 반환할 수 있도록 interface를 수정한다.

public interface ControllerV3 {
    ModelView process(Map<String, String> paramMap);
}

위 처럼 request, response를 전달하는 것이 아니라 parameter만 전달하게 되면 controller들은 servlet을 전혀 사용하지 않고 비즈니스 로직을 수행할 수 있다.

Controllers

MemberFormControllerV3

public class MemberFormControllerV3 implements ControllerV3 {
    @Override
    public ModelView process(Map<String, String> paramMap) {
        return new ModelView("new-form");
    }
}

controller는 ModelView에 jsp파일 전체 경로가 아닌 파일이름만 넣어서 전달한다.

MemberSaveControllerV3

public class MemberSaveControllerV3 implements ControllerV3 {
    private MemberRepository memberRepository=MemberRepository.getInstance();
    @Override
    public ModelView process(Map<String, String> paramMap) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));

        Member newMember=new Member();
        newMember.setUsername(username);
        newMember.setAge(age);

        memberRepository.save(newMember);

        //Model에 데이터 보관
        ModelView modelView = new ModelView("save-result");
        modelView.getModel().put("member",newMember);
        return modelView;
    }
}

Model에 데이터 추가

ModelView modelView = new ModelView("save-result");
modelView.getModel().put("member",newMember);

기존에 request.setAttribute를 이용해서 데이터를 전달하는데, 이제는 ModelView 내의 model을 통해서 데이터를 전달하게 된다. 저장하게 될때, parameter name, parameter value 형태로 저장해서 나중에 request에 전달하기 용이 하도록 한다.

MemberListControllerV3

public class MemberListControllerV3 implements ControllerV3 {
    private MemberRepository memberRepository=MemberRepository.getInstance();
    @Override
    public ModelView process(Map<String, String> paramMap) {
        List<Member> members=memberRepository.findAll();

        ModelView modelView = new ModelView("members");
        modelView.getModel().put("members",members);
        return modelView;
    }

}

FrontControllerV3

front controller에서는 Http Request로 요청받은 parameter를 파싱해서 Controller에 전달해야한다.

전달받은 ModelView 객체를 이용해서 작업을 수행한다. ModelView에 저장된 viewPath 정보를 이용해 viewResolver를 통한 MyView 객체를 생성하게 된다.

그런 다음, MyView의 render 메소드에 ModelView 객체의 모델을 넣어서 호출하게 된다. 이를 위해 우선적으로, MyView 클래스를 수정해야한다.

MyView Class

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

위 처럼, model를 받았을 때 model에 등록된 paramter 정보를 분석해서 request에 담아줘야한다. 기존에 저장할떄, parameter name, parameter value 형태로 저장했기 때문에 request에 저장하기 쉽다.

Request Parameter Parsing

Map<String, String> paramMap=createParamMap(request,response);

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

controller에 parameter 정보를 전달할 때, Map<String,String> 형태로 전달하기 위해 위와 같이 request.getParameterNames를 활용해서 Map을 생성해준다.

viewResolver

ModelView modelView=controller.process(paramMap);
MyView myView=viewResolver(modelView.getViewName());

myView.render(modelView.getModel(),request,response);

private MyView viewResolver(String viewName){
          return new MyView("/WEB-INF/views/"+viewName+".jsp");
    }

viewResolver는 파일의 물리적 주소와 논리적 이름을 결합해서 viewPath를 완성한 다음 MyView 클래스에 담아서 반환한다.

V4

ModelView 객체를 이용해서 front controller와 controller가 데이터를 주고받게 되면서, controller는 더 이상 servlet의 request, response를 사용하지 않아도 되고, viewPath 또한, 전체 경로가 아닌 논리적 파일 이름만 제공하므로써 간편하게 구현할 수 있었다.

이번에 개선할 부분은 ModelView 객체이다. controller는 매번 ModelView 객체를 만들어서 viewPath와 parameter를 ModelView에 넣어서 front controller에 제공하였다. 하지만, 매번 ModelView 객체를 생성해서 이를 반환하는 부분은 어떻게 보면 개발자 입장에서는 불필요한 부분일 수도 있다.

front_controller_v4

위의 그림을 보면, front controller에서 model을 만들어서 이를 controller에 제공하고, controller는 이 model에 데이터를 담아서 전달하면 된다. 또한, viewPath는 String 형태로 바로 반환하도록 한다.

ControllerV4

Controller는 front controller로 부터 request parameter 목록과 model을 입력받아 viewPath를 String 형태로 전달하게 된다.

public interface ControllerV4 {
    String process(Map<String, String> paramMap, Map<String,Object> model);
}

Controllers

MemberFormControllerV4

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

controller는 jsp파일 전체 경로가 아닌 파일이름을 그대로 문자열 형태로 반환한다.

MemberSaveControllerV4


public class MemberSaveControllerV4 implements ControllerV4 {
    private MemberRepository memberRepository=MemberRepository.getInstance();
    @Override
    public String process(Map<String, String> paramMap,Map<String,Object> model) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));

        Member newMember=new Member();
        newMember.setUsername(username);
        newMember.setAge(age);

        memberRepository.save(newMember);

        model.put("member",newMember);
        return "save-result";
    }
}

Model에 데이터 추가

model.put("member",newMember);

기존에는 ModelView를 생성해서, viewPath 와 모델을 입력해서 ModelView 객체를 반환했는데, 이제는 front controller에서 model을 제공하기 때문에, 제공된 model에 데이터를 넣으면 되고 jsp의 파일 이름만 반환하면 된다.

MemberListControllerV4

public class MemberListControllerV4 implements ControllerV4 {
    private MemberRepository memberRepository=MemberRepository.getInstance();
    @Override
    public String process(Map<String, String> paramMap,Map<String,Object> model) {
        List<Member> members=memberRepository.findAll();

        model.put("members",members);
        return "members";
    }

}

FrontControllerV4

front controller에서 직접 Model를 생성해서 controller에 전달하는 부분만 변경하면 된다.

Model를 생성해서 controller에 전달한다.

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

String viewPath=controller.process(paramMap,model);
MyView myView=viewResolver(viewPath);

전체 코드

@WebServlet(name="frontControllerServletV4",urlPatterns = "/front-controller/v4/*")
public class FrontControllerServletV4 extends HttpServlet {
    private Map<String, ControllerV4> controllerMap=new HashMap<>();

      public FrontControllerServletV4() {
          controllerMap.put("/front-controller/v4/members/new-form", new
                  MemberFormControllerV4());
          controllerMap.put("/front-controller/v4/members/save", new
                  MemberSaveControllerV4());
          controllerMap.put("/front-controller/v4/members", new
                  MemberListControllerV4());
      }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        System.out.println("FrontControllerServletV4.service");
        
        String requestUrl=request.getRequestURI();
        System.out.println("requestUrl = " + requestUrl);

        ControllerV4 controller=controllerMap.get(requestUrl);

        if(controller==null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        Map<String, String> paramMap=createParamMap(request,response);
        Map<String, Object> model=new HashMap<>();

        String viewPath=controller.process(paramMap,model);
        MyView myView=viewResolver(viewPath);

        myView.render(model,request,response);

    }

    private Map<String,String> createParamMap(HttpServletRequest request,HttpServletResponse response){
        Map<String, String> paramMap=new HashMap<>();
        request.getParameterNames().asIterator().forEachRemaining(
                paramName-> paramMap.put(paramName,request.getParameter(paramName))
        );
        return paramMap;
    }
    private MyView viewResolver(String viewName){
          return new MyView("/WEB-INF/views/"+viewName+".jsp");
    }
}

V5

프레임워크의 공통 기능이 많으면 많을 수록 개발자가 구현해야되는 부분은 점점 더 줄어들고, 더욱 편하게 개발에 임할 수 있습니다. V1 -> V4 까지 많은 부분은 점진적으로 발전하면서 점점 controller 개발이 쉬워졌습니다.

만약, V3 이나 V4 controller을 선택해서 사용하고자 한다면 어떻게 해야 될까?

이를 위해 어댑터 개념을 도입해야한다.

front_controller_v5

위의 그림을 보면 핸들러와 핸들러 어뎁터 부분이 추가되는 것을 확인할 수 있다.

handler는 controller의 상위 개념으로 어댑터에 의해 실행된다.

handler adapter는 front controller와 handler 사이에 존재하며, front controller는 handler adapter를 이용해서 적절한 handler를 호출하게 된다. 이때, 해당 핸들러를 실행하기 위해서는 적절한 어댑터가 필요한데, 이러한 어댑터를 여러개 가지고 있으므로써 다양한 핸들러를 이용하는 것이 가능해진다.

HandlerAdapter

MyHandlerAdapter Interface

public interface MyHandlerAdapter {
    boolean supports(Object handler);

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

}

MyHandlerAdapter라는 인터페이스를 둬서 각각의 handler adapter를 관리한다.

support 해당 핸들러가 어댑터를 처리할 수 있는지 여부를 나타내는 메소드이며,

handle 메소드는 controller을 호출하며 ModelView를 반환 받는다.

ControllerV3 관련 핸들러 어댑터

Supports 메소드

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

해당 어댑터는 ControllerV3를 처리할 수 있음을 나타내는 메소드이다.

handler 메소드

@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
    ControllerV3 controller=(ControllerV3) handler;
    Map<String,String> paramMap=createParamMap(request,response);

    ModelView mv=controller.process(paramMap);
    return mv;
}

이 부분은 front controller가 controller을 호출하는 부분과 매우 유사하며, handler adpaer가 handler를 통해 controller을 호출하는 것이다. request parameter 와 model을 전달해서 ModelView를 반환받는다.

FrontControllerServletV5

HandlerMappingMap

private final Map<String,Object> handlerMappingMap=new HashMap<>();

public void initHandlerMapping(){
    //v3
    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());
}

위와 같이 request url에 대한 handlerMappingMap을 유지한다.

HandlerAdapter

private final List<MyHandlerAdapter> handlerAdapters=new ArrayList<>();public void initHandlerAdapters(){
        handlerAdapters.add(new ControllerV3HandlerAdapter());
    }

해당 front controller에 등록되어 있는 handler adapter

service 수행

@Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        System.out.println("FrontControllerServletV3.service");

        Object handler=getHandler(request);

        if(handler==null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        MyHandlerAdapter myHandlerAdapter=getHandlerAdapter(handler);
        ModelView mv = myHandlerAdapter.handle(request, response, handler);

        MyView myView = viewResolver(mv.getViewName());
        myView.render(mv.getModel(),request,response);
    }
    
 private Object getHandler(HttpServletRequest request){
        String requestURI=request.getRequestURI();
        return handlerMappingMap.get(requestURI);
    }

private MyHandlerAdapter getHandlerAdapter(Object handler){
    for(MyHandlerAdapter adapter: handlerAdapters){
        if(adapter.supports(handler)){
            return adapter;
        }
    }
    throw new IllegalArgumentException("handler adapter을 찾을 수 없습니다: " + handler);
}
  1. getHandler 메소드를 이용해서 http request의 request URI를 분석해서 이에 대응되는 handler를 handlerMappingMap에서 찾는다.

  2. 해당 핸들러를 처리할 수 있는 handler adapter를 찾기 위해 getHandlerAdapter 메소드를 실행한다. 각각의 handler adpater에 대해 해당 handler를 처리 가능한지 support method을 이용한다.

  3. handler adapter는 handler를 호출하고 최종적으로 ModelView를 반환받게 된다.

ControllerV4 관련 코드 추가

HandlerMapping, HandlerAdapter 추가

public void initHandlerMapping(){
        //v3
        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());
        //v4
        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", new MemberListControllerV4());
    }

    public void initHandlerAdapters(){
        handlerAdapters.add(new ControllerV3HandlerAdapter());
        //v4
        handlerAdapters.add(new ControllerV4HandlerAdapter());
    }

ControllerV4HandlerAdapter

ControllerV4를 처리하는 HandlerAdapter를 추가한다.

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

    @Override
    public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        ControllerV4 controller=(ControllerV4) handler;
        Map<String,String> paramMap=createParamMap(request,response);
        Map<String,Object> model=new HashMap<>();
        String viewPath=controller.process(paramMap,model);

        ModelView modelView=new ModelView(viewPath);
        modelView.setModel(model);
        return modelView;
    }

ControllerV3와 달리 Model를 직접 만들어서 controller에 전달한다.

controller로 부터 viewPath를 문자열로 받기 때문에 ModelView 객체를 만들어서 viewPath와 model를 넣어서 front controller로 전달하게 된다.

이처럼 Front Controller에서 ModelView를 이용하는 방식으로 통일시켜 여러 controller들을 이용한 처리가 가능한 것이다.

References

link: inflearn

link:springmvc

댓글남기기