[JavaWeb] 6장 서블릿 JSP를 활용해 동적인 웹 애플리케이션 개발하기 - 3
6.4 MVC 프레임워크 요구사항 1단계
JSP에 상당 부분의 로직을 포함하는 것이 초기 개발 속도는 빨랐지만 유지보수 비용은 증가했다.
이 같은 단점을 보완해 유지보수 비용을 줄이기 위해 MVC(Model, View, Controller) 패턴 기반으로 웹 애플리케이션을 개발하는 방향으로 발전했다.
6.4.1 요구사항
MVC 패턴을 지원하는 프레임워크를 구현하라
모든 요청을 RequestHandler 가 받아서 요청 URL 에 따라 분기처리 했듯이
서블릿도 모든 요청을 하나의 서블릿이 받은 후 요청 URL 에 따라 분기 처리하는 방식으로 구현한다.
컨트롤러 : 사용자의 최초 진입 지점
뷰에 직접 접근하는 것을 막고 항상 컨트롤러를 통해 접근하도록 해야 한다.
ex) /user/form.jsp 과 같이 jsp 로 직접 접근하지 않도록 한다.
모든 클라이언트 요청은 먼저 DispatcherServlet 이 받는다.
DispatcherServlet 은 요청 URL 에 따라 해당 컨트롤러에 작업을 위임한다.
→ @WebServlet 으로 URL 을 매핑할 때 urlPatterns=”/” 와 같이 설정하면 된다.
css, javascript, image 처럼 정적인 자원은 컨트롤러가 필요없고 해당 요청은 DispatcherServlet 처리에서 제외하기 위해 서블릿 필터를 추가한다. (본 프로젝트에는 이미 ResourceFilter 가 추가되어 있음)
6.4.2 요구사항 분리 및 힌트
-
모든 요청을 서블릿 하나가 받을 수 있도록 url을 매핑한다.
@WebServlet(name = "dispatcher", urlPatterns = "/", loadOnStartup = 1) // loadOnStartup 속성이 무슨 일을 하는 지 학습해 본다. // 서블릿은 "/" 로 설정함으로써 모든 요청을 하나의 서블릿으로 매핑할 수 있다. -
Controller 인터페이스를 추가한다.
public interface Controller { String execute(HttpServletRequest request, HttpServletResponse response) throws Exception; } // execute() 메소드의 반환 값이 String 인 것을 눈여겨보자. -
서블릿으로 구현되어 있는 회원관리 기능을 앞 단계에서 추가한 Controller 인터페이스 기반으로 다시 구현한다. execute() 메소드의 반환 값은 리다이렉트 방식으로 이동할 경우 redirect:로 시작하고 포워드 방식으로 이동할 경우 jsp 경로를 반환한다.
public class ListUserController implements Controller { @Override public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception { if(!UserSessionUtils.isLogined(req.getSession())) { return "redirect:/users/loginForm"; } req.setAttribute("users", Database.findAll()); return "/user/list.jsp"; } } - RequestMapping 클래스를 추가해 요청 url 과 컨트롤러 매핑을 설정한다.
- 특별한 로직 없이 뷰에 대한 이동만을 담당하는 ForwardController 를 추가한다. (회원가입 화면, 로그인 화면)
- DispatcherServlet 에서 요청 URL 에 해당하는 컨트롤러를 찾아 execute() 메소드를 호출해 실질적인 작업을 위임한다.
- 컨트롤러의 execute() 메소드 반환 값 string 을 받아 서블릿에서 JSP 로 이동할 때의 중복을 제거한다.
6.5 MVC 프레임워크 구현 1단계
구현 완료 참고 branch : step2-user-with-mvc-framework
클라이언트 요청에 대한 처리를 담당하는 부분을 Controller interface로 추상화한다.
public interface Controller {
String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception;
}
지금까지 HttpServlet 을 상속해 구현한 서블릿 코드를 Controller 인터페이스를 구현하도록 변경한다.
예)
as-is
@WebServlet("/users")
public class ListUserController extends HttpServelt {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if(!UserSessionUtils.isLogined(req.getSession())) {
resp.sendRedirect("/users/loginForm");
return;
}
req.setAttribute("users", Database.findAll());
RequestDispatcher rd = req.getRequestDispatcher("/user/list.jsp");
rd.forward(req, resp);ㅁㄴㅇ
}
}
to-be
public class ListUserController implements Controller {
@Override
public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
if(!UserSessionUtils.isLogined(req.getSession())) {
return "redirect:/users/loginForm";
}
req.setAttribute("users", Database.findAll());
return "/user/list.jsp";
}
}
특별한 로직 없이 뷰(JSP) 에 대한 이동만을 담당하는 ForwardController 를 추가한다.
public class ForwardController implements Controller {
private String forwardUrl;
public ForwardController (String forwardUrl) {
this.forwardUrl = forwardUrl;
if(forwardUrl == null) {
throw new NullPointerException("forwardUrl is null.");
}
}
@Override
public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
return forwardUrl;
}
}