4. 페이지 컨트롤러 MemberListServlet 클래스를 일반 클래스로 전환
회원 목록을 처리하는 페이지 컨트롤러 'MemberListServlet' 클래스를 일반 클래스로 전환한다. spms.controls 패키지에 MemberListController 클래스를 생성한다.
package spms.controls;
import java.util.Map;
import spms.dao.MemberDao;
public class MemberListController implements Controller {
@Override
public String execute(Map<String, Object> model) throws Exception {
//Map 객체에서 MemberDao를 꺼냄
MemberDao memberDao = (MemberDao)model.get("memberDao");
//회원 목록 데이터를 Map 객체에 저장
model.put("members", memberDao.selectList());
//화면을 출력할 JSP 페이지의 URL 반환
return "/member/MemberList.jsp";
}
}
- Controller 인터페이스의 구현
페이지 컨트롤러가 되려면 Controller 규칙에 따라 클래스를 작성해야 한다.
public class MemberListController implements Controller {
@Override
public String execute(Map<String, Object> model) throws Exception {
...
}
}
페이지 컨트롤러는 더 이상 예외 처리를 할 필요가 없다. 예외가 발생했을 때 호출자인 프런트 컨트롤러에게 던지면 된다.
- 페이지 컨트롤러에서 사용할 객체를 Map에서 꺼내기
MemberDao 객체는 프런트 컨트롤러가 넘겨준 'Map 객체(model)'에 들어있다. 물론 프런트 컨트롤러가 그런 준비 작업을 하도록 변경할 것이다.
//Map 객체에서 MemberDao를 꺼냄
MemberDao memberDao = (MemberDao)model.get("memberDao");
- 페이지 컨트롤러가 작업한 결과물을 Map에 담기
Map 객체 'model' 매개변수는 페이지 컨트롤러가 작업한 결과를 담을 때도 사용한다. 예제에서는 회원 목록 데이터를 model에 저장하고 있다.
//회원 목록 데이터를 Map 객체에 저장
model.put("members", memberDao.selectList());
Map 객체에 저장된 값은 프런트 컨트롤러가 꺼내서 ServletRequest 보관소로 옮길 것이다. ServletRequest 보관소에 저장된 값은 다시 JSP가 꺼내서 사용할 것이다.
- 뷰 URL 반환
페이지 컨트롤러의 반환값은 화면을 출력할 JSP의 URL이다. 프런트 컨트롤러는 이 URL로 실행을 위임한다.
//화면을 출력할 JSP 페이지의 URL 반환
return "/member/MemberList.jsp";
5. 프런트 컨트롤러 변경
이제 Controller 규칙에 따라 페이지 컨트롤러로 호출하도록 프런트 컨트롤러를 변경한다. 일단 MemberListController 클래스만 준비했기 때문에 회원 목록에 대해서만 적용해 본다. spms.servlets.DispatcherServlet 클래스의 service() 메서드를 다음과 같이 변경한다.
package spms.servlets;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
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.controls.Controller;
import spms.controls.MemberListController;
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 {
ServletContext sc = this.getServletContext();
//페이지 컨트롤러에게 전달할 Map 객체를 준비한다.
HashMap<String,Object> model = new HashMap<String,Object>();
model.put("memberDao", sc.getAttribute("memberDao"));
model.put("session", request.getSession());
//String pageControllerPath = null;
//인터페이스 타입의 참조 변수 선언 - Controller의 구현체 클래스들 객체 주소 저장 위함
Controller pageController = null;
//페이지 컨트롤러로 위임
if("/member/list.do".equals(servletPath)) {
//pageControllerPath = "/member/list";
pageController = new MemberListController();
}else if("/member/add.do".equals(servletPath)) {
//pageControllerPath = "/member/add";
pageController = new MemberAddController();
if(request.getParameter("email") != null) {
//요청 매개변수로부터 VO 객체 준비
/*request.setAttribute("member", new Member().setEmail(request.getParameter("email"))
.setPassword(request.getParameter("password"))
.setName(request.getParameter("name")));*/
//Map 객체에 VO 객체 준비
model.put("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";
pageController = new MemberUpdateController();
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")));*/
//Map 객체에 VO 객체 준비
model.put("member", new Member().setNo(Integer.parseInt(request.getParameter("no")))
.setEmail(request.getParameter("email"))
.setName(request.getParameter("name")));
}else {
model.put("no", new Integer(request.getParameter("no")));
}
}else if("/member/delete.do".equals(servletPath)) {
//pageControllerPath = "/member/delete";
pageController = new MemberDeleteController();
model.put("no", new Integer(request.getParameter("no")));
}else if("/auth/login.do".equals(servletPath)) {
//pageControllerPath = "/auth/login";
pageController = new LogInController();
if(request.getParameter("email") != null) {
model.put("loginInfo", new Member().setEmail(request.getParameter("email"))
.setPassword(request.getParameter("password")));
}
}else if("/auth/logout.do".equals(servletPath)) {
//pageControllerPath = "/auth/logout";
pageController = new LogOutController();
}
//페이지 컨트롤러로 실행 위임
/*RequestDispatcher rd = request.getRequestDispatcher(pageControllerPath);
rd.include(request, response);*/
//페이지 컨트롤러 실행
String viewUrl = pageController.execute(model);
//Map 객체에 저장된 값을 ServletRequest에 복사
for(String key : model.keySet()) {//keySet() : key값 가져옴
request.setAttribute(key, model.get(key)); //model.get(key) : value값 가져옴
}
//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);
}*/
//JSP로 실행 위임
if(viewUrl.startsWith("redirect:")) {
response.sendRedirect(viewUrl.substring(9));
return;
}else {
RequestDispatcher 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);
}
}
}
클래스의 전체 코드에서 service() 메서드의 try 블록만 변경했다.
- Map 객체 준비
프런트 컨트롤러와 페이지 컨트롤러 사이에 데이터나 객체를 주고 받을 때 사용할 Map 객체를 준비한다. 즉 MemberListController 클래스가 사용할 객체를 준비하여 Map 객체에 담아서 전달해 준다.
ServletContext sc = this.getServletContext();
//페이지 컨트롤러에게 전달할 Map 객체 준비
HashMap<String,Object> model = new HashMap<String,Object>();
model.put("memberDao", sc.getAttribute("memberDao"));
MemberListController 클래스는 회원 목록을 가져오기 위해 MemberDao 객체가 필요하다. 그래서 ServletContext 보관소에 저장된 MemberDao 객체를 꺼내서 Map 객체에 담았다.
- 회원 목록을 처리할 페이지 컨트롤러 준비
페이지 컨트롤러는 Controller 인터페이스의 구현체이기 때문에, 인터페이스 타입의 참조 변수를 선언한다. 그래야만 앞으로 만들 MemberAddController, MemberUpdateController, MemberDeleteController 등의 객체 주소도 저장할 수 있다.
//인터페이스 타입의 참조 변수 선언 - Controller의 구현체 클래스들 객체 주소 저장 위함
Controller pageController = null;
회원 목록 요청을 처리할 페이지 컨트롤러를 준비한다.
//페이지 컨트롤러로 위임
if("/member/list.do".equals(servletPath)) {
pageController = new MemberListController();
}
나머지 조건문은 그대로 두고, 회원 목록에 대해서만 적용한다.
- 페이지 컨트롤러의 실행
이전에는 페이지 컨트롤러가 서블릿이었기 때문에 인클루딩이라는 방법을 통해 실행을 위임했었다. 이제는 MemberListController 클래스가 일반 클래스이기 때문에 메서드를 호출해야 한다. 그것도 Controller 인터페이스에 정해진 대로 execute() 메서드를 호출한다.
//페이지 컨트롤러 실행
String viewUrl = pageController.execute(model);
execute() 메서드를 호출할 때 페이지 컨트롤러를 위해 준비한 Map 객체를 매개변수로 넘긴다. execute() 메서드의 반환 값은 화면 출력을 수행하는 JSP의 URL이다.
- Map 객체에 저장된 값을 ServletRequest에 복사
Map 객체는 페이지 컨트롤러에게 데이터나 객체를 보낼 때 사용되기도 하지만 페이지 컨트롤러의 실행 결과물을 받을 때도 사용한다. 따라서 페이지 컨트롤러의 실행이 끝난 다음, Map 객체에 보관되어 있는 데이터나 객체를 JSP가 사용할 수 있도록 ServletRequest에 복사한다.
//Map 객체에 저장된 값을 ServletRequest에 복사
for(String key : model.keySet()) {//keySet() : key값 가져옴
request.setAttribute(key, model.get(key)); //model.get(key) : value값 가져옴
}
- 뷰로 실행 위임
나머지 코드는 이전과 같이 JSP로 실행을 위임하거나 리다이렉트를 위한 코드이다.
//JSP로 실행 위임
if(viewUrl.startsWith("redirect:")) {
response.sendRedirect(viewUrl.substring(9));
return;
}else {
RequestDispatcher rd = request.getRequestDispatcher(viewUrl);
rd.include(request, response);
}
- 회원 목록 페이지 실행 테스트
새로운 구조의 페이지 컨트롤러가 잘 동작하는지 테스트해본다. 톰캣을 재시작하고 http://localhost:9999/web06/member/list.do를 요청한다. 이전과 같이 잘 동작한다(그림 1).
참고도서 : https://freelec.co.kr/book/1674/
[열혈강의] 자바 웹 개발 워크북
[열혈강의] 자바 웹 개발 워크북
freelec.co.kr
'교재 실습 > 자바 웹 개발 워크북' 카테고리의 다른 글
82. DI를 이용한 빈 의존성 관리 (1) (0) | 2022.07.31 |
---|---|
81. 페이지 컨트롤러의 진화 (3) (0) | 2022.07.30 |
79. 페이지 컨트롤러의 진화 (1) (0) | 2022.07.27 |
78. 프런트 컨트롤러의 도입 (4) (0) | 2022.07.26 |
77. 프런트 컨트롤러의 도입 (3) (0) | 2022.07.25 |
댓글