본문 바로가기
👨‍💻 2. 웹개발_Back end/2-4 JSP & Servlet

[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (2) 페이지 컨트롤러의 진화

by 달님🌙 2021. 10. 18.
반응형

 

6.2 페이지 컨트롤러의 진화

 

1. 페이지 컨트롤러의 진화

 

- 기존의 페이지 컨트롤러는 프런트 컨트롤러와 마찬가지로 서블릿이다.

- 클라이언트 요청은 프런트 컨트롤러가 받기 때문에 페이지 컨트롤러는 서블릿이 될 필요가 없다.

- 페이지 컨트롤러가 서블릿이 아니기 때문에 include 기법으로 실행을 위임할 수 없다.

 

- 페이지 컨트롤러로 실행을 위임할 새로운 규칙이 필요!

- 프런트 컨트롤러와 페이지 컨트롤러 사이에 호출 규칙을 정의한다.

- 기존의 페이지 컨트롤러를 변경해야 한다.

 

- 기존의 MemberListServlet은 Servlet 인터페이스를 구현한 서블릿이다.

- 이제 서블릿 구현을 버리고, Controller 규칙을 따르게 하자.

- 클래스 이름도 그에 맞추어 Controller 접미사를 붙이자.

 

 

2. 클라이언트 요청을 처리하는 과정

 

1) 클라이언트에서 회원 목록을 요청한다.

2) 프런트 컨트롤러는 페이지 컨트롤러를 호출한다.

3) 페이지 컨트롤러는 DAO를 호출한다.

4) DAO는 DBMS에서 회원 정보를 조회하여 회원 목록을 만든다.

5) DAO는 페이지 컨트롤러에게 회원목록을 반환한다.

6) 페이지 컨트롤러는 회원 목록을 파라미터로 받은 맵 객체에 보관한다.

7) 페이지 컨트롤러는 뷰 URL을 프런트 컨트롤러에게 반환한다.

8) 프런트 컨트롤러는 맵개체에 들어 있는 회원 목록을 요청 객체에 보관한다.

9) 프런트 컨트롤러는 뷰 URL로 요청을 위임한다.

10) JSP는 요청 객체에 보관되어 있는 회원 목록을 꺼낸다.

11) JSP는 회원 목록 데이터를 가지고 HTML 화면을 생성한다.

12) JSP 실행이 끝나면, 프런트 컨트롤러는 클라이언트에게 응답한다

 

3. 실습 : 서블릿으로 파일을 일반 자바 클래스로 변경하기 ( _25_MVC_NoServlet )

 

1) Controller 인터페이스 생성

package spms.controller;

import java.util.Map;

/*
 * 1) FrontController에서 PageController 호출 방식 동일화
 * 2) 모든 PageController에서 Servlet을 제거하고 Controller 인터페이스를 상속 받음
 * 
 */

public interface Controller {
	public String execute(Map<String, Object> model) throws Exception;
}

 

2) servlet패키지에 있는 서블릿 파일들을 controller 패키지로 옮기기

 

 

3) LoginController.java

package spms.controller;

import java.util.Map;

import javax.servlet.http.HttpSession;

import spms.dao.MemberDAO;
import spms.vo.Member;

/*
 * 페이지 컨트롤러들이 Servlet이 제거되고 일반 클래스로 변하기 때문에
 * HttpServletRequest, HttpServletResponse 및 ServletContext를 사용할 수 없다
 * execute 메소드의 매개변수로 String Key값을 가지는 객체들을 맵에 담아서 전달
 */
public class LoginController implements Controller {
	@Override
	public String execute(Map<String, Object> model) throws Exception {
		if(model.get("loginInfo") == null) { //model 매개변수에 담아줌
			return "/auth/LoginForm.jsp";
		} else {
			MemberDAO memberDAO = (MemberDAO) model.get("memberDAO");
			Member loginInfo = (Member)model.get("loginInfo");
			
			Member member = memberDAO.exist(loginInfo.getEmail(), loginInfo.getPassword());
		
			if(member != null) {
				//세션이라는 키값을 가지는 HttpSession 모델 객체를 맵에 담아서 전달해줌
				HttpSession session = (HttpSession)model.get("session");
				session.setAttribute("Member", member);
				return "redirect:../member/list.do";
			} else {
				return "/auth/LoginFail.jsp";
			}
		}
	}
}

 

4) MemberListController.java

package spms.controller;

import java.util.Map;

import spms.dao.MemberDAO;

public class MemberListController implements Controller {
	@Override
	public String execute(Map<String, Object> model) throws Exception {
		MemberDAO memberDAO = (MemberDAO)model.get("memberDAO");
		//리턴될 조회 결과 맵에 담음
		model.put("memberList", memberDAO.selectlist());
		
		return "/member/MemberList.jsp";
	}
}

 

5) DispatcherServlet.java

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.controller.Controller;
import spms.controller.LoginController;
import spms.controller.LogoutController;
import spms.controller.MemberAddController;
import spms.controller.MemberDeleteController;
import spms.controller.MemberListController;
import spms.controller.MemberUpdateController;
import spms.vo.Member;

/**
 * Servlet implementation class DispatcherServlet
 */
//모든 .do url 호출에 대한 처리를 프론트컨트롤러인 Dispatcher에서 처리
/*
 * 1) 의미 : tomcat으로부터 바로 제어 컨트롤러를 전송하는 방식
 * 			-> tomcat은 FrontController로 요청을 전송
 * 			   FrontController에서 각 PageController분기해서 처리하도록
 * 			   FrontController : DispatcherServlet
 * 			   BackController(PageController) : 각 페이지 별 서블릿
 * 
 * 2) 이유 : 공통된 중복코드를 한 곳에 모아서 관리하기 편하게 하기 위함 -> 프레임워크의 기초 개념
 *          FrontController만 HttpServlet을 상속받고
 *          PageController들은 일반 자바 클래스로 변환
 *          
 * 3) FrontController의 역할
 *    3-1) 요청에 따른 페이지별 분기 처리
 *    3-2) 페이지 컨트롤러로 전송할 VO 객체 생성
 *    3-3) 서블릿/JSP 페이지 이동 처리
 *    3-4) PageController에서 발생하는 모든 예외 처리
 *    3-5) 신규 페이지나 기존 페이지 관리
 *    
 * 4) 장점
 * 	  4-1) FrontController만 HttpServlet을 상속받기 때문에 다른 WAS로 이식할 때 FrontController만 수정하면 됨
 *    4-2) 브라우저에 노출하고 싶지 않은 페이지 감춤
 *    4-3) 공통역할을 FrontController에 모아놓기 때문에 자동화(Framework화)하기 편함
 * 
 * */
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		
		//분기 처리를 위한 servlet(요청url) 경로 얻음
		String servletPath = request.getServletPath();
		try {
			//서블릿 경로에 따른 페이지컨트롤러 경롤 담을 변수
			Controller pageController = null;
			ServletContext sc = this.getServletContext();
			HashMap<String, Object> model = new HashMap<String, Object>();
			model.put("memberDAO", sc.getAttribute("memberDAO"));
			model.put("session", request.getSession());
			
			if("/member/list.do".equals(servletPath)) {
				pageController = new MemberListController();
			} else if("/member/add.do".equals(servletPath)) {
				pageController = new MemberAddController();
				
				//새로운 입력값이 있으면 doPost로 없으면 doGet 처리
				if(request.getParameter("email") != null) {
					model.put("member", new Member()
												    .setEmail(request.getParameter("email"))
												    .setPassword(request.getParameter("password"))
												    .setName(request.getParameter("name")));
				}
			} else if("/member/update.do".equals(servletPath)) {
				pageController = new MemberUpdateController();
				
				//새로운 입력값이 있으면 doPost로 없으면 doGet 처리
				if(request.getParameter("email") != null) {
					model.put("member", new Member()
													.setNo(Integer.parseInt(request.getParameter("no")))
				   									.setEmail(request.getParameter("email"))
				   									.setName(request.getParameter("name")));
				} else {
					model.put("no", Integer.parseInt(request.getParameter("no")));
				}
			} else if("/member/delete.do".equals(servletPath)) {
				pageController = new MemberDeleteController();
				model.put("no", Integer.parseInt(request.getParameter("no")));
			} else if("/auth/login.do".equals(servletPath)) {
				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)) {
				pageController = new LogoutController();
			}
			
			//POJO Page Controller
			String viewUrl = "";
			if(pageController != null) {
				viewUrl = pageController.execute(model);
				for(String key : model.keySet()) {
					request.setAttribute(key, model.get(key));
				}
			} else {
				System.out.println("주소 대상  Controller를 찾을 수 없습니다.");
			}
			
			//반환받은 값에 redirect가 포함되어 있으면 바로 페이지 이동
			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);
		}
	}
}

 

6) LogoutController.java

package spms.controller;

import java.util.Map;
import javax.servlet.http.HttpSession;

public class LogoutController implements Controller {
	@Override
	public String execute(Map<String, Object> model) throws Exception {
		// TODO Auto-generated method stub
		HttpSession session = (HttpSession)model.get("session");
		//세션 무효화 처리
		session.invalidate();
		
		return "redirect:login.do";
	}

}

 

7) MemberAddController.java

package spms.controller;

import java.util.Map;

import spms.dao.MemberDAO;
import spms.vo.Member;

//HttpServlet은 GenericServlet을 상속받은 클래스
//service() 메소드를 호출방식에 따라 doGet(), doPost(), doPut(), doDelete()로 분리해놓음
public class MemberAddController implements Controller {
	@Override
	public String execute(Map<String, Object> model) throws Exception {
		if(model.get("member") == null) {
			return "/member/MemberFrom.jsp";
		} else {
			MemberDAO memberDAO = (MemberDAO)model.get("memberDAO");
			Member member = (Member)model.get("member");
			memberDAO.insert(member);
			
			return "redirect:list.do";
		}
	}
}

 

8) MemberDeleteController.java

package spms.controller;

import java.util.Map;

import spms.dao.MemberDAO;

public class MemberDeleteController implements Controller {
	
	@Override
	public String execute(Map<String, Object> model) throws Exception {
		MemberDAO memberDAO = (MemberDAO)model.get("memberDAO");
		
		Integer no = (Integer)model.get("no");
		memberDAO.delete(no);
		
		return "redirect:list.do";
	}
}

 

9) MemberUpdateController.java

package spms.controller;

import java.util.Map;

import spms.dao.MemberDAO;
import spms.vo.Member;

public class MemberUpdateController implements Controller {
	@Override
	public String execute(Map<String, Object> model) throws Exception {
		MemberDAO memberDAO = (MemberDAO)model.get("memberDAO");
		
		if(model.get("member") == null) {
			Integer no = (Integer)model.get("no");
			Member member = memberDAO.selectOne(no);
			model.put("member", member);
			return "/member/MemberUpdateForm.jsp";
		} else {
			Member member = (Member)model.get("member");
			memberDAO.update(member);
			return "redirect:list.do";
		}
	}

}

 

10) 실행 화면

 

반응형

댓글