View 분리 - V2
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
앞서 프론트 컨트롤러 도입을 통해,
모든 컨트롤러의 입구를 하나로 만들어보았다.
하지만 위의 코드처럼 모든 컨트롤러에서 뷰로 이동하는 부분에 중복이 있고, 깔끔하지 않다.
이 부분도 프론트 컨트롤러를 통해 분리하여 공통 처리해 보도록 하자.
V2 구조
앞의 V1 구조와 달라진 점은 MyView의 존재 여부이다.
프론트 컨트롤러를 통해 각 컨트롤러를 호출하게 되고 해당 컨트롤러는 자신의 로직을 수행한 뒤,
다시 프론트 컨트롤러에게 viewPath (뷰 경로)를 반환한다.
반환한 경로를 MyView라는 클래스의 render()를 통해 호출하여 JSP를 forward 하는 방식이다.
MyView
public class MyView {
private String viewPath;
public MyView(String viewPath) {
this.viewPath = viewPath;
}
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
MyView 클래스의 생성자를 통해 viewPath를 저장한다.
render()를 통해 전달반응 viewPath 경로로 JSP forward를 진행한다.
ControllerV2
public interface ControllerV2 {
MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
ControllerV2 인터페이스의 경우,
MyView 타입을 반환하는 구현체를 요구한다.
MemberFormControllerV2 - 회원 등록 폼
public class MemberFormControllerV2 implements ControllerV2 {
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
return new MyView("/WEB-INF/views/new-form.jsp");
}
}
이제 각 컨트롤러는 복잡한 dispatcher.forward()를 직접 생성해서 호출하지 않아도 된다.
단순히 MyView 객체를 생성하고 거기에 뷰 이름만 넣고 반환하면 된다.
ControllerV1을 구현한 클래스와 ControllerV2를 구현한 클래스를 비교해 보면,
이 부분의 중복이 확실하게 제거된 것을 확인할 수 있다.
MemberSaveControllerV2 - 회원 저장
return new MyView("/WEB-INF/views/save-result.jsp");
MemberListControllerV2 - 회원 목록
return new MyView("/WEB-INF/views/members.jsp");
회원 저장, 목록 클래스의 경우도 마찬가지로 V1의 코드와 동일하다.
기존의 dispatcher.forward() 코드를 지우고 마지막에 return 값만 반환하면 된다.
프론트 컨트롤러 V2
private Map<String, ControllerV2> controllerMap = new HashMap<>();
public FrontControllerServletV2() {
controllerMap.put("/front-controller/v2/members/new-form", new MemberFormControllerV2());
controllerMap.put("/front-controller/v2/members/save", new MemberSaveControllerV2());
controllerMap.put("/front-controller/v2/members", new MemberListControllerV2());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
ControllerV2 controller = controllerMap.get(requestURI);
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
MyView view = controller.process(request, response);
view.render(request, response);
}
ControllerV2의 반환 타입이 MyView 이므로 프론트 컨트롤러는 컨트롤러의 호출 결과로 MyView를 반환받는다.
그리고 view.render()를 호출하면 forward 로직을 수행해서 JSP가 실행된다.
MyView.render()
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
프론트 컨트롤러의 도입으로 MyView 객체의 render()를 호출하는 부분을 모두 일관되게 처리할 수 있다.
각각의 컨트롤러는 MyView 객체를 생성만 해서 반환하면 된다.
실행
- 등록 : http://localhost:8080/front-controller/v2/members/new-form
- 목록 : http://localhost:8080/front-controller/v2/members
출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
'Spring MVC > MVC 프레임워크 만들기' 카테고리의 다른 글
[Spring MVC] 프론트 컨트롤러 - 어댑터 추가 (2) (0) | 2023.02.01 |
---|---|
[Spring MVC] 프론트 컨트롤러 - 어댑터 추가 (1) (0) | 2023.02.01 |
[Spring MVC] 프론트 컨트롤러 - 단순하고 실용적인 컨트롤러 (0) | 2023.02.01 |
[Spring MVC] 프론트 컨트롤러 - Model 추가 (0) | 2023.01.31 |
[Spring MVC] 프론트 컨트롤러 패턴 (0) | 2023.01.31 |