일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 불변객체
- DispatcherServlet
- fcm성능비교
- 검증 실패 예외처리
- HttpServlet
- 동등성동일성
- multiparfile데이터
- 디스패처서블릿
- 왜불변객체인가
- java enum
- 스프링요청반응
- 옵티마이저
- Wrapper class
- equals
- 중첩클래스
- 옵티마저
- biblecash
- rest api 검증
- multipart바인딩
- 프로세스 생성
- 동기비동기블로킹논블로킹
- 클라이언트요청반응
- HTTP프로토콜
- 래퍼클래스
- 데이터베이스파서
- 공유기작동방식
- rest api
- fcmv1
- fcm데이터구조
- httpservlet기술
- Today
- Total
개발은 아름다워
[ Spring ] 고대의 서블릿을 찾아서(7) - ModelView를 없애고 더욱 단순하게 본문
고대의 서블릿에서 현재 Spring MVC 프레임워크까지 여정을 살펴보고 있다.
서블릿 -> 템플릿 엔진 -> MVC 패턴 -> FrontController -> view -> ModelView 여정까지 오게 되었다.
ModelView 까지 적용한 과정을 간략하게 적어보면
1. ModelView 객체에 viewName과 controller로 부터 발생한 데이터를 model에 담는다.
2. controller는 ModelView 객체를 반환한다.
3. FrontController는 controller가 반환한 ModelView 객체에 있는
1) viewName을 viewResolver를 통해 viewPath로 바꿈
2) model에 담긴 데이터를 HttpServletRequest request객체로 데이터 전달
4. view.render는 model 데이터를 받은 후 jsp 서블릿에게 데이터를 전달
그렇다면 질문! 굳이 꼭 ModelView 객체를 생성하고 반환해야 하는 것일까? 실제 필자가 다니고 있는 회사는 올드한 회사라 아직 ModelAndView를 쓴다. 매번 컨트롤러에서 ModelAndView 객체를 생성 후 return 각 컨트롤러마다 써야하는 귀찮음이 있다.
그렇다면 ModelView를 반환하지 않고 실용적인 방법으로 바꿔보자
객체를 이용하기
이번에는 바로 FrontController 코드를 봐보자
@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 {
System.out.println("FrontControllerServletV2.service");
String requestURI = request.getRequestURI();
ControllerV4 controller = controllerMap.get(requestURI);
if(controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
// http요청에 있는 데이터를 paramMap에 넣어줘야함
Map<String,String> paramMap = createParamMap(request);
Map<String,Object> model = new HashMap<>();
// 실제 뷰 경로를 가져와야함
String viewName = controller.process(paramMap, model);
MyView view = viewResolver(viewName);
view.render(model,request,response);
}
private Map<String, String> createParamMap(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");
}
}
이전 ModelView를 이용했을 때의 차이는 Map<String,Object> model = new HashMap<>();를 넘겨준다는 것이다. 요청이 오면 model이라는 객체가 생성된다. 이 때 model 객체의 참조값은 x001이라고 가정하자. controller가 model로 받는 매개변수의 참조값도 x001이 된다. 왜냐하면 자바는 call by Value이기 때문
Map<String,Object> model = new HashMap<>();
// controller에 매개변수로 넘겨주는 model의 참조값도 x001
String viewName = controller.process(paramMap, model);
이제 감이 오지 않는가? FrontController에서 생성된 model의 참조값은 controller에서 사용되는 model의 참조값과 같다. 따라서 controller.process(paramMap, model); 이 진행된 후에는 model의 참조값 x001에 해당하는 데이터 값들이 변경되어 있다는 것이다.
이에 맞게 Controller 인터페이스와 구현체인 controller들의 코드를 변경해보자.
public interface ControllerV4 {
String process(Map<String,String> paramMap,Map<String,Object> model);
}
public class MemberFormControllerV4 implements ControllerV4 {
// ModelView 사용 전
// @Override
// public MyView process(HttpServletRequest request, HttpServletResponse response)
// throws ServletException, IOException {
// return new MyView("/WEB-INF/views/new-form.jsp");
// }
// ModelView 사용 후
// @Override
// public ModelView process(Map<String, String> paramMap) {
// return new ModelView("new-form");
// }
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
return "new-form";
}
}
public class MemberSaveControllerV4 implements ControllerV4{
private MemberRepository memberRepository = MemberRepository.getInstance();
// ModelView 사용 전
// @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);
// System.out.println("member = " + member);
// memberRepository.save(member);
// // Model에 데이터를 보관
// request.setAttribute("member", member);
// return new MyView("/WEB-INF/views/save-result.jsp");
// }
// ModelView 사용
// @Override
// public ModelView process(Map<String, String> paramMap) {
// //paramMap은 Http 요청 데이터를 주고 받는 용도
// String username = paramMap.get("username");
// int age = Integer.parseInt(paramMap.get("age"));
// Member member = new Member(username, age);
// memberRepository.save(member);
// // ModelView에 데이터를 넣을 수 있음
// ModelView mv = new ModelView("save-result");
// mv.getModel().put("member", member);
// return mv;
// }
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
//paramMap은 Http 요청 데이터를 주고 받는 용도
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";
}
}
public class MemberListControllerV4 implements ControllerV4{
private MemberRepository memberRepository = MemberRepository.getInstance();
// ModelView 사용 전
// @Override
// public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// System.out.println("MvcMemberListServlet.service");
// List<Member> members = memberRepository.findAll();
// request.setAttribute("members", members);
// return new MyView("/WEB-INF/views/members.jsp");
// }
// ModelView 사용
// @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;
// }
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
List<Member> members = memberRepository.findAll();
model.put("members", members);
return "members";
}
}
이전 코드를 비교 할 수 있게 주석처리를 해놨다. 딱봐도 코드가 훨~씬 말끔하게 변경되었다. ModelView와 동작 방식은 같다. 차이는 ModelView 객체를 반환하는 것이 아니라 컨트롤러는 논리 이름만 반환한다. controller에서 발생한 데이터는 FrontController에서 생성한 model 객체에 담기게 된다. 이번 버전의 컨트롤러는 매우 단순하고 실용적이다. 기존 구조에서 모델을 파라마터로 넘기고, 뷰의 논리 이름을 반환한다는 작은 아이디어를 적용했을 뿐인데, 코드가 훨씬 깔끔해졌다.
고대의 서블릿에서 지금 FrontController까지 한 번에 점프한 것이 아니라 점진적으로 불편한 점을 해결하면서 발전했다. 이로 보아 프레임워크나 공통 기능이 수고로워야 사용하는 개발자가 편리해진다는 것을 알 수 있다.
참고
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/
'스프링' 카테고리의 다른 글
[ Spring ] 스프링에서 응답 데이터 만드는 방식 (0) | 2024.10.14 |
---|---|
[ Spring ] 고대의 서블릿을 찾아서(8) - 어댑터패턴과 트레이드 오프 (0) | 2024.10.14 |
[ Spring ] 고대의 서블릿을 찾아서(6) - ModelView의 등판 (1) | 2024.10.14 |
[ Spring ] 고대의 서블릿을 찾아서(5) FrontController에 View 추가 (0) | 2024.10.14 |
[ Spring ] 고대의 서블릿을 찾아서(3) 템플릿 엔진을 너머 MVC패턴으로 (0) | 2024.10.12 |