인프런 'Spring MVC 기본_1편' 공부 내용 정리 

 

Http request message 

  • Start Line(Http message, URL, query string, method)
  • Header
  • Body

sevlet이란?

개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 HTTP Request Message를 파싱한다. 

파싱한 결과를 HttpServletRequest 객체에 담아서 제공한다. 

 

jsp란?

템플릿 엔진 중 하나. 

템플릿 엔진은 HTML 문서에서 필요한 곳만 동적으로 변경할 수 있게 해준다.

 

jsp의 문제점

servlet으로 화면과 비즈니스 로직까지 구현하면 너무 복잡해짐. 자바 코드에 HTML문 일일이 작성해야함

-> jsp에 화면을 기반으로 작성하고, 동적인 부분은 자바 코드로 대체

-> jsp가 혼자서 많은 일들을 처리하게 되므로 유지보수가 쉽지 않다 

-> servlet 기능과 jsp기능을 분리해서 코드를 작성하면 된다

 

MVC 패턴 

sevlet은 controller로, jsp는 view로 역할을 나눈 것을 말한다

 

- Controller: HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직 실행, 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.

- Model: 뷰에 출력할 데이터를 담아둔다. 이러면 view는 조회해야할 데이터에 대해 몰라도 된다.

- View: 모델에 담겨있는 데이터로 화면을 그리는 역할이다. 

 

FrontController : 주로 서블릿 컨테이너의 제일 앞단에서 서버에 들어오는 클라이언트의 모든 요청을 받아 처리하는 컨트롤러

클라이언트 요청을 한 곳에서 처리 후 필요한 controller를 호출하는 방식. 역할,추상화,다형성의 느낌

스프링MVC의 핵심은 FrontController!

 

dispatch: 보내다 

Dispatcher Servlet : Http 프로토콜로 들어오는 모든 요청을 받아 적합한 컨트롤러에 위임하는 프론트 컨트롤러이다.

 

 

FrontControllerV1

전체 구조에서 FrontController를 만든 후 요청에 따른 컨트롤러를 호출하는 정도의 역할만한다.

 

request에서 requestURI를 가져온 후, 해당 키에 따른 객체들을 가져온다 

객체 validation을 해준 후, 객체가 null이 아니라면 해당 객체에서 process를 실행하면 된다 

문제: 각 구현 컨트롤러에서 process 메서드 내용이 중복되는 것이 있다. dispatcher~~~

 

getDispatcher란?

 

FrontControllerV2

v1에서 문제였던 중복코드를 없앤 버전. render(disaptcher, forward) 부분을 MyView라는 클래스가 수행하게 한 후 

반환한다. 구현 컨트롤러는 viewName만 My View 객체에 넣어서 보내주면 된다.

반환된 My View 객체로 render를 수행하면 된다.

 

구현 controller에서 MyView를 반환하고, 이를 render해서 jsp에 전달한다 

MyView 클래스에서는 viewPath를 받아서 dispatcher에 처리한다 

ControllverV2 인터페이스에서는 String이 아닌 MyView 타입을 반환한다 

구현 클래스의 process 메서드가 MyView를 반환하므로, 반환되는 view로 render를 하면된다

 

 

view.render()를 호출하면 forward 로직을 수행해서 jsp가 실행된다 

RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);

 

MyView 클래스에는 process, render 메서드가 있는거네?!

 

문제

  1. Servlet에 대한 의존성 제거할 수 있지 않나?
  2. 뷰 이름 중복 제거할 수 있지 않나?(물리 경로가 중복된다)

 

FrontControllerV3

 

servlet에 대한 의존성 제거

Model을 직접 만들고, View이름까지 전달하는 객체를 만들면 된다. (구현 컨트롤러)

ModelView 클래스를 만들고, viewName과 model(뷰를 렌더링할 때 필요함)을 필드로 가진다. 

 

뷰 이름 중복 제거(ViewResolver method), 논리 주소만 전달 

 

 

구현 컨트롤러 process 메서드는 V2에서 MyView를 반환했다면, V3에서는 ModelView를 반환한다.

파라미터도 request/response가 아닌 Map<String, String> paraMap을 받는다 

 

—> 구현 컨트롤러는 단순해졌지만, FrontController에서 처리하는게 많아졌다. 

paraMap 처리, ModelView 처리

 

문제: ModelView 객체를 생성하고 반환하는 것이 번거롭다. 

private Map<String, ControllerV3> controllerMap = new HashMap<>();
    public FrontControllerServletV3() {
        controllerMap.put("/front-controller/v3/members/new-form", new MemberFormControllerV3());
        controllerMap.put("/front-controller/v3/members/save", new MemberSaveControllerV3());
        controllerMap.put("/front-controller/v3/members", new MemberListControllerV3());
}
    
@Override 
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String requestURI = request.getRequestURI();
        ControllerV3 controller = controllerMap.get(requestURI);
        
        if (controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
		return; 
} 

        Map<String, String> paramMap = createParamMap(request);
        ModelView mv = controller.process(paramMap);
        String viewName = mv.getViewName();
        MyView view = viewResolver(viewName);
        view.render(mv.getModel(), request, response);
    }

    private 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;
      }
      
	private MyView viewResolver(String viewName) {
    	return new MyView("/WEB-INF/views/" + viewName + ".jsp");
 } 
}

 

FrontControllerV4 

구현 컨트롤러에 paraMap(request 정보를 가지고 있음), model을 생성한 후 전달해준다 

그러면 model에 객체를 담고, view의 논리 이름을 반환하면 FronController에서 이걸 가지고 물리 주소를 찾고, render 하면 된다. 

 

기존 구조에서 모델을 파라미터로 넘기고, 뷰의 논리 이름을 반환한다는 작은 아이디어를 적용했을 뿐이다.

 

FrontControllerV5 

다양한 타입의 컨트롤러를 사용하기 위해 어댑터패턴을 적용한다.

 

순서 

  1. FrontController에서 핸들러를 조회한다
  2. 핸들러를 처리할 수 있는 핸들러 어댑터를  핸들러 어댑터 목록(interface)에서 찾는다
  3. 해당 어댑터가 있다면, 핸들러어댑터에게 메시지를 보낸다
  4. 컨트롤러 어댑터에서 해당 컨트롤러로 다시 메시지를 전달한 후 연산을 수행한다 
  5. 나머지는 v3에서 처리하는 과정과 동일하다

 

더 자세하게 이해하기

-MyHandlerAdapter : interface이고, supports, handle 시그니처 있음

-ControllerV3HandlerAdapter : MyHandlerAdapter 구현 클래스 이고, supports()에 ControllerV3, ModelView handle(~~ paraMap<>()) 있음 

 

requestURI와 InitHandlerMappingMap을 이용해 해당 핸들러 있는지 확인

handler가 있으면 handlerAdapters()에서 해당 HandlerAdapter가 있는지 확인

해당 handlerAdapter가 있으면 그 어뎁터로 handle()메시지 보낸다 

+ Recent posts