이번에는 v4를 한번 넣어보자.
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());
//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());
}
v4를 넣어줄 수 있는 어댑터도 만들어주자.
private void ininHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
handlerAdapters.add(new ControllerV4HandlerAdapter());
}
ControllerV4HandlerAdapter에서 핸들로직도 만들어주자.
package hello.setvlet.web.frontcontroller.v5.adapter;
import hello.setvlet.web.frontcontroller.ModelView;
import hello.setvlet.web.frontcontroller.v5.MyHandlerAdapter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ControllerV4HandlerAdapter implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
return false;
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
return null;
}
}
핸들로직은 아래와 같다.
package hello.setvlet.web.frontcontroller.v5.adapter;
import hello.setvlet.web.frontcontroller.ModelView;
import hello.setvlet.web.frontcontroller.v4.ControllerV4;
import hello.setvlet.web.frontcontroller.v5.MyHandlerAdapter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class ControllerV4HandlerAdapter implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
return false;
}
@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를 어댑터에서 생성해준다.
ModelView mv = new ModelView(viewName);
mv.setModel(model);
return mv;
}
private 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로 넘어가는 걸 볼 수 있다... 해결중이다.
일단 결론적으로는 cycle이 아래와 같이 v3에서처럼 v4도 돌게된다.
//FromControllerServletV5
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 핸들러 호출하고
Object handler = getHandler(request);
if (handler == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 2 핸들러 어댑터를 가져오고
MyHandlerAdapter adapter = getHandlerAdapter(handler);
// 핸들러를 호출한다.
ModelView mv = adapter.handle(request, response, handler);
// 뷰네임 가져와서 뷰 리졸브 호출해주고
String viewName = mv.getViewName();// 논리이름 new-form
// 뷰를 얻은 다음에 뷰를 렌더 호출한다.
MyView view = viewResolver(viewName);
// 렌더 호출하고 모델 넘겨준다.
view.render(mv.getModel(), request, response);
}
일단 결론적으로는 이렇게 설계한 프레임워크가 굉장히 설계가 잘되었다.
- 역할과 구현이 분리가 되어있는데 핸들러 어댑터 목록은 인터페이스로 되어있고 인터페이스를 통해서 호출을 하고
- 정 안되는건 어댑터를 통해서 맞추는 식으로 되어있다.
- viewResolver도 인터페이스로 만들어서 꽂아넣는식으로 하면 된다.
- view도 ? 그렇다 다 인터페이스 기반으로 설계해서 바꾸고 싶은 부분만 구현체로 꽂아 넣으면 되는 것이다!
- Front Controller를 고치지 않고 OCP(SOLID의 Open-Closed Principle : 개방-폐쇄 원칙)를 지키면서 개발을 할 수가 있다.
이런 부분들 이런거를 구현체로 넣어주는 것이다.
// FromControllerV5 class
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());
//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());
}
private void ininHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
handlerAdapters.add(new ControllerV4HandlerAdapter());
}
정리를 해보면
지금까지 v1 ~ v5로 점진적으로 프레임워크를 발전시켰는데
- v1: 프론트 컨트롤러를 도입하고
- 기존 구조를 최대한 유지한채로 프론트 컨트롤러를 도입했다.
- v2 에서는 view로 분류하고
- 단순 반복 되는 뷰로 로직을 분리하고
- v3: Model 추가해서
- 서블릿 종속성을 제거하고
- 뷰 이름 중복을 제거했다.
- v4: 단순하고 실용적인 컨트롤러로
- v3와 거의 비슷하고
- 구현 입장에서 ModelView를 직접 생성해서 반환하지 않도록 편리하게 인터페이스를 제공했다.
- v5: 유연한 컨트롤러로
- 어댑터를 도입하고
- 어댑터를 추가해서 프레임 워크를 유연하고 확장성 있게 설계를 했다.
여기에서 애노테이션을 사용해서 컨트롤러를 편리하게 발전시킬 수도 있다.
애노테이션을 지원하는 어댑터를 추가하면 된다
그럼 다형성과 어댑터 덕분에 기존 구조 유지하면서, 프레임워크의 기능을 확장 할 수 있다.
스프링 MVC로 들어가기 전에 스프링 MVC의 핵심 구조를 파악하는데 필요한 부분을 알아보았다.
<출처 김영한: 스프링 MVC 1편 - 벡앤드 웹 개발 핵심 기술>
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -
www.inflearn.com
'Spring > SpringMVC' 카테고리의 다른 글
핸들러 매핑과 핸들러 어댑터 (0) | 2022.04.28 |
---|---|
스프링 MVC 전체 구조 (0) | 2022.04.25 |
유연한 컨트롤러1 - v5 (0) | 2022.04.25 |
단순하고 실용적인 컨트롤러 - v4 (0) | 2022.04.25 |
Model 추가 - v3 (0) | 2022.04.24 |