[Spring Web MVC] Adaptor
서버에서 클라이언트로 데이터를 전송할 때 ModelView 객체를 만들어서 데이터를 전송하기도 하고 Map 자료구조를 사용해 데이터를 전송하는 방식도 경험 해 봤는데..
특정 부분에는 첫 번째 방식을, 또 어느 부분에는 두 번째 방식을 사용해 데이터를 전송하게 할 수는 없을까?
어댑터 패턴을 사용하면 프론트 컨트롤러가 다양한 방식의 컨트롤러를 처리할 수 있다.
핸들러 : 클라이언트의 요청을 처리하는 객체 (컨트롤러)
클라이언트의 요청의 URL에 따라 매핑된 정보를 핸들러 매핑을 통해 조회한다.
그 후 프론트 컨트롤러는 해당 컨트롤러를 직접 호출하지 않고 핸들러 어댑터를 통해 호출해야 한다.
핸들러 어댑터에게 핸들러를 넘겨주면 핸들러 어댑터는 해당하는 핸들러를 호출해 실행 결과를 받고 프론트 컨트롤러에게 결과를 전달해준다.
어댑터를 사용하면 꼭 컨트롤러 뿐만 아니라 어떤 종류든지 처리할 수 있다.
public interface MyHandlerAdapter {
boolean supports(Object handler);
ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException, ServletException;
}
HandlerAdapter는 supports 메서드와 handle 메서드를 가지는 인터페이스다.
각각 특정 핸들러를 지원하는지와 실제 처리를 담당한다.
ModelView를 통해 데이터를 전송하는 메서드를 만들자.
인자로 request , response 대신 handler가 들어간다.
@WebServlet(name = "dq2", 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 initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
handlerAdapters.add(new ControllerV4HandlerAdapter());
}
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());
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());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("launch test");
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(Collections.unmodifiableMap(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 Object getHandler(HttpServletRequest request) {
String requestURI = request.getRequestURI(); // 요청 URI 꺼내
return handlerMappingMap.get(requestURI); // 해당하는 컨트롤러 찾아
}
private static MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
}
handlerMappingMap 에는 key로 URL / value로 핸들러가 있고, handlerAddapters에는 지원하는 핸들러들이 있다.
어떤 핸들러든 받을 수 있도록 Object 타입으로 변경됐다.
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 IOException, ServletException {
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;
}
}
어댑터를 변환을 제대로 활용하는 부분이다.
원래는 문자열으로 뷰의 논리적 이름만 반환했는데, 어댑터는 ModelView를 만들어 반환하도록 설계돼있다.
어댑터를 통해 ModelView를 만들어 형식에 맞춰 반환한다.
HandlerAdapter를 통해 Adapter 패턴을 구현하고, 요청을 처리할 수 있는 다양한 형태의 핸들러에 대한 공통 인터페이스를 제공한다.
핸들러 어댑터를 사용해 다양한 유형의 핸들러를 같은 방식으로 취급해 새로운 유형의 핸들러를 쉽게 추가할 수 있고, 프론트 컨트롤러와 개별 핸들러 사이의 결합도를 낮춰 유지보수성을 높인다.
'Spring > Spring Web MVC' 카테고리의 다른 글
[Spring Web MVC] 요청 정보 다루기 (0) | 2022.08.17 |
---|---|
[Spring Web MVC] Spring MVC (0) | 2022.08.16 |
[Spring Web MVC] Model (0) | 2022.08.15 |
[Spring Web MVC] Front Controller (0) | 2022.08.15 |
[Spring Web MVC] 서블릿과 HTTP 통신 (0) | 2022.08.14 |
댓글
이 글 공유하기
다른 글
-
[Spring Web MVC] 요청 정보 다루기
[Spring Web MVC] 요청 정보 다루기
2022.08.17 -
[Spring Web MVC] Spring MVC
[Spring Web MVC] Spring MVC
2022.08.16 -
[Spring Web MVC] Model
[Spring Web MVC] Model
2022.08.15 -
[Spring Web MVC] Front Controller
[Spring Web MVC] Front Controller
2022.08.15