관심쟁이 영호

[#8 Spring Boot 정주행] 유연한 컨트롤러 만들기 ㅣ Handler, Adapter, 다형성 본문

Bank-End/Spring Boot

[#8 Spring Boot 정주행] 유연한 컨트롤러 만들기 ㅣ Handler, Adapter, 다형성

관심쟁이 영호 2021. 9. 16. 17:43
반응형

지금까지 여러 종류의 모양을 반환하는 컨트롤러를 만들어보았다.

 

path부터 modelAndView 까지.. 계속해서 발전해왔다.

 

 

[#6-1 Spring Boot 정주행] MVC 프레임워크를 직접 만들어보자

이전에 포스팅한 글에서 서블릿 dispatcher를 통해서 JSP를 반환하는 것까지 성공했다! 하지만 문제가 있었다. 이번 포스팅에서는 해당 문제들을 짚어보고 하나씩 해결해보면서 MVC 프레임워크를 만

bestkingit.tistory.com

 

 

[#6-2 Spring Boot 정주행] View Path를 없애보자! ㅣ MVC 프레임워크 만들기

이전 포스팅에서 FrontController를 통해서 공통 처리를 가능하게 하였다. 하지만 View의 Path를 설정하는 부분과, Servlet에서 View & 또 다른 Servlet으로 넘겨주는 함수가 모든 컨트롤러에 적용된다는 것

bestkingit.tistory.com

 

 

[#7 SpringBoot 정주행] ViewResolver를 만들어보자!

[#6-2 Spring Boot 정주행] View Path를 없애보자! ㅣ MVC 프레임워크 만들기 이전 포스팅에서 FrontController를 통해서 공통 처리를 가능하게 하였다. 하지만 View의 Path를 설정하는 부분과, Servlet에서 View..

bestkingit.tistory.com

 

근데.. 개발자의 입장에서 modelAndView 1개만 반환해야 한다면 답답할 수 있다.

model과 view가 필요가 없는데, modelAndView를 반환해야하기 때문이다.

 

그래서!!

개발자가 무엇을 반환하든 상관없는 모양으로 만들어보자!

 

먼저 뒷내용을 이해하려면 객체의 "다형성"에 관해서 잘 알아야한다.

혹시나 모른다면! 다음 글을 보도록 하자.

 

 

[JAVA]추상클래스와 추상메서드에 대해서! ㅣ Abstract

오늘은 추상클래스와 추상메서드에 대해서 공부를 해볼 계획이다. 추상클래스? 추상클래스는 완성하지않은 메서드를 가지고 있는 클래스를 말한다! 한마디로, 몸통부분인 "{}"가 없는 것을 말한

bestkingit.tistory.com

 

 

[JAVA] 인터페이스 ㅣ Interface

오늘은 인터페이스에 관해서 공부를 할 예정이다. (출간된 "자바의 정석" 책을 참고했습니다.) 인터페이스란? - 추상 메서드의 집합이다. (추상 메서드 : 구현을 하지않은 메서드) - 모든 멤버가 pu

bestkingit.tistory.com

 


목차

  • 어댑터와 핸들러
  • 어댑터 만들기
  • 핸들러 만들기
  • 마치며

어댑터와 핸들러

어댑터와 핸들러를 이해하기 전에, 위에서 이야기했던 내용을 계속해서 해보자.

 

개발자들은 반환 값을 상관하지 않고 여러 가지 방식 중에서 자기가 원하는 것을 반환하고 싶다.

(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편"을 참고하여 작성되었습니다.

300x250
Comments