3. 프런트 컨트롤러 만들기
프런트 컨트롤러를 만든다. web06 프로젝트의 spms.servlets 패키지에 DispatcherServlet 클래스를 생성한다. 물론 HttpServlet 클래스를 상속받아야 한다.
package spms.servlets;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import spms.vo.Member;
@SuppressWarnings("serial")
@WebServlet("*.do") //프런트 컨트롤러의 배치
public class DispatcherServlet extends HttpServlet {//서블릿이기 때문에 HttpServlet 상속
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=UTF-8");
String servletPath = request.getServletPath(); //서블릿 경로 추출
try {
String pageControllerPath = null;
//페이지 컨트롤러로 위임
if("/member/list.do".equals(servletPath)) {
pageControllerPath = "/member/list";
}else if("/member/add.do".equals(servletPath)) {
pageControllerPath = "/member/add";
if(request.getParameter("email") != null) {
//요청 매개변수로부터 VO 객체 준비
request.setAttribute("member", new Member().setEmail(request.getParameter("email"))
.setPassword(request.getParameter("password"))
.setName(request.getParameter("name")));
}
}else if("/member/update.do".equals(servletPath)) {
pageControllerPath = "/member/update";
if(request.getParameter("email") != null) {
//요청 매개변수로부터 VO 객체 준비
request.setAttribute("member", new Member().setNo(Integer.parseInt(request.getParameter("no")))
.setEmail(request.getParameter("email"))
.setName(request.getParameter("name")));
}
}else if("/member/delete.do".equals(servletPath)) {
pageControllerPath = "/member/delete";
}else if("/auth/login.do".equals(servletPath)) {
pageControllerPath = "/auth/login";
}else if("/auth/logout.do".equals(servletPath)) {
pageControllerPath = "/auth/logout";
}
//페이지 컨트롤러로 실행 위임
RequestDispatcher rd = request.getRequestDispatcher(pageControllerPath);
rd.include(request, response);
//JSP로 위임
String viewUrl = (String)request.getAttribute("viewUrl");
if(viewUrl.startsWith("redirect:")) {
response.sendRedirect(viewUrl.substring(9));
return;
}else {
rd = request.getRequestDispatcher(viewUrl);
rd.include(request, response);
}
} catch (Exception e) {//오류 처리
e.printStackTrace();
request.setAttribute("error", e);
RequestDispatcher rd = request.getRequestDispatcher("/Error.jsp");
rd.forward(request, response);
}
}
}
프런트 컨트롤러도 서블릿이기 때문에 HttpServlet 클래스를 상속받는다. 그런데 여기서 주목할 점은 오버라이딩하는 메서드이다.
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
}
doGet() 메서드, doPost() 메서드가 아니라 service() 메서드이다. 이 메서들의 매개변수를 보면 ServletRequest와 ServletResponse가 아니라 HttpServletRequest와 HttpServletResponse 이다. 즉 Servlet 인터페이스에 선언된 메서드가 아니다.
그럼 이 메서드는 서블릿 컨테이너(톰캣)가 직접 호출하지 않는다는 것인데, 언제 호출되는지 다음 그림을 통해 살펴본다(그림 1).
① 클라이언트로부터 요청이 들어오면 서블릿 컨테이너는 규칙에 따라 Servlet 인터페이스에 선언된 service() 메서드를 호출한다(원래는 스레드가 호출함).
② 이 service(ServletRequest, ServletResponse) 메서드는 HttpServlet 클래스에 추가된 동일한 이름의 service(HttpServletRequest, HttpServletResponse) 메서드를 호출한다. 이름은 같지만, 매개변수가 다르다.
③ service(HttpServletRequest, HttpServletResponse) 메서드는 HTTP 요청 프로토콜을 분석하여 다시 doGet() 메서드, doPost() 메서드 등을 호출한다.
GET 요청이든, POST 요청이든 service(HttpServletRequest, HttpServletResponse) 메서드가 호출된다는 것이다. 그래서 이 메서드를 오버라이딩 한 것이다.
물론 "doGet() 메서드나 doPost() 메서드를 오버라이딩해도 될텐데"라고 생각할 수 있다. doGet() 메서드, doPost() 메서드를 오버라이딩하여 프런트 컨트롤러를 만들 수 있지만, 그럼에도 service() 메서드를 오버라이딩 한 이유는 첫째, GET, POST 뿐만 아니라 다양한 요청 방식에도 대응하기 위해서다. 둘째, 가능한 HttpServlet 클래스의 내부 구조를 조금이라도 파악하기 위함이다.
- JSP로 위임
페이지 컨트롤러의 실행이 끝나면, 화면 출력을 위해 ServletRequest에 보관된 뷰 URL로 실행을 위임한다. 단 뷰 URL이 "redirect:"로 시작한다면, 인클루딩 하는 대신 sendRedirect() 메서드를 호출한다.
//JSP로 위임
String viewUrl = (String)request.getAttribute("viewUrl");
if(viewUrl.startsWith("redirect:")) {
response.sendRedirect(viewUrl.substring(9));
return;
}else {
rd = request.getRequestDispatcher(viewUrl);
rd.include(request, response);
}
- 오류 처리
서블릿을 만들 때 매번 작성한 것 중의 하나가 오류 처리이다. 이제 프런트 컨트롤러에서 오류 처리를 담당하기 때문에, 페이지 컨트롤러를 작성할 때는 오류 처리 코드를 넣을 필요가 없다.
try {
...
} catch (Exception e) {//오류 처리
e.printStackTrace();
request.setAttribute("error", e);
RequestDispatcher rd = request.getRequestDispatcher("/Error.jsp");
rd.forward(request, response);
}
- 프런트 컨트롤러의 배치
@WebServlet 어노테이션을 사용하여 프런트 컨트롤러의 배치 URL을 "*.do"로 지정한다. 즉 클라이언트의 요청 중에서 서블릿 경로 이름이 .do로 끝나는 경우는 DispatcherServlet 클래스가 처리하겠다는 의미이다.
@WebServlet("*.do") //프런트 컨트롤러의 배치
public class DispatcherServlet extends HttpServlet {//서블릿이기 때문에 HttpServlet 상속
...
}
참고도서 : https://freelec.co.kr/book/1674/
[열혈강의] 자바 웹 개발 워크북
[열혈강의] 자바 웹 개발 워크북
freelec.co.kr
'교재 실습 > 자바 웹 개발 워크북' 카테고리의 다른 글
78. 프런트 컨트롤러의 도입 (4) (0) | 2022.07.26 |
---|---|
77. 프런트 컨트롤러의 도입 (3) (0) | 2022.07.25 |
75. 프런트 컨트롤러의 도입 (1) (0) | 2022.07.21 |
74. 미니 MVC 프레임워크 만들기 도입부 및 환경설정 (0) | 2022.07.20 |
73. DataSource와 JNDI (4) (0) | 2022.07.19 |
댓글