5단계 - 어댑터 패턴을 적용하기
- 어댑터 패턴을 적용한 프론트 컨트롤러는 다양한 방식의 컨트롤러를 처리할 수 있다.

- 핸들러 어댑터는 어댑터의 역할을 하여 다양한 종류의 컨트롤러를 호출할 수 있도록 해준다.
public interface MyHandlerAdapter {
boolean supports(Object handler);
ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException;
}
- 어댑터의 역할은 실제 컨트롤러를 호출하고 ModelView 를 반환해야 한다.
- 만약 컨트롤러가 반환하지 못할 경우 어댑터가 대신 ModelView 를 직접 생성해서 반환해야 한다.
1️⃣ 실제 어댑터 구현하기
V3 을 지원하는 어댑터
public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof ControllerV3);
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
ControllerV3 controller = (ControllerV3) handler;
Map<String, String> paramMap = createParamMap(request);
return controller.process(paramMap);
}
private Map<String, String> createParamMap(HttpServletRequest request) {
HashMap<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator().forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
return paramMap;
}
}
V4 을 지원하는 어댑터
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 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;
}
}
2️⃣ 프론트 컨트롤러 리팩토링
@WebServlet(name = "frontControllerServletV5", urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {
private final Map<String, Object> handlerMappingMap = new HashMap<>();
private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();
public FrontControllerServletV5() {
initHandlerMappingMap();
initHandlerAdapters();
}
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());
}
private void initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("FrontControllerServletV1.service"); // 실제 테스트 해볼 때에는 로그로 해보는 것이 좋다.
Object handler = getHandler(request);
if (handler == null) { // 페이지가 없거나 못찾을 경우(예외처리)
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
MyHandlerAdapter adapter = getHandlerAdapter(handler);
ModelView mv = adapter.handle(request, response, handler);
String viewName = mv.getViewName();
MyView view = viewResolver(viewName);
view.render(mv.getModel(), request, response);
}
private Object getHandler(HttpServletRequest request) {
String requestURI = request.getRequestURI();
return handlerMappingMap.get(requestURI);// value 값에 들어있는 객체 인스턴스가 반환이 된다.
}
private MyHandlerAdapter getHandlerAdapter(Object handler) {
for (MyHandlerAdapter adapter : handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler = " + handler);
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
}
- 이전과 달라진 점이 있다면, 전의 코드에서는 컨트롤러를 직접 매핑해서 사용했지만, 이제는 어댑터를 사용하기 때문에 컨트롤러 뿐만 아니라 어댑터가 지원하는 선에서는 어떤 것이라도 URL 을 매핑해서 사용이 가능하다.
<김영한 - '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술'을 참고하였습니다.>
'Spring' 카테고리의 다른 글
| [spring] logging (0) | 2023.03.13 |
|---|---|
| [spring] spring MVC 구조 이해 - dispatcherServlet (0) | 2023.03.12 |
| [spring] Front Controller (2) (0) | 2023.03.08 |
| [spring] Front Controller (1) (0) | 2023.03.07 |
| [spring] MVC 패턴 도입하기 (0) | 2023.03.06 |