Version02 문제점
위의 Version02의 문제점은 무엇일까?
1. 서블릿에 종속적
2. 뷰 이름 중복
현재 컨트롤러는 단순히 비즈니스 로직만을 처리하는데 파라미터로 HttpServletRequest, HttpServletResponse를 받고 있다.
또한 /WEB-INF/views를 각각의 뷰에 선언함으로써 코드의 중복이 일어나고 있다.
해결방안
이번 포스팅의 목적은 아래 두 가지이다.
비즈니스 로직을 처리하는 컨트롤러가 서블릿 기술을 몰라도 동작할 수 있도록 동작하게 하자!
뷰의 논리 이름을 반환하고 실제 물리 위치의 이름은 FrontController에서 처하도록 단순화하자!
현재는 비즈니스 로직에 서블릿이 로직이 뒤섞여있다.
이를 ModelView를 통해서 걷어내 보자!
컨트롤러 인터페이스 생성
version02의 코드는 파라미터로 HttpServletRequest, HttpServletResponse 정보를 넘겼지만
현재는 request 정보를 Map으로 담아서 넘긴다.
또한 Request, Response정보를 담을 수 있는 ModelView 객체를 반환하게끔 설계한다.
public interface ControllerVersion03 {
ModelView process(final Map<String, String> paramMap);
}
ModelView 생성
MyView에서 처리하였던 viewName 정보를 처리하고
서블릿 관련 정보를 담기 위한 Map 정보를 처리하기 위한 ModelView를 설계하였다.
@Getter @Setter
public class ModelView {
private final String viewName;
private Map<String, Object> model = new HashMap<>();
public ModelView(String viewName) {
this.viewName = viewName;
}
}
폼, 저장, 조회 클래스 생성
비즈니스 로직은 서블릿 기술이 들어가지 않고 비즈니스 로직만 처리할 수 있다.
이전과는 다르게 구체적인 경로를 리턴하는 것이 아니라 논리명을 리턴한다.
또한 뷰에 랜더링 할 때 필요한 정보들을 담은 정보들을 Map에 담아 리턴한다.
public class MemberSaveController implements ControllerVersion03 {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public ModelView process(final Map<String, String> paramMap) {
final String username = paramMap.get("username");
final int age = Integer.parseInt(paramMap.get("age"));
final Member member = new Member(username, age);
memberRepository.save(member);
final ModelView modelView = new ModelView("save-result");
modelView.getModel().put("member", member);
return modelView;
}
}
FrontController 생성
비즈니스 로직의 서블릿 기술을 덜어내면서 FrontController의 책임이 조금 무거워졌다.
누군가 책임을 떠넘기면 다른 누군가는 그 책임을 안고 가야 하기 때문이다. 이러한 책임을 담당하는 것이 FrontController이다.
FrontController는 다른 비즈니스 로직과는 다르게 모든 요청의 첫 번째 단계이기에 귀찮은 모든 요청을 사전에 처리하며 컨트롤러에 디스 패치한다.
요청받은 모든 정보를 Map에 담기
: 컨트롤러에서 서블릿 기술 (ex request.setAttribute("member", member)) 를 처리하지 않기 때문에 FrontController에서 서블릿 요청의 모든 파라미터를 가져와서 Map으로 담는 책임을 담당한다.
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) {
MyView myView = new MyView("/WEB-INF/views/" + viewName + ".jsp");
return myView;
}
MyView 수정
기존의 Request처리를 비즈니스 로직에서 담당하였다면 현재는 MyView에서 해당 역할을 담당한다.
이는 FrontCotroller로부터 전달받은 map을 request에 set 하고 뷰에 랜더링 한다.
public class MyView {
private final String viewPath;
public MyView(final String viewPath) {
this.viewPath = viewPath;
}
public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
model.forEach((key, value) -> {
request.setAttribute(key, value);
});
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
Code Link
'CS > DesignPattern' 카테고리의 다른 글
(DesignPattern) FrontController - Version05 (with HandlerAdapter) (0) | 2021.05.11 |
---|---|
(DesignPattern) FrontController - Version04 (단순하고 실용적인 컨트롤러) (0) | 2021.05.07 |
(DesignPattern) FrontController - Version02 (View 분리) (0) | 2021.05.02 |
(DesignPattern) FrontController 패턴이란? (with FrontController - Version01) (0) | 2021.04.29 |
(DesignPattern) 메멘토 패턴 (0) | 2020.07.26 |