관심쟁이 영호
[#8 Spring Boot 정주행] 유연한 컨트롤러 만들기 ㅣ Handler, Adapter, 다형성 본문
[#8 Spring Boot 정주행] 유연한 컨트롤러 만들기 ㅣ Handler, Adapter, 다형성
관심쟁이 영호 2021. 9. 16. 17:43지금까지 여러 종류의 모양을 반환하는 컨트롤러를 만들어보았다.
path부터 modelAndView 까지.. 계속해서 발전해왔다.
근데.. 개발자의 입장에서 modelAndView 1개만 반환해야 한다면 답답할 수 있다.
model과 view가 필요가 없는데, modelAndView를 반환해야하기 때문이다.
그래서!!
개발자가 무엇을 반환하든 상관없는 모양으로 만들어보자!
먼저 뒷내용을 이해하려면 객체의 "다형성"에 관해서 잘 알아야한다.
혹시나 모른다면! 다음 글을 보도록 하자.
목차
- 어댑터와 핸들러
- 어댑터 만들기
- 핸들러 만들기
- 마치며
어댑터와 핸들러
어댑터와 핸들러를 이해하기 전에, 위에서 이야기했던 내용을 계속해서 해보자.
개발자들은 반환 값을 상관하지 않고 여러 가지 방식 중에서 자기가 원하는 것을 반환하고 싶다.
(model, view, path, string 기타 등등..)
그러기 위해서는 어떻게 해야 할까?
다음 그림을 보자.
이해하기 힘들 수 있다. 천천히 보자.
핸들러란?
이전에 사용했던 Controller랑 똑같은 개념이다.
핸들러 어댑터란?
핸들러가 어떠한 값을 보내든 ModelAndView로 바꾸어주는 어댑터라고 할 수 있다.
어댑터는 "Stirng -> ModelAndView", "Model -> ModelAndView", "void -> ModelAndView" 등등 가능한 모든 반환 값을 ModelAndView로 바꾸어주는 역할을 한다.
왜 필요한가?
이제 제대로 이해해보자.
개발자가 어떤 값을 컨트롤러로 반환할지 모른다.
그래서 개발자가 어떠한 값을 리턴하더라도 상관없이 그 값을 ModelAndView로 바꾸어주기로 하자.
그렇게 되는 경우 리턴 값을 딱히 신경 쓰지 않아도 될 것이다.
해야 할 작업
1. 먼저, 반환할 값에 타입에 따라 어댑터를 생성해준다. 그리고 Front Controller에 어댑터를 추가해준다.
FrontController.java 추가
//...//
private void initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
handlerAdapters.add(new ControllerV4HandlerAdapter()); //V4 추가
}
//...//
2. handlerAdapter 생성
public interface MyHandlerAdapter {
boolean support(Object handler);
Model handle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws ServletException, IOException;
};
handle : 어댑터는 각 반환 값을 ModelAndView로 바꾸어주는 로직을 추가한다.
support : Front Controller가 해당 어댑터가 사용자가 요청한 내용을 처리할 수 있는지 확인하기 위한 support 메소드를 추가해준다.
3. 특정한 반환 값을 대처하는 Adapter를 만들자.
public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof ControllerV3);
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponseresponse, Object handler) {
ControllerV3 controller = (ControllerV3) handler;
Map<String, String> paramMap = createParamMap(request);
ModelView mv = controller.process(paramMap);
return mv;
}
//...//
ControllerV3는 ModelView의 타입을 반환하는 컨트롤러이다.
최종적으로 ModelView로 FrontController에 반환해준다.
그럼 다른 타입을 반환하는 내용을 보자.
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponseresponse, Object handler) {
ControllerV4 controller = (ControllerV4) handler;
Map<String, String> paramMap = createParamMap(request);
Map<String, Object> model = new HashMap<>();
String viewName = controller.process(paramMap, model);
ModelView mv = new ModelView(viewName);
mv.setModel(model);
return mv;
}
ControllerV4는 viewName인 String을 반환하는 Controller이다.
viewName을 ModelView로 바꾸어 리턴하는 모습이다.
이렇게 컨트롤러(핸들러) 반환 값에 따른 어댑터를 전부 생성해주면 어떤 반환 값이던 ModelAndView(ModelView)로 Front Controller가 받을 수 있을 것이다.
4. FrontController는 어댑터 check와 어댑터 실행을 전담한다.
다음 코드를 보며 이해해보자.
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
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);
MyView view = viewResolver(mv.getViewName());
view.render(mv.getModel(), request, response);
}
//요청에서 uri를 뽑는 메소드
private Object getHandler(HttpServletRequest request) {
String requestURI = request.getRequestURI();
return handlerMappingMap.get(requestURI);
}
// 해당 요청을 다룰 수 있는 adapter 찾기
private MyHandlerAdapter getHandlerAdapter(Object handler) {
for (MyHandlerAdapter adapter : handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
코드를 보면 handler를 얻고, 해당 핸들러가 지원하는지, support로 체크를 한다.
support 체크를 통과하여 어댑터를 찾으면 해당 어댑터의 handle을 호출한다.
해당 어댑터는 핸들러(컨트롤러)를 실행하게 된다. 그리고 컨트롤러의 반환 값을 ModelAndView로 변환하여 리턴한다.
ModelAndView를 받은 Front Controller는 ModelAndView를 ViewResolver를 거쳐 올바른 path로 바꾸어주고 view에 Model을 넘겨준다.
지금까지 했던 과정의 끝에 도달했다.
내용을 보자.
굳!
모든 구조를 이해했다.
아주 놀랍게도....
이 구조가 스프링이랑 똑같다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
(스프링은 더 많다 참고로)
스프링에서는 거의 모든 타입의 어댑터, 뷰 리졸버가 구현되어 있다.
끝!
다음 시간부터는 스프링에 대해서 공부해보자!
해당 포스팅은 "인프런 - 김영한 개발자님의 스프링 MVC 1편"을 참고하여 작성되었습니다.
'Bank-End > Spring Boot' 카테고리의 다른 글
[#7 SpringBoot 정주행] ViewResolver를 만들어보자! (0) | 2021.09.13 |
---|---|
[#6-2 Spring Boot 정주행] View Path를 없애보자! ㅣ MVC 프레임워크 만들기 (0) | 2021.09.12 |
[#6-1 Spring Boot 정주행] MVC 프레임워크를 직접 만들어보자 (0) | 2021.09.12 |
[#5 Spring Boot 정주행] HTTP로 HTML을 응답해보자! (0) | 2021.09.10 |
[#4 Spring Boot 정주행] Http에 대해서 알아보자! (0) | 2021.09.10 |