Web/Spring

SpringMVC (4.5) - FrontController V5 [어댑터 패턴 - 중요]

now0204 2023. 10. 2. 15:31

 

1. 유연한 컨트롤러 - v5

 

- 이전에 살펴본 v3방식과 v4방식을 섞어서 쓰고 싶다면 어떻게 해야할까?

- 두 컨트롤러는 매개변수와 리턴값이 다르다.

 

** 어댑터 패턴

 

두 컨트롤러의 인터페이스는 다르므로 호환이 불가능하다 마치 110v와 220v와 같다.

이럴 때 호환이 안되는 두 인터페이스를 일관되게 사용하고 싶을 때 어댑터 패턴을 사용하면 된다.

v3용 어댑터와 v4용 어댑터를 만들고, 프론트 컨트롤러는 어댑터를 통해서 컨트롤러 메서드를 호출하게 만들자.

 

변화하는 부분은 요청에 따른 각컨트롤러, 그리고 컨트롤러별 매개변수와 리턴 값이다.

어댑터는 이러한 부분을 캡슐화함 -> 각 컨트롤러에 따라 로직을 처리하도록 request에서 값 뽑기, 매개변수 전달 등등을 수행 

 

실제 로직 처리를 각 어답티(컨트롤러)에게 위임하는데, 이때 각 어답티들 호출에 필요한 매개변수 처리등

어댑터 안에서 수행 -> 클라이언트는 걍 수행 결과만 리턴받음!! 

 

출처: 스프링MVC(인프런) - 김영한

- 핸들러 어댑터: 중간의 어댑터역할 다양한 종류의 컨트롤러를 일관되게 호출

- 핸들러 : 컨트롤러임 좀 더 넓은 의미를 담기 위해서 핸들러라고 표현함 이제 어댑터만 있다면, 어떠한 종류의 컨트롤러도 다 처리할 수 있다 (일관되게)

 

1.1 MyHandlerAdapter

 

어댑터 인터페이스 

public interface MyHandlerAdapter{

	boolean supports(Object handler);
    
    ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 
    throws ServletException, IOException;
}

- supports : 어댑터가 해당 컨트롤러 처리할 수 있는지 판단

- handle : 실제 컨트롤러 호출해서 결과로 ModelView 반환해야함 

- 이제 프론트컨트롤러는 어댑터를 통해서 컨트롤러 호출함

 

* 프론트 컨트롤러의 핵심 로직 중 컨트롤러 호출하고, paramMap만들어서 전달하고, 컨트롤러 처리 결과 받는 로직어댑터가함 따라서 어댑터 내부에서 컨트롤러 처리결과 다 받은 다음에 일관된 값으로 return함 

 

2. ControllerV3HandlerAdapter

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){

		//frontController에서 하던 부분 
		ControllerV3 controller = (ControllerV3) handler; 
        Map<String,String> paramMap = createMap(request);
        ModelView mv = controller.process(paramMap);
        return mv;
}
}

 

2.1 FrontControllerServletV5

 

@WebServlet(name = "frontControllerServletV5", urlPatterns = "/frontcontroller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {


	private final Map<String,Object> handlerMappingMap = new HashMap<>();
    private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();
    
    public FrontControllerServletV5(){
    	initMapingMap();
        initAdapters();
    }
    
    private void initMapingMap(){
    	//이전 url과 컨트롤러 맵핑하던 것과 정확하게 동일 단, Object로 저장
    	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 initAdapters() {
    //어댑터들 저장 
 handlerAdapters.add(new ControllerV3HandlerAdapter());
 }
 
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse 
response)
 throws ServletException, IOException{
 
 	Object handler = getHandler(request); // 걍 url맵핑된 것에서 컨트롤러 찾음
    if (handler == null) {
	 response.setStatus(HttpServletResponse.SC_NOT_FOUND);
	 return;
 	}
    MyHandlerAdapter adapter = getHandlerAdapter(handler); //처리할 어댑터 있는지 검사해서 return
    ModelView mv = adapter.handle(request,response,handler);
    
    MyView view = viewResolver(mv.getViewName());
    view.render(mv.getModel(),request,resposne);
    
 
 }
 private Object getHandler(HttpServeltRequest request){
 		return handlerMapingMap.get(request.getRequestURI());
 }
 
 private MyHandlerAdapter getHandlerAdapter(Object handler){
 	for(MyHandlerAdapter adp : handlerAdapters){
    	if(adp.supports(handler){
        	return adapter;
        }
    }
    throw new IllegalArgumentsException("어답터 없어유");
 
 }
 
 
 
 
 
}

- 컨트롤러 -> 핸들러 

  이전에는 컨트롤러를 직접 매핑해서 사용함, 하지만 이제는 어댑터를 사용하기때문에, 컨트롤러 뿐 아니라 어댑터가 지원하기만 하면,

어떤 것이라도 URL에 맵핑해서 사용할 수 있음 그래서 핸들러로 이름을 변경 

 

- 핸들러 맵핑정보와, 핸들러 어댑터를 등록 

  url맵핑과, 핸들러들을 처리할 수 있는 어댑터들을 등록 

  

- 어댑터 호출

  어댑터의 handle 메서드를 통해 어댑터가 호출됨 -> 어댑터는 컨트롤러 호출하고, 결과 반환 

 

 

3. ControllerV4를 처리할 수 있는 어댑터 추가 

 

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){


			ControllerV4 controller = (ControllerV4) handler;
            Map<String,String> paramMap = createParamMap(request);
            Map<String,Object> model = new HashMap<>();
            
            String viewName = controller.process(paramMap,model);
            
            ModelView = new ModelView(viewName);
            mv.setModel(model);
            
            return mv;

}
}

- 이 어댑터를 어댑터 리스트에 추가하면, ControllerV4를 구현한 컨트롤러를 처리할 수 있다.

- 어댑터는 컨트롤러 처리결과를 다시 리턴값을 맞추기 위해 ModelView로 변환해서 리턴한다.

- 어댑터 패턴의 강점은 바로 여기임!

 

 

참고자료: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원

www.inflearn.com