반응형
현재 코드의 문제점
- 컨트롤러는 매번 사용하지도 않는 HttpServletRequest, HttpServletResponse를 받는다.
- "/WEB-INF/views/new-form.jsp" 같은 경로에서 "/WEB-INF/views" 같이 경로가 반복된다.
현재 서블릿에서 종속성을 제거하기 위해 Model과 View 이름을 전달하는 객체 ModelView를 생성해보겠다.
ModelView는 MyView와 마찬가지로 다른 버전에서도 계속 사용하기 위해 상위 폴더에 둔다.
ModelView
@Getter @Setter
public class ModelView {
private String viewName;
private Map<String, Object> model = new HashMap<>();
public ModelView(String viewName) {
this.viewName = viewName;
}
}
- 귀찮아서 롬복의 @Getter @Setter를 사용했지만 라이브러리 없이 이용한다면 viewName과 model에 대해 각각 getter, setter 메소드를 생성해야 한다.
ControllerV3
public interface ControllerV3 {
ModelView process(Map<String, String> paramMap);
}
- v1에서는 void, v2는 MyView, v3는 ModelView 형태로 return
이제 Controller 3개를 추가한다.
MemberFormControllerV3
public class MemberFormControllerV3 implements ControllerV3{
@Override
public ModelView process(Map<String, String> paramMap) {
return new ModelView("new-form");
}
}
- /WEB-INF/views/v3/new-form.jsp 풀 경로를 쓰는게 아니라 new-form이라는 고유한 view 이름만 입력
MemberSaveControllerV3
public class MemberSaveControllerV3 implements ControllerV3{
MemberRepository memberRepository = MemberRepository.getInstance();
@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;
}
}
- 기존의 HttpServletRequest 대신 Map 사용 -> Map.get() 메소드를 이용
- Member를 선언해 Repository에 save한 뒤, ModelView에 view 이름 전달
- ModelView 안의 Map에 member를 put하고 return 시켜준다.
MemberListControllerV3
public class MemberListControllerV3 implements ControllerV3{
MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public ModelView process(Map<String, String> paramMap) {
List<Member> members = memberRepository.findAll();
ModelView mv = new ModelView("members");
mv.getModel().put("members", members);
return mv;
}
}
FrontControllerServletV3
@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 req, HttpServletResponse res) throws ServletException, IOException {
String reqURI = req.getRequestURI();
ControllerV3 controller = controllerMap.get(reqURI);
if(controller == null) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
Map<String, String> paramMap = createParam(req);
ModelView mv = controller.process(paramMap);
String viewName = mv.getViewName();
MyView view = viewResolver(viewName);
view.render(mv.getModel(), req, res);
}
private Map<String, String> createParam(HttpServletRequest req) {
Map<String, String> paramMap = new HashMap<>();
req.getParameterNames().asIterator()
.forEachRemaining(paramName -> paramMap.put(paramName, req.getParameter(paramName)));
return paramMap;
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
}
- reqURI를 받아와서 해당 URI에 대한 controller를 ControllerV3 controller에 저장
- Map paramMap에 HttpServletRequest의 파라미터들 저장 // createParam()
- ModelView mv에 controller.process의 실행 결과인 ModelView 객체 저장
- mv에서 viewName을 가져오고 viewResolver() 메소드를 통해 MyView 생성
- 생성된 MyView에서 render() 함수를 실행
MyView
...
public void render(Map<String, Object> model, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
modelToRequestAttribute(model, req);
RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
dispatcher.forward(req, res);
}
private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest req) {
model.forEach((key,value)-> req.setAttribute(key, value));
}
...
- Map을 받는 render 메소드를 추가
- modelToRequestAttribute를 보면 forEach를 돌며 HttpServletRequest에 .setAttribute()를 작업 실행
- Client는 Front Controller로 접근
- Front Controller는 request에 따라 적절한 Controller 배치
- Controller에서 ModelView에 데이터 저장 후 반환
- Controller에서 반환한 ModelView에서 viewName을 가져와 MyView 생성
- MyView의 render() 실행
본 게시글은 김영한 님의 '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 강의를 구매 후 정리하기 위한 포스팅입니다.
내용을 임의로 추가, 수정, 삭제한 부분이 많으며 정확한 이해를 위해서 강의를 구매하시는 것을 추천 드립니다.
반응형
'Develop > Java' 카테고리의 다른 글
[MVC] 어댑터 추가 (0) | 2021.05.19 |
---|---|
[MVC] Controller 단순화 (0) | 2021.05.13 |
[MVC] View 분리 (0) | 2021.05.10 |
[MVC] 프론트 컨트롤러 패턴 (2) | 2021.05.10 |
[MVC패턴] 회원 관리 웹 애플리케이션 (0) | 2021.05.10 |