지난 포스팅에서 MVC 패턴의 Model, Controller, View를 구현했다. 이번 포스팅에서는 조금 더 유연한 MVC 패턴을 만들어 보려고 한다.
위 그림은 지난 포스팅까지 구현해본 MVC 패턴 구조이다. 여기서 Controller를 보자. 위 그림에서는 Controller가 매개변수로 Model을 받고 Model을 반환한다. 그러나 Controller에는 여러가지 종류가 있다. Model을 반환 할 수도, View name을 반환할 수도 있다. Controller는 어떤 데이터를 매개변수로 받느냐, 어떤 데이터를 반환하느냐에 따라 다양하게 분류된다. 실제로 SpringMVC에는 다양한 Controller가 존재한다. SpringMVC에 어떤 종류의 Controller가 있는지는 다음에 다루어 보도록 하겠다. 이번 포스팅에서는 다양한 종류의 Controller를 지원할 수 있는 MVC 패턴 구조에 대해서 다루어 보겠다.
어댑터 패턴 ( Adapter Pattern )
기존에는 Front-Servlet이 비즈니스 로직을 처리하기 위해 Controller의 process메소드를 호출했다. 그러다보니 Front-Servlet이 한 가지 Controller에 종속되어 버렸다. 유연한 MVC 패턴을 구현하려면 한 가지 Controller가 아닌 다양한 Controller를 지원해야 한다. 이를 위해, SpringMVC는 어댑터 패턴 ( Adapter Pattern )으로 결합도를 낮추고 확장성을 올리는 방식을 선택했다.
SpringMVC에서는 어댑터 패턴이 적용된 구조에서 Controller 구현체를 Handler라고 표현한다. Request(요청)이 들어오면 Front-Servlet은 요청에 매핑되는 Controller 구현체, 즉 handler를 생성한다. 여기서 Controller의 process 메소드를 바로 호출하면 기존의 방식처럼 강하게 결합하게 된다. process 실행 권한을 Adapter에게 넘기면 결합도를 낮추고 확장성을 올릴 수 있다. Adapter는 Controller의 종류에 따라 Adpater 구현체를 갖는다. 그러면 가장 먼저, 요청에 의해 생성된 handler가 어떤 Adapter 구현체에서 지원하는지 구분해야 한다.
그럼 간단히 예제로 만들어 본 Front-Servlet 코드를 확인해보자.
Front-Servlet
@WebServlet(name = "frontControllerServlet",urlPatterns = "/front-controller/*")
public class FrontControllerServlet extends HttpServlet {
private final Map<String,Object> handlerMappingMap = new HashMap<>(); // handler 모음
private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>(); // Adapter 모음
//중략 ...
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// [ STEP1. 요청과 매핑되는 Controller 구현체(handler) 가져오기 ]
Object handler = getHandler(request);
// 요청과 매핑되는 핸들러가 없을 시, 404에러
if (is404(response, handler)) return;
// [ STEP2. 핸들러를 지원하는 핸들러어댑터 탐색하기 ]
MyHandlerAdapter adapter = getHandlerAdapter(handler);
// [ STEP3. 어댑터에게 핸들러의 비즈니스 로직 수행 요청하기 ]
ModelView mv = adapter.handle(request, response, handler);
// [ STEP4. 비즈니스로직 수행 결과 VIEW 영역에 전달하기
String viewName = mv.getViewName();
MyView view = viewResolver(viewName);
view.render(mv.getModel(),request,response);
}
//중략...
}
STEP1에서 요청에 따른 handler를 생성하고 STEP2에서 handler를 지원하는 Adapter를 찾는다. getHandlerAdapter 메소드를 자세히 보자.
private MyHandlerAdapter getHandlerAdapter(Object handler) {
MyHandlerAdapter a;
for (MyHandlerAdapter adapter : handlerAdapters) { // Adatper 탐색
if( adapter.supports(handler)){ // handler를 지원하는 Adapter인지 확인
return adapter;
}
}
throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler=" + handler);
}
handler를 지원하는 Adapter를 찾을 때, Adapter의 supports 메소드가 호출됨을 확인할 수 있다. support 메소드는 handler가 해당 Adapter가 지원하는 Controller 유형인지를 판별하는 메소드이다.
ControllerHandlerAdapter
public class HandlerAdapterV3 implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
return ( handler instanceof ControllerV3 ); // handler가 지원하는 Controller인지 판별
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response,Object handler) throws ServletException, IOException {
ControllerV3 controller = (ControllerV3) handler;
ModelView mv = controller.process(createParamMap(request)); // process 메소드 호출
return mv;
}
}
코드를 보면 HandlerAdapter의 supports메소드가 instanceof로, 해당 Adapter가 지원하는 Controller 유형인지 확인할 수 있다. 이와 같이, handler를 지원하는 Adapter를 찾을 수 있다. Adpater를 찾으면 Adapter의 handle 메소드로 handler(Controller 구현체)의 process 메소드(비즈니스 로직)를 실행한다. 이처럼 Controller의 process 메소드가 프론트 서블릿이 아닌 어댑터에서 호출되어 실행된다. 프론트 서블릿과 컨트롤로가 직접 연관되지 않으니, 프론트 서블릿은 다양한 Controller 유형을 지원할 수 있다. 이것이 SpringMVC에 어댑터 패턴이 적용된 이유이다.
참고자료
'SPRING > Spring MVC' 카테고리의 다른 글
[SpringMVC] @RequestMapping, @Controller (0) | 2023.08.10 |
---|---|
[SpringMVC] HandlerAdapter (0) | 2023.08.10 |
[SpringMVC] MVC 패턴 구현하기(3) - Model (0) | 2023.08.09 |
[SpringMVC] MVC 패턴 구현하기(2) - Controller (0) | 2023.08.08 |
[SpringMVC] MVC 패턴 구현하기(1) - View (0) | 2023.08.08 |