본문 바로가기
Develop/Java

[MVC] Model 분리

by 연로그 2021. 5. 11.
반응형

현재 코드의 문제점

  1. 컨트롤러는 매번 사용하지도 않는 HttpServletRequest, HttpServletResponse를 받는다.
  2. "/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편 - 백엔드 웹 개발 핵심 기술' 강의를 구매 후 정리하기 위한 포스팅입니다.

내용을 임의로 추가, 수정, 삭제한 부분이 많으며 정확한 이해를 위해서 강의를 구매하시는 것을 추천 드립니다.

 

inf.run/B756

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원

www.inflearn.com

 

반응형

'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