본문 바로가기
교재 실습/자바 웹 개발 워크북

87. 리플랙션 API를 이용하여 프런트 컨트롤러 개선하기 (3)

by Jint 2022. 8. 6.

4. 프런트 컨트롤러의 변경

페이지 컨트롤러를 변경했으니, 이제는 프런트 컨트롤러를 변경한다. spms.servlets.DsipatcherServlet 클래스를 다음과 같이 편집한다.

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.bind.DataBinding;
import spms.bind.ServletRequestDataBinder;
import spms.controls.Controller;
import spms.controls.LogInController;
import spms.controls.LogOutController;
import spms.controls.MemberAddController;
import spms.controls.MemberDeleteController;
import spms.controls.MemberListController;
import spms.controls.MemberUpdateController;
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")); //memberDao 객체는 더이상 Map 객체에 담을 필요 없음.
			model.put("session", request.getSession()); //로그인 및 로그아웃 페이지 컨트롤러에서 사용할 세션 객체
			
			//String pageControllerPath = null;
			//인터페이스 타입의 참조 변수 선언 - Controller의 구현체 클래스들 객체 주소 저장 위함
			//Controller pageController = null;
			//ServletContext에서 페이지 컨트롤러 꺼낼 때 서블릿 URL 사용
			Controller pageController = (Controller)sc.getAttribute(servletPath);
			
			//DataBinding 구현 여부 검사하여, 해당 인터페이스를 구현한 경우에만 호출
			if(pageController instanceof DataBinding) {
				prepareRequestData(request, model, (DataBinding)pageController);
			}
			
			//페이지 컨트롤러로 위임
			//조건문 변경 : 페이지 컨트롤러가 사용할 데이터를 준비하는 부분 외 제거
			/*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); //뷰 URL 반환받음
			
			//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);
		}
	}
	
	//페이지 컨트롤러에 필요한 데이터 준비
	private void prepareRequestData(HttpServletRequest request, HashMap<String, Object> model, DataBinding dataBinding) throws Exception {
		//페이지 컨트롤러에게 필요한 데이터 묻기
		Object[] dataBinders = dataBinding.getDataBinders();
		//배열에서 꺼낸 값 보관할 임시 변수
		String dataName = null; //데이터 이름(String)
		Class<?> dataType = null; //데이터 타입(Class)
		Object dataObj = null; //데이터 객체(Object)
		//배열 반복하며 값 꺼내기
		for(int i=0 ; i < dataBinders.length ; i+=2) {
			dataName = (String)dataBinders[i];
			dataType = (Class<?>)dataBinders[i+1];
			dataObj = ServletRequestDataBinder.bind(request, dataType, dataName);
			model.put(dataName, dataObj);
		}
	}
}

 

- service() 메서드

드디어 service() 메서드에서 조건문이 사라졌다. 이제부터는 매개변수 값을 사용하는 페이지 컨트롤러를 추가하더라도 조건문을 삽입할 필요가 없다. 대신 데이터 준비를 자동으로 수행하는 prepareRequestData() 메서드를 호출한다.

//DataBinding 구현 여부 검사하여, 해당 인터페이스를 구현한 경우에만 호출
if(pageController instanceof DataBinding) {
    prepareRequestData(request, model, (DataBinding)pageController);
}

매개변수 값이 필요한 페이지 컨트롤러에 대해 DataBinding 인터페이스를 구현하기로 규칙을 정했다. 따라서 DataBinding을 구현했는지 여부를 검사하여, 해당 인터페이스를 구현한 경우에만 prepareRequestData() 메서드를 호출하여 페이지 컨트롤러를 위한 데이터를 준비했다.

 

- prepareRequestData() 메서드

prepareRequestData() 메서드에서 어떤 방법으로 데이터를 준비하는지 살펴본다. 먼저 페이지 컨트롤러에게 필요한 데이터가 무엇인지 묻는다.

//페이지 컨트롤러에게 필요한 데이터 묻기
Object[] dataBinders = dataBinding.getDataBinders();

getDataBinders() 메서드가 반환하는 것은 ["데이터이름", 데이터타입, "데이터이름", 데이터타입 ...] 으로 나열된 Object 배열일 것이다.

배열을 반복하기 전에 배열에서 꺼낸 값을 보관할 임시 변수를 준비한다. 데이터 이름(String), 데이터 타입(Class), 데이터 객체(Object)를 위한 참조 변수이다.

//배열에서 꺼낸 값 보관할 임시 변수
String dataName = null; //데이터 이름(String)
Class<?> dataType = null; //데이터 타입(Class)
Object dataObj = null; //데이터 객체(Object)

데이터 이름과 데이터 타입을 꺼내기 쉽게 2씩 증가하면서 반복문을 돌린다.

//배열 반복하며 값 꺼내기
for(int i=0 ; i < dataBinders.length ; i+=2) {
    dataName = (String)dataBinders[i];
    dataType = (Class<?>)dataBinders[i+1];
    dataObj = ServletRequestDataBinder.bind(request, dataType, dataName);
    model.put(dataName, dataObj);
}

for문 안을 들여다보면, ServletRequestDataBinder 클래스의 bind() 메서드를 호출하고 있다. 이 메서드는 dataName과 일치하는 요청 매개변수를 찾고 dataType을 통해 해당 클래스의 인스턴스를 생성한다. 찾은 매개변수 값을 인스턴스에 저장하며 그 인스턴스를 반환한다.

dataObj = ServletRequestDataBinder.bind(request, dataType, dataName);
model.put(dataName, dataObj);

bind() 메서드가 반환한 데이터 객체는 Map 객체에 담는다. 이 작업을 통해 페이지 컨트롤러가 사용할 데이터를 준비하는 것이다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

댓글