MVC 프레임워크 만들기
프론트 컨트롤러 패턴 소개

공통 처리를 하기 위한 로직을 모든 컨트롤러에 포함시켜서 각각을 호출해주어야 한다.

공통 로직을 수행하는 프론트 컨트롤러는 클라이언트와 세부 컨트롤러를 중계하는 역할을 한다.
어떤 컨트롤러든 프론트 컨트롤러를 통해 호출된다.
FrontController 패턴 특징
- 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
- 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출
- 입구를 하나로! 공통 처리 가능
- 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨 (웹 요청을 직접 받을 필요 x)
스프링 웹 MVC와 프론트 컨트롤러
스프링 웹 MVC의 핵심도 바로 FrontController
스프링 웹 MVC의 *DispatcherServlet*이 FrontController 패턴으로 구현되어 있음
프론트 컨트롤러 도입 - v1

컨트롤러를 인터페이스로 정의 (다형성)
-> 매핑 정보를 참조하여 컨트롤러를 호출할 때 일관성을 부여
@WebServlet(name = "frontControllerV1", urlPatterns = "/front-controller/v1/*"
/front-controller/v1/ 이하의 어떤 url이 호출되더라도 해당 서블릿이 우선 매핑된다.
예) /front-controller/v1 , /front-controller/v1/a , /front-controller/v1/a/b
> http://localhost:8080/front-controller/v1/members/new-form
request.getRequestURI() : "/front-controller/v1/members/new-form" 반환
controllerMap
key: 매핑 URL
value: 호출될 컨트롤러
service()
먼저 requestURI 를 조회해서 실제 호출할 컨트롤러를 controllerMap 에서 찾는다.
만약 없다면 404(SC_NOT_FOUND) 상태 코드를 반환한다.
컨트롤러를 찾고 controller.process(request, response); 을 호출해서 해당 컨트롤러를 실행한다.
JSP (뷰)
JSP는 이전 MVC에서 사용했던 것을 그대로 사용한다
참고:
구조적 개선 -> 세부적 개선 순으로 리팩토링!
(front-controller) (dispatcher.forward(viewPath) 중복)
개선할 점
뷰로 이동하는 중복 코드
View 분리 - V2
컨트롤러에서 뷰로 이동(forward)하는 부분의 중복
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
=> 별도로 뷰를 처리하는 객체 생성

각 컨트롤러에서 호출해야 할 jsp 경로에 대한 view 객체를 반환한다.
만약 jsp 뿐만 아니라 다양한 뷰를 생성해야 한다면 MyView 객체도 인터페이스로 정의하여 다형성을 활용할 수 있다.
개선할 점
스펙상 주고 받는 실제 사용되지 않는 서블릿 파라미터들 (request, response)
request: 요청 파라미터만 필요
response: 모든 비지니스 로직 실행 후 응답을 위한 뷰(jsp) 반환 시에만 필요
Model 추가 - V3
서블릿 종속성 제거
컨트롤러 입장에서 HttpServletRequest, HttpServletResponse이 꼭 필요할까?
요청 파라미터 정보 -> Model 객체, 자바의 Map으로 대신 넘김
컨트롤러가 서블릿 기술을 몰라도 동작할 수 있도록 리팩토링 (테스트 코드 작성 용이)
뷰 이름 중복 제거
컨트롤러에서 지정하는 뷰 이름의 중복
컨트롤러는 뷰의 논리 이름을 반환하고, 실제 물리 위치의 이름은 프론트 컨트롤러에서 처리하도록 단순화 하자.
이렇게 해두면 향후 뷰의 폴더 위치가 함께 이동해도 프론트 컨트롤러만 고치면 된다. 변경 지점 단일화
/WEB-INF/views/new-form.jsp -> new-form
/WEB-INF/views/save-result.jsp -> save-result
/WEB-INF/views/members.jsp -> members

viewResolver: 논리 이름 -> 물리 이름 변환
HttpServletRequest가 제공하는 파라미터는 프론트 컨트롤러가 paramMap에 담아서 각 컨트롤러 호출
각 컨트롤러가 뷰 이름과 paramMap에서 뷰를 렌더링 할 때 필요한 Model 데이터만을 포함하는 ModelView 객체 반환
ModelView 를 생성할 때 view의 논리 이름을 지정하고, 실제 물리 이름은 프론트 컨트롤러에서 처리한다. (viewResolver)
실제 물리 이름으로 생성한 뷰 객체를 통해 HTML을 렌더링한다.
뷰를 렌더링하기 위해서는 Model 객체에 포함된 데이터들을 request.setAttribute()로 추가해야 한다.
(jsp가 request의 attribute를 참조하여 뷰 생성)