
v1, v2 를 제외한 v3 이해가 어려워 해당 부문만 자세하게 정리했다.

앞서 기존에 했었던 예제에서는 출입문이 다 제각기 있어서 각 페이지 마다 각각의 서블릿이 있어야 했다. 따라서 공통된 작업에 낭비되는 코드들이 많았다. 가령 일일히 path를 지정해준다던가

URL 매핑을 해서 클라이언트에서 요청이 오면, WAS 서버가 처음 요청이 오는 곳이 Servlet인데 그 역할을 이미 프론트 컨트롤러가 대신 해주기 때문에 나머지 컨트롤러들은 HttpRequest, Httpresponse등을 상속 받고 사용한다 던가 할 필요가 사라졌다.
스프링 웹MVC와 프론트 컨트롤러
스프링 웹 MVC의 핵심도 Front Controller
스프링 웹 MVC 자체가 DispatcherServlet이 FrontController 패턴으로 구현되어 있다.
이번 파트에서는 구조적인 부분만 프론트 컨트롤러를 도입한다
Front Controller는 수문장 같은 역할을 담당 시켜 매핑정보를 담아 놓는다
가령/a로 호출이 오면 컨트롤러 A가 호출되고,/b가 호출되면 컨트롤러 B가 호출되게 한다.
![[Pasted image 20240222131928.png]]
이제 컨트롤러는 요청이 오면 이전에 했던 예제 그대로 JSP를 forward(서버 내부에서 일어나는 연결 요청) 하고 JSP에서 HTML 응답을 하게 하면 된다.
예제 도입에 앞서 컨트롤러를 다형성을 적용해 인터페이스로 만들 것이다.
이 인터페이스를 토대로 회원 저장, 목록,회원 폼 컨트롤러를 모두 구현할 것이다.
번외
인터페이스의 메서드를 구현할 때엔 접근제어자를 명시할 필요가 없다. 인터페이스를 구현하는 클래스 에서는 인터페이스에 정의된 메서드를 모두 public으로 취급 한다.
모든 컨트롤러에서 뷰로 이동하는 부분에 중복이 있고 깔끔하지 않았다
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDisPatcher(viewPath);
dispatcher.forward(request,response);
이 부분을 전담으로 처리하는 view라는 객체를 별도로 만들어줄 것이다.

이제는 컨트롤러에서 JSP로 직접 포워드 해줬지만, 이제는 그러지 않고
MyView라는 객체를 만들어서 반환 해줄 것이다. 이후에 ForntController가MyView를 호출 한다.
더 복잡해지는 것 같지만? 코드를 살펴보자
컨트롤러 입장에서 HttpServletRequest, HttpServletResponse는 꼭 필요할까?
요청 파라미터 정보는 Map으로 대신 넘기도록 하면 지금 구조에서는 컨트롤러가 서블릿 기술을 몰라도 동작할 수 있다. 그리고 request 객체를 Model로 사용하는 대신 별도의 Model 객체를 만들어 반환한다. 우리가 구현하는 컨트롤러가 서블릿 기술을 전혀 사용하지 않도록 변경 하면, 코드도 매우 단순해지고 테스트코드 작성이 쉬워진다.
컨트롤러에서 지정하는 뷰 이름에 중복이 있는 것을 확인할 수 있다. 컨트롤러는 뷰의 논리 이름을 반환하고, 실제 물리 위치의 이른은 프론트 컨트롤러에서 처리하도록 단순화 하자. 이렇게 해두면 향후 뷰의 폴더 위치가 함께 이동해도 프론트 컨트롤러만 고쳐도 된다.
/WEB-INF/views/new-form.jsp ->new-form /WEB-INF/views/save-result.jsp-> save-result/WEB-INF/views/members.jsp ->membersprefix suffix를 만들겠다는 뜻

기존 V2에서는 FrontController <- Controller 에서 Model을 반환 했지만 v3에서는 ModelView를 반환한다. 뷰에서 이제 논리 이름만 반환할 것인데 이 논리 이름을 물리 이름으로 바꾸는 게 viewResolver이고 MyView로 전환하고 Render를 호출한다.
ModelView
지금까지 컨트롤러에서 서블릿에 종속적인 HttpServletRequest를 사용했다. 그리고 Model 도
request.setAttribute()를 통해 데이터를 저장하고 뷰에 전달했다.
서블릿의 종속성을 제거하기 위해 Model을 직접 만들고, 추가로 View이름까지 전달하는 객체를 만들어 본다. ( 따라서 이번 v3는 HttpServletRequest를 사용할 수 없다. 따라서request.setAttribute()를 호출할 수도 없다.
따라서Model이 별도로 필요하다
컨트롤러가 반환 할 모델 뷰, 이름에서도 알수 있듯 뷰에 대한 vieName과 setAttribute를 대신할 ModelView Map 객체가 있다.
@Data
public class ModelView{
private String viewName;
private Map<String, Object> model= new HasMap<>();
public ModelvView(String vieName){
this.viewName= vieName;
}
}
각기 뷰에 해당하는 컨트롤러들을 구현체의 토대가될 인터페이스. 아래에서 볼수 있듯 HttpServlet reuqest, response가 없는 모습을 볼수 있다. v3에서 진행할 자바 맵 객체에 종속적이지 서블릿에 종속적이지 않은 모습을 볼수 있다.
public interface ControllerV3{
Modelview process(Map<String, String> pramMap){
}
}
이제 컨트롤러들을 구현 해보자
폼 컨트롤러
public class MemberFormControllerV3 implements ControllerV3{
@Override
public ModelView process(Map<String, String> paramMap){
return new (ModelView("new-form"))
}
}
컨트롤러 인터페이스 안에 있는 모델뷰 객체를 구현한 예시. 모델뷰로 전달하는 파라메터는 해당 뷰에대한 ㅍiewName 정보이다.
세이브 컨트롤러
public class MemberSaveControllerV3 implements ControllerV3{
private MemberRepository memberRepository =
memberRepository.getInstance();
@Override
protected ModelView process(Map<String, String> paramMap){
String username= paramMap.get("username");
int age= Integer.parseInt(paramMap.get("age"));
Member member = new Member(username, age);
memberRepository.save(member);
ModelView mv = new ModelView("save-result");
mv.getModel().put("member",member);
return mv;
}
}
getParameter가 아닌 맵에서 꺼내는 모습. HttpServlet대신 쓰게 된 Map 에서 get 해주고 파라메터를 FrontController에 넘겨준다.
paramMap은 클래스 인터페이스에서 받아와서 쓰는 말 그대로 파라메터 맵이다.
model 은 modelView 에 넘길 String Object 인 모델 대신 쓸 맵이다.
.get().get() 하고 .put() 하기 멤버 리스트 컨트롤러
public class MemberListControllerV3 implements ControllerV3{
MemberRepository memberRepository = memberRepository.getInstance();
@Overrride
protected class Service(Map<String,String> paramMap){
List<Member> members= memberRepository.findAll();
ModelView mv=new Modelview("members");
mv.getModel().put(members);
return mv;
}
}
findAll() 로 모든 멤버 담기getModel() 로 모델 불러온 후 .put(members)mv 반환하기ModelView를 생성할 때 viewName 도 반드시 넣어준다
@WebServlet(name="FrontControllerV3", urlNames="/fornt-controller/v3/*")
public class FrontControllerServletV3 extends HttpServlet{
private Map<String, ControllerV3> controllerMap = new HasMap<>();
public FrontControllerServletV3(){
controllerMap.put("/front-controller/v3/members/new-form",
new MemberFromControllerV3());
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, IOEException{
String requestURI = reuqest.getRequestURI();
ControllerV3 controller= controller.get(requestURI);
if(controller ==null){
response.setStatuse(HttpServletResponse.SC_NOT_FOUN);
return; }
My<String, String> paramMap = createParamMap(request);
Modelview mv = controller.process(paramMap);
String viewName= mv.getViewName();
Myview view = viewResolver(viewName);
view.render(my.getModel(),request,response);
}
private Map<String, String>createParamMap(HttpServlet request){
Map<String,String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(paraName -> paramMap.put(paramName,
result.getParameter(paramName)));
return paramMap;
}}}
FrontControllerV3() 클래스으, service() 로 라우팅service() 는 HttpServletRequest 객체를 통해 클라이언트의 요청정보를 받아온다.getRequestURI()로 현재 해당하는 컨트롤러를 찾는다.controllerMap 에 없으면 404 오류를 반환한다getParameter()로 파라미터를 추출 후 맵으로 변환하기 위하여 getParameterNames, asIterater 로 데이터를 전부 꺼낸 후 paramMap에 put()해준다.ModelView 를 반환한다Modelvie 객체에 담아 반환한다ModelView 객체에서 뷰 이름을 가져온다viewResolver로 실제 JSP 파일 경로로 뷰 이름을 변환한다.render 보낸다.어떻게 render 되는지?
마이뷰에서 오버로드된 render 함수 중 v3에 쓰이는 render 함수는 이러하다
modelToRequestAttribute 주어진 모델의 데이터를 request객체의 속성(attribute)로 변환한다RequestDisPatcher JSP파일을 포워딩 하게 위해 생성한다. 이 객체는 Servlet 컨테이너에게 요청을 전달하는 역할을 한다RequestDisPatcher를 사용하여 지정된 JSP(viewPath) 로 요청을 포워딩한다. 이때 현재의 HttpServletRequest 와 HttpServletResponse가 함께 전달된다. 이를 통해 JSP는 서블릿과 동일한 컨텍스트 안에서 실행되며, JSP 파일에서는 request와 response 객체를 사용하여 클라이언트에게 응답을 생성할 수 있다.HttpServletResponse를 통해 클라이언트에게 응답으로 보낸다.public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
modelToRequestAttribute(model, request);
RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
requestDispatcher.forward(request, response);
}