[ Spring ] 고대의 서블릿을 찾아서(5) FrontController에 View 추가
지금까지의 고대의 서블릿 찾는 여정...
서블릿 -> 템플릿 엔진 -> mvc패턴 -> FrontController 도입
전에 있던 단점을 보안하며 FrontController까지 오게되었다.
왜 view가 필요할까?
전에 있던 단점들은 대부분 중복되는 코드들을 어떻게 하면 공통으로 쓸 수 있을까? 에 대한 고민이였다. 이번 view를 추가하는 것도 동일하다.
FrontController의 controller들의 코드를 봐보자
public class MemberFormControllerV1 implements ControllerV1{
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
public class MemberSaveControllerV1 implements ControllerV1{
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void 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);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
public class MemberListControllerV1 implements ControllerV1{
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("MvcMemberListServlet.service");
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
controller마다 중복되는 코드가 보인다! 그것은 바로 viewPath에 해당하는 jsp 서블릿을 찾아서 데이터를 넘겨주는 부분이다.
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
중복되는 코드들을 해결하기 위해 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);
}
}
이 때 FrontController에서 MyView라는 객체의 역할을 명확하게 나타내기 위해서 Controller 인터페이스의 반환값을 변경한 ControllerV2 인터페이스를 만든다.
public interface ControllerV2 {
MyView process(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException;
}
Q.왜 ControllerV2가 MyView를 반환하게 만들어야할까?
이 부분을 좀 더 자세히 설명하자면 다음과 같다.
MyView를 ControllerV1의 인터페이스를 상속 받은 controller에 적용시킨다면 다음과 같다.
public class MemberSaveControllerV1 implements ControllerV1{
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void 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);
String viewPath = "/WEB-INF/views/save-result.jsp";
// 이전 코드 RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
// 이전 코드 dispatcher.forward(request, response);
// MyView 적용
MyView view = new MyView(viewPath);
view.render(request, response);
}
}
controller 내부에서 view.render까지 진행해도 된다. 하지만 이럴 경우에는 각 controller마다 view.render까지 코드를 작성해야한다. 즉, 중복 코드를 없애기 위해 MyView를 만들었는데 다시 중복 코드가 생기는 것이다. 또한 FrontController에서 MyView의 역할을 명확하게 알 수 없다. 따라서 MyView를 반환하는 ControllerV2 인터페이스를 만드는 것이다.
MyView를 반환하는 ContollerV2와 ContollerV2를 상속 받는 controller들
public interface ControllerV2 {
MyView process(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException;
}
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");
}
}
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);
System.out.println("member = " + member);
memberRepository.save(member);
// Model에 데이터를 보관
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 {
System.out.println("MvcMemberListServlet.service");
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
return new MyView("/WEB-INF/views/members.jsp");
}
}
각 contoller들을 반환값으로 jsp 경로가 있는 MyView 인스턴스를 반환한다. 이제 반환된 MyView 인스턴스가 render를 하도록 만들면된다. 위에서 언급한 것처럼 이 부분은 공통이기 때문에 FrontController에서 진행하면 된다.
@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 {
System.out.println("FrontControllerServletV2.service");
String requestURI = request.getRequestURI();
ControllerV2 controller = controllerMap.get(requestURI);
if(controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
MyView view = controller.process(request, response);
view.render(request, response);
}
}
이렇게 해서 FrontController에서는 jsp 서블릿으로 데이터를 넘겨주는 공통 처리 기능인 render까지 할 수 있게 되었다.
정리
전에 강의를 들으면서 곰곰히 생각하지 않았던 부분들이 있었다. 그냥 그렇게 하라니깐 코드를 작성했는데, 복습하고 정리하면서 '왜 이 코드를 여기서 작성하는거지?' 라는 질문을 하면서 생각하는 시간을 가졌고 코드를 이해하는 정도가 깊어진 것 같다. 궁금한 것이 있으면 끝까지 생각해보고 스스로 질문을 해야겠다.
참고
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1