ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링 MVC 1] 4. MVC 프레임워크 만들기
    INFLEARN/스프링 MVC 1편 2021. 10. 23. 00:25

    4. MVC 프레임워크 만들기

     

    [강의 정리] 김영한님 :: 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

     

     

    1. 프론트 컨트롤러 패턴 소개

     

    # 프론트 컨트롤러 도입 전/후

    • 프론트 컨트롤러 도입 전 : 클라이언트에서 요청이 들어왔을 때 controller를 바로 호출 (공통 로직은 각각의 Controller에서 알아서 처리) 
    • 프론트 컨트롤러 도입 후 : 클라이언트에서 요청이 들어왔을 때 먼저 Front Controller (공통 로직을 처리)에서 받은 후 Front Controller에서 Controller를 호출

    # FrontController 패턴 특징

    • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
    • 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출
    • 공통 로직을 프론트 컨트롤러에서 처리
    • 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨

    # 스프링 웹 MVC와 프론트 컨트롤러

    • 스프링 웹 MVC의 DispatcherServlet이 FrontController 패턴으로 구현되어 있음

     

     

    2. 프론트 컨트롤러 도입 - v1

     

    # v1 구조

    출처 : 김영한님 강의 자료

     

    # ControllerV1

    • 서블릿과 비슷한 모양의 컨트롤러 인터페이스 도입
    • 프론트 컨트롤러는 이 인터페이스를 호출해서 구현과 관계없이 로직의 일관성 가져가기

     

    # 회원 등록 컨트롤러 / 회원 저장 컨트롤러 / 회원 목록 컨트롤러 

    • 내부 로직은 기존 서블릿과 거의 같음

     

    # FrontControllerServletV1 - 프론트 컨트롤러

    • urlPatterns
      • /front-controller/v1을 포함한 모든 하위 요청을 서블릿에서 받음

    • controlllerMap
      • key : 맵핑 URL (/front-controller/v1/members/new-form)
      • value : 호출될 컨트롤러 (new MemberFormControllerV1())

    • service()
      • requestURI 조회 : String requestURI = request.getRequestURI();
      • controllerMap에서 찾기 : ControllerV1 controller = controllerMap.get(requestURI);
      • 컨트롤러 없으면 404(SC_NOT_FOUND) 상태 코드 반환 : response.setStatus(HttpServletResponse.SC_NOT_FOUND);
      • 컨트롤러 찾으면 호출하고 해당 컨트롤러 실행: controller.process(request, response);
    • JSP
      • 이전 MVC에서 사용했던 것 그대로 사용

     

     

    3. View 분리 - v2

     

    # 중복 부분

    • 모든 컨트롤러에서 뷰로 이동하는 부분에 중복 존재
    • 별도로 뷰를 처리하는 객체 생성

    • 이 부분을 MyView라는 클래스로 따로 뺌

     

    # v2 구조

    • controller에서 JSP를 직접 forward 하지 않음

    출처 : 김영한님 강의 자료

    # ControllerV2

    • 앞서 만든 V1과 동일하지만 return 값이 MyView
    • 컨트롤러에서 MyView를 반환

     

    # 회원 등록 컨트롤러 / 회원 저장 컨트롤러 / 회원 목록 컨트롤러 

    • 기존에 각각 컨트롤러에서 호출하던 복잡한 dispatcher.forward() 부분을 호출하지 않아도 됨
    • 단순히 MyView 객체를 생성하고 뷰 이름을 넣고 반환

    # 프론트 컨트롤러

    • 앞서 ControllerV2의 반환 타입이 MyView이므로, 프론트 컨트롤러도 해당 타입으로 받음
    • view.render()를 호출하면 forward 로직을 수행해서 JSP가 실행됨
    • 프론트 컨트롤러의 도입으로 MyView 객체의 render() 호출 영역은 일관된 처리 가능 
    • 각각의 컨트롤러는 MyView 객체를 생성만 해서 반환하면 됨

     

     

    4. Model 추가 - v3

     

    # v3 구조

    출처 : 김영한님 강의 자료

    # 서블릿 종속성 제거

    • 컨트롤러 입장에서 HttpServletRequest, HttpServletResponse 꼭 필요하지 않음
    • 요청 파라미터 정보는 Map을 사용 (컨트롤러 서블릿 기술 몰라도 됨)
    • request 객체를 Model로 사용하는 대신 별도의 Model 객체 생성 후 반환 (기존의 request 역할)

     

    # 뷰 이름 중복 제거

    • 컨트롤러에서 지정하는 뷰 이름에 중복이 있음 ("/WEB-INF/views/...")
    • 컨트롤러는 뷰의 논리 이름(ex. new-form)을 반환하고, 실제 물리 위치(/WEB-INF/views/new-form.jsp) 는 프론트 컨트롤러에서 처리

     

    # ModelView

    • 기존 방식 : 컨트롤러에서 HttpServletRequest 사용하고, Model은 request.setAttribute() 통해 저장하고 뷰에 전달
    • 서블릿 종속성 제거 : Model 직접 생성, View 이름까지 전달하는 객체 생성

     

    # ControllerV3

    • 해당 컨트롤러에서는 서블릿 기술 사용하지 않음
    • 구현이 단순해지고, 테스트에 용이
    • 기존에 HttpServletRequest가 제공하는 파라미터는 프론트 컨트롤러가 paramMap에 담아서 호출
    • 응답 결과로 ModelView 객체 반환 (뷰 이름과 뷰에 전달할 Model 데이터)

     

    # MemberFormControllerV3 - 회원 등록 폼

    • ModelView를 생성할 때 논리 이름 지정하고 반환 (물리 이름은 프론트 컨트롤러에서 처리)

     

    # MemberSaveControllerV3 - 회원 저장

    • 파라미터 정보는 map에 담겨있고, map에서 요청 파라미터 조회

    • member 객체 모델에 담고 반환

     

    # FrontControllerServletV3

    • 뷰 리졸버
      • MyView view = viewResolver(viewName);
      • 컨트롤러가 반환한 논리 뷰 이름을 실제 물리 뷰 경로로 변경
      • 실제 물리 경로가 있는 MyView 객체 반환
    • 뷰 객체 통해서 HTML 화면 렌더링
      • view.render(mv.getModel(), request, response);
      • JSP는 reqeust.getAttribute()로 데이터를 조회하므로, 그
      • 모델이 있는 데이터를 request attribute로 변경
      • JSP로 포워딩 해서 JSP 렌더링

     

     

    5. 단순하고 실용적인 컨트롤러 - v4

     

    # V4 구조

    • 기존 구조는V3와 같고, 대신 컨트롤러가 ModelView를 반환하지 않고 ViewName만 반환 (3)
    • 항상 ModelView 객체를 생성하고 반환하는 부분 번거로움

    출처 : 김영한님 강의 자료

    # ControllerV4

    • ModelView 없음
    • model 객체는 파라미터로 전달되고, 뷰의 이름만 반환

    # MemberFormControllerV4 / MemberSaveControllerV4 / MemberListControllerV4

    • 논리 이름만 반환하면 된다 (ex. retrun "new-form");
    • 모델이 파라미터로 전달되므로, 직접 생성하지 않아도 됨

    # FrontControllerServletV4

    • V3 버젼과 거의 동일, 추가 된 부분 모델 객체 전달 
    • 모델 객체를 프론트 컨트롤러에서 생성해서 넘겨줌
    • 컨트롤러에서 모델 객체에 값을 담으면 여기에 그대로 담겨있음 

    • 컨트롤러가 직접 뷰의 논리 이름 반환 (이 값으로 물리 뷰 찾음)

     

     

    6. 유연한 컨트롤러1 - v5

     

    # 어댑터 패턴

    • 기존의 프론트 컨트롤러는 한가지 방식의 컨트롤러 인터페이스만 사용 가능 (완전히 다른 컨트롤러에 대해서 호환 불가능)
    • 프론트 컨트롤러가 다양한 방식의 컨트롤러를 처리할 수 있도록 

    # v5 구조

    • 핸들러 어댑터
      • 중간에 어댑터 역할을 하는 어댑터
      • 다양한 종류의 컨트롤러 호출 가능
    • 핸들러 
      • 컨트롤러의 이름을 더 넓은 범위인 핸들러로 변경

    출처 : 김영한님 강의 자료

     

    # MyHandlerAdapter

    • boolean supports(Object Handler); 
      • handler는 컨트롤러를 말함
      • 어댑터가 해당 컨트롤러를 처리할 수 있는지 판단하는 메서드
    • ModelView handle(HttpServletRequest request, HttpServletResponse response, Object Handler) throws ServletException, IOException; 
      • 어댑터는 실제 컨트롤러 호출, 결과로 ModelView 반환
      • 이전에는 프론트 컨트롤러가 실제 컨트롤러를 호출했다면, 이제는 어댑터를 통해 컨트롤러를 호출

     

    # ControllerV3HandlerAdapter

    • 해당 컨트롤러(V3)를 처리할 수 있는 어댑터인지 체크

    • handler를 컨트롤러 V3로 변환하고 호출 (V3는 ModelView 반환하므로 똑같이)

     

    # FrontControllerServletV5

    • 컨트롤러(Controller) -> 핸들러 (Handler)
      • 이전에는 컨트롤러를 직접 매핑해서 사용
      • 어댑터가 지원하면 어떤 것이라도 URL 매핑해서 사용 가능 
    • 매핑 정보
      • 매핑 정보의 값을 아무 값이나 받을 수 있도록 Object로 변경
      • private final Map<String, Object> handlerMappingMap = new HashMap<>();
    • 핸들러 매핑
      • Object handler = getHandler(request);
      • 핸들러 매핑 정보에서 URL에 매핑 핸들로(컨트롤러) 객체 반환

    • 핸들러를 처리할 수 있는 어댑터 조회
      • MyHandlerAdapter adapter = getHandlerAdapter(handler);
      • handler를 처리할 수 있는 어댑터를 support를 통해서 찾음
      • ControllerV3HandlerAdapter 객체가 반환
    • 어댑터 호출
      • ModelView mv = adapter.handle(request, response, handler);
      • 실제 어댑터 호출
      • 어댑터는 handler(컨트롤러) 호출하고 어댑터에 맞춰 반환

     

    7. 유연한 컨트롤러2 - v5

     

    # FrontControllerServletV5 (+ControllerV4 추가)

    • handlerMappingMap에 ControllerV4 사용하는 컨트롤러 추가
    • 처리할 수 있는 어댑터 ControllerV4HandlerAdapter 추가

    # ControllerV4HandlerAdapter

    • 앞서 동일하게 supports 메서드로 처리 가능한 handler인지 체크
      • return (handler instanceof ControllerV4);
    • paramMap, model을 만들어서 컨트롤러 호출하고 viewName을 반환
    • 어댑터 변환 (String -> modelView)
      • ControllerV4는 뷰의 이름을 반환, 어댑터는 ModelView를 반환
      • 그래서 어댑터는 ControllerV4가 반환한 뷰의 이름을 ModelView로 형식 맞춰서 반환

    댓글

Programming Diary