참고
FrontController - V3
NOTE
Model 추가, ViewModel 추가
HttpServletRequest/HttpServletResp onse Servlet 종속성 코드 제거
Servlet 종속성 제거를 위해 Model를 만들고 View 이름까지 전달하는 객체 ModelView를 만들어보자!
•
Controller는 HttpServletRequest, HttpServletResponse는 필요하지 않다
◦
Controller는 요청 파라미터 정보만 필요하기에 Map<String, String> 객체만 넘겨주면 된다
◦
request가 없기에 별도의 Model객체를 만들어서 반환하는 구조로 가지도록 해야한다.
•
Controller는 파라미터 데이터를 이용하여 비즈니스 로직을 수행하고 결과값을 ModelView 객체에 Map<String, String>형식으로 View 경로와 함께 저장한 후 반환한다.
V3 - FrontController
NOTE
@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")
public class FrontControllerServletV3 extends HttpServlet {
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;
}
// Controller에서 request의 parameter를 Map으로 전해준다
ModelView modelView = controller.process(paramMap(request));
// 이후 Controller에서 ModelView에 경로와 값을 저장하고 반환
MyView myView = viewResolver(modelView.viewName());
// 반환받은 ModelView의 viewName으로 render 실행
myView.render(modelView.model(), request, response);
}
private Map<String, String> paramMap(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");
}
}
Java
복사
•
파라미터 데이터 추출 (paramMap)
◦
createParamMap()
◦
FrontController에서는 HttpServletRequest request를 활용해서 Map<String, String> paramMap에 파라미터 이름과 그 값을 매핑하여 저장시키고 있다
◦
생성된 paramMap을 controller에 주입시켜 Model값과 ViewName을 가진 ModelView를 반환하도록 한다
◦
이때 반환된 ViewName은 단순한 경로만을 나타낸다 (prefix와 suffix를 가지 않은 경로)
•
뷰 리졸버 (경로반환)
◦
MyView view = viewResolve(viewName)
◦
Controller가 반환한 논리 뷰 이름을 실제 물리 뮤 경로로 변경하고 실제 물리 경로가 있는 MyView 객체를 반환한다
▪
논리 뷰 이름 : members
▪
물리 뷰 경로 : /WEB-INF/views/members``.jsp
•
렌더링 작업 (request값 다 넘기고 이동)
◦
view.render(mv.getModel(), request, response)
◦
MyView를 통해서 HTML 화면 렌더링
◦
MyView의 render()는 Model 정보도 함께 받는다.
◦
Model 정보는 내부에서 request.setAttribute()로 담아둔다.
V3 - MyView
NOTE
public class MyView {
private String viewPath;
public MyView(String viewPath) {
this.viewPath = viewPath;
}
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
requestDispatcher.forward(request, response);
}
public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 넘어온 데이터를 전부 request에 넣어줌
modelToRequestAttribute(model, request);
RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
requestDispatcher.forward(request, response);
}
private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
model.forEach((key, value) -> request.setAttribute(key, value));
}
}
Java
복사
MyView는 기존 메서드와 더불어 모델을 저장하는 기능을 가지는 render()를 구현해줘야 한다
•
MyView는 request.setAttribute를 사용하여 데이터를 저장하고 뷰로 데이터 흐름을 이동시킨다.
V3 - ModelView
NOTE
public class ModelView {
private String viewName;
private Map<String, Object> model = new HashMap<>();
public ModelView(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
public void setViewName(String viewName) {
this.viewName = viewName;
}
public Map<String, Object> getModel() {
return model;
}
public void setModel(Map<String, Object> model) {
this.model = model;
}
}
Java
복사
ModelView는 뷰의 이름과 뷰를 렌더링할 때 필요한 model 객체를 가지고 있다
V3 - Controller 인터페이스
NOTE
public interface ControllerV3 {
ModelView process(Map<String, String> paramMap);
}
Java
복사
HttpServletRequest가 제공하는 파라미터는 FrontController가 paramMap에 담아서 호출해주면 된다
•
응답 결과로 뷰 이름과 뷰에 전달할 Model 데이터를 포함하는 ModelView 객체를 반환하면 된다.
V3 - V2 회원저장 코드 비교
NOTE
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 데이터 저장
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
// Model에 데이터 보관
request.setAttribute("member", member);
// View 경로를 저장하는 MyView 반환
return new MyView("/WEB-INF/views/save-result.jsp");
}
// V3
@Override
public ModelView process(Map<String, String> paramMap) {
// Request의 데이터를 꺼내서 작업
String username = paramMap.get("username");
int age = Integer.parseInt(paramMap.get("age"));
Member member = new Member(username, age);
memberRepository.save(member);
// 반환값을 가지고 ModelView 생성이후 반환
ModelView modelView = new ModelView("save-result");
modelView.getModel().put("member", member);
return modelView;
}
Java
복사
HttpServletRequest가 제공하는 파라미터는 FrontController가 paramMap에 담아서 호출해주면 된다
•
응답 결과로 뷰 이름과 뷰에 전달할 Model 데이터를 포함하는 ModelView 객체를 반환하면 된다.
FrontController - V4
NOTE
단순하고 실용적인 컨트롤러
기본 V3와 매우 비슷한 구조이지만, Controller에게 파라미터 정보는 물론 Model정보까지 넘겨준다
•
V3 Controller는 Servlet 종속성을 제거하고 View 경로의 중복을 제거하는 등 잘 설계된 Controller다
•
좋은 프레임워크는 아키텍쳐도 중요하지만, 개발자가 단순하고 편리하게 사용할 수 있어야 한다.
◦
v3를 변형해서 개발자가 편리하게 개발하도록 설계한다.
V4 - FrontController
NOTE
@WebServlet(name = "frontControllerServletV4", urlPatterns = "/front-controller/v4/*")
public class FrontControllerServletV4 extends HttpServlet {
private Map<String, ControllerV4> controllerMap = new HashMap<>();
public FrontControllerServletV4() {
controllerMap.put("/front-controller/v4/members/new-form", new MemberFormControllerV4());
controllerMap.put("/front-controller/v4/members/save", new MemberSaveControllerV4());
controllerMap.put("/front-controller/v4/members", new MemberListControllerV4());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
ControllerV4 controller = controllerMap.get(requestURI);
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
Map<String, String> paramMap = paramMap(request); // 파라미터 값들
Map<String, Object> model = new HashMap<>(); // 여기다 데이터 담음
String viewName = controller.process(paramMap, model); // 주소만 반환
MyView myView = viewResolver(viewName);
myView.render(model, request, response);
}
private Map<String, String> paramMap(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");
}
}
Java
복사
•
FrontController에서 Map<String, Object> model = new HashMap<>(); 을 통해 모델 객체를 생성한다 (request 역활)
•
기존과는 다르게 Controller에게 모델을 넘겨주어 Controller에서 모델에 값을 넣는 방식으로 취한다.
•
또한 Controller는 View 경로만 반환하면 되므로 이를 이용해 ViewResolver도 손쉽게 호출 가능하게 한다.
V4 - V3 회원 저장 비교
NOTE
// V3
@Override
public 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;
}
// V4
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
String username = paramMap.get("username");
int age = Integer.parseInt(paramMap.get("age"));
Member member = new Member(username, age);
memberRepository.save(member);
model.put("member", member);
return "save-result";
}
Java
복사