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 의 구조를 이해해보자.