2️⃣ V 2
🤔💭 View 로 전송하는 dispatcher 메서드가 겹치는데..? 이걸 개선할 수 없을까? ➡️ getRequestDispatcher 라는 공통 메서드를 하나로 통합시키자 !
출처: 김영한의 스프링MVC (인프런)
💡 그래서 등장한 V2 아키텍쳐. V1 에서는 모든 컨트롤러가 뷰로 넘어가는 로직을 갖고 있어서 중복이 있고 구조상 깔끔하지 않았다. V2에서는 컨트롤러는 비즈니스 로직을 수행하는 것에만 집중하고 MyView 객체를 반환하여 뷰로 넘어가는 작업은 다른 객체에서 수행하도록 역할을 분리했다.
⚡️ 2-1. MyView
- MyView 클래스를 만들었다. render() 메서드로 viewPath 경로로 브라우저를 이동시키는 작업을 수행한다.
public class MyView {
private String viewPath;
public MyView(String viewPath) {
this.viewPath = viewPath;
}
//각 컨트롤러에서 jsp 로 넘겨주는 로직을 View 객체에 넣었다.
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
⚡️ 2-2. FrontControllerServletV2
이제 프론트 컨트롤러에서는 requestDispatcher 작업이 생략된다. V1 에서 했던것처럼 요청 uri에 해당하는 비즈니스 로직을 가진 컨트롤러를 호출한다는 큰 틀은 같지만, 다른 점은 각 컨트롤러에서 리턴하는 값이 MyView 객체라는 점이다.
각 컨트롤러들이 MyView 객체에 viewPath 값을 담아 반환하면, 프론트 컨트롤러에서 render() 를 실행하여 브라우저를 이동시킨다.
@WebServlet(name = "frontControllerServletV2", urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV2 extends HttpServlet {
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);
}
}
⚡️ 2-3. ControllerV2
- 각 컨트롤러가 구현하는 인터페이스. MyView 를 반환하도록 만들었다.
public interface ControllerV2 {
//이번 인터페이스는 MyView 를 반환하도록 설계함
MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
⚡️ 2-4. 구현체 컨트롤러들…
- 각 컨트롤러는 비즈니스 로직 수행 후 MyView 객체에 생성자로 url path 를 넣고 반환한다.
public class MemberFormControllerV2 implements ControllerV2 {
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
MyView myView = new MyView("/WEB-INF/views/new-form.jsp");
return myView;
}
}
public class MemberSaveControllerV2 implements ControllerV2 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@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);
memberRepository.save(member);
request.setAttribute("member", member);
return new MyView("/WEB-INF/views/save-result.jsp");
}
}
public class MemberListControllerV2 implements ControllerV2 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
return new MyView("/WEB-INF/views/members.jsp");
}
}
🤔💭 가독성이 좀 나아진 것 같아. 그런데 viewPath 의 “/WEB-INF/views” 경로는 항상 겹치는데 이걸 공통로직으로 만들 수 없을까? 그리고 request 대신 다른 Model 을 사용하면 서블릿에 대한 종속성을 없앨 수 있을 것 같은데…