5️⃣ V5 - V3
🤔💭 아주 완벽해졌어! 이제 더 손볼 곳이 없겠는걸! ( 정말 그럴까 … ? )
출처: 김영한의 스프링MVC (인프런)
💡 V4 는 아주 단순하고 실용적인 프레임워크이지만, 오로지 단 한 가지 방식의 컨트롤러 인터페이스만 사용할 수 있다는 단점이 있다. 이를 개선하여 유연한 사용이 가능하도록 확장해볼 것이다.
⚡️ 5-1. MyHandlerAdapter
- 핸들러(컨트롤러) 를 지정하여 가져올 수 있는 인터페이스를 만들었다. 이번 V5 에서는 V3 컨트롤러를 우선 가져와볼 것이라서 V3 인터페이스에서 사용하는 ModelView 핸들러를 만들어줬다.
public interface MyHandlerAdapter {
boolean supports(Object handler);
ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException;
}
⚡️ 5-2. FrontControllerServletV5
uri 매핑을 위한 Map 객체의 제네릭을 Object 타입으로 설정하여 어떤 핸들러(컨트롤러)가 와도 대응할 수 있도록 확장했다.
List 타입으로 핸들러 어댑터를 만들어서 사용 가능한 핸들러(컨트롤러) 리스트를 넣었다.이때 바로 컨트롤러 객체를 생성해서 넣는 것이 아닌, 중간 단계에 핸들러 어댑터(ControllerV3HandlerAdapter) 를 거치게 만들었다.
이번 V5 예제에서는 V3 의 핸들러 어댑터만 가져올 수 있게 만들고 실제 로직도 V3 컨트롤러에 맞춰 구현했다.
@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());
}
private void initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
}
@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");
}
}
⚡️ 5-3. ControllerV3HandlerAdapter
- 5-1의 MyHandlerAdapter 인터페이스를 구현하는 구현체. V3 컨트롤러를 반환하도록 만들었다.
public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
//ControllerV3 의 인스턴스야? 물어보고 맞으면 true, 아니면 false를 반환하는 구조
return (handler instanceof ControllerV3);
}
//V3 컨트롤러는 ModelView 객체를 사용했기 때문에 맞는 타입의 핸들을 만든다.
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
ControllerV3 controllerV3 = (ControllerV3) handler;
Map<String, String> paramMap = createParamMap(request);
ModelView mv = controllerV3.process(paramMap);
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;
}
}
🤔💭 그래봤자 V3 핸들러만 추가했잖아. 감흥이 없는걸… ➡️ V4 컨트롤러를 추가해보자.