6️⃣   V5 - V4

🤔💭 그래봤자 V3 핸들러만 추가했잖아. 감흥이 없는걸… ➡️ V4 컨트롤러를 추가해보자.

⚡️   6-1. ControllerV4HandlerAdapter

  • V4 핸들러 어댑터를 추가했다. 그런데 문제가…?

  • V4 는 Model Map객체와, 폼으로부터 파라미터 값을 받아오는 인자(paramMap)를 매개변수로 하고 viewPath(String) 를 리턴받는 프로세스를 가진 컨트롤러이다.

  • MyHandlerAdapter 인터페이스의 추상메서드는 ModelView 타입의 객체를 반환하도록 설계되어 있다.

  • 그래서 핸들러 어댑터에서 데이터를 ModelView 객체에 들어가도록 가공해서 프론트 컨트롤러에 넘겨주는 역할을 수행한다. (이것이 어댑터의 역할이다 !)


public class ControllerV4HandlerAdapter implements MyHandlerAdapter {
    @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);
        HashMap<String, Object> model = new HashMap<>();
        String viewName = controller.process(paramMap, model);

        ModelView mv = new ModelView(viewName);
        mv.setModel(model);

        return mv;
    }

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

⚡️   6-2. FrontControllerServletV5

  • 프론트 컨트롤러는 수정할 사항이 거의 없다.

  • V4 컨트롤러가 사용 가능하도록 추가했으니 handlerMappingMap 과 handlerAdapters 종류애 V4 컨트롤러도 추가해주기만 하면 된다.


@WebServlet(name = "frontControllerServletV5", urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {

    //어떤 컨트롤러가 와도 대응할 수 있도록 Object 타입을 value 값으로 넣었다.
    private final Map<String, Object> handelerMappingMap = new HashMap<>();
    private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();

    public FrontControllerServletV5() {
        initHandlerMappingMap();
        initHandlerAdapters();
    }

    private void initHandlerMappingMap() {
        handelerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3());
        handelerMappingMap.put("/front-controller/v5/v3/members/save", new MemberSaveControllerV3());
        handelerMappingMap.put("/front-controller/v5/v3/members", new MemberListControllerV3());
        //V4 컨트롤러 추가
        handelerMappingMap.put("/front-controller/v5/v4/members/new-form", new MemberFormControllerV4());
        handelerMappingMap.put("/front-controller/v5/v4/members/save", new MemberSaveControllerV4());
        handelerMappingMap.put("/front-controller/v5/v4/members", new MemberListControllerV4());
    }

    private void initHandlerAdapters() {
        //V4 핸들러어댑터 추가
        handlerAdapters.add(new ControllerV3HandlerAdapter());
        handlerAdapters.add(new ControllerV4HandlerAdapter());
    }

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

        String requestURI = request.getRequestURI();
        //URI(key)값의 value(컨트롤러)를 handler 에 저장.
        Object handler = handelerMappingMap.get(requestURI);

        if(handler == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND); //웹 상태코드를 404로 바꾸고 리턴.
            return;
        }

        //해당 핸들러(컨트롤러)에 맞는 어댑터 가져오기
        MyHandlerAdapter adapter = getHandlerAdapter(handler);

        //V3 핸들러(컨트롤러)의 경우, ModelView 형태를 사용한다. 해당 어댑터에서 각 컨트롤러에서 사용하는 객체를 반환해줌.
        ModelView mv = adapter.handle(request, response, handler);
        //이하 과정은 V3 패키지 내용과 동일.
        String viewName = mv.getViewName();
        MyView view = viewResolver(viewName);

        view.render(mv.getModel(), request,response);
    }

    private MyHandlerAdapter getHandlerAdapter(Object handler) {
        for (MyHandlerAdapter adapter : handlerAdapters) {
            if(adapter.supports(handler)) {
                return adapter;
            }
        }
        throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler = " + handler);
    }

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

이렇게 스프링 MVC 의 기본 구조와 유사한 프레임워크를 직접 만들어보았다. 스프링은 핸들러를 사용하는 지금의 구조에서 어노테이션을 활용하여 간편하게 사용 가능하도록 만들었다. 다음 시간부터는 진짜 스프링 MVC 의 구조를 이해해보자.