Controller에서 나눠서 원하는 클래스를 호출해주는 작업을 할 때, if문을 사용하지 않고 Map 객체를 이용하면 속도도 향상시킬 수 있고, 가독성이 더 좋아진다.
기존 ControllerServlet 코드
package xyz.itwill.mvc;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//@WebServlet("*.do")
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("ControllerServlet 클래스의 service() 메소드 호출");
//HttpServletRequest.getRequestURI() : 요청 URL 주소에서 URI 주소를 반환하는 메소드
String requestURI=request.getRequestURI();
//System.out.println("requestURI = "+requestURI);//requestURI = /mvc/XXX.do
//HttpServletRequest.getContextPath() : 요청 URL 주소에서 컨텍스트 경로를 반환하는 메소드
String contextPath=request.getContextPath();
//System.out.println("contextPath = "+contextPath);//contextPath = /mvc
String command=requestURI.substring(contextPath.length());
//System.out.println("command = "+command);//command = /XXX.do
//모델 역할의 클래스가 상속받기 위한 인터페이스로 참조변수 생성
// => 인터페이스로 생성된 참조변수에는 인스페이스를 상속받은 모든 자식클래스(모델)의 객체 저장 가능
Action action=null;
//클라이언트 요청정보를 구분하여 요청을 처리하기 위한 모델 역할의 클래스로 객체를
//생성하여 인터페이스 참조변수에 저장
if(command.equals("/loginform.do")) {
action=new LoginFormModel();
} else if(command.equals("/login.do")) {
action=new LoginModel();
} else if(command.equals("/logout.do")) {
action=new LogoutModel();
} else if(command.equals("/writeform.do")) {
action=new WriteFormModel();
} else if(command.equals("/write.do")) {
action=new WriteModel();
} else if(command.equals("/list.do")) {
action=new ListModel();
} else if(command.equals("/view.do")) {
action=new ViewModel();
} else if(command.equals("/modifyform.do")) {
action=new ModifyFormModel();
} else if(command.equals("/modify.do")) {
action=new ModifyModel();
} else if(command.equals("/remove.do")) {
action=new RemoveModel();
} else if(command.equals("/error.do")) {
action=new ErrorModel();
} else {//클라이언트 요청에 대한 모델 역할의 클래스가 없는 경우
action=new ErrorModel();
}
//인터페이스 참조변수로 추상메소드를 호출하면 참조변수에 저장된 모델 객체의 요청
//처리 메소드를 호출하고 뷰 관련 정보를 반환받아 저장 - 오버라이드의 의한 다형성
ActionForward actionForward=action.execute(request, response);
//응답 관련 정보가 저장된 ActionForward 객체를 이용하여 응답 처리
if(actionForward.isForward()) {//ActionForward 객체의 forward 필드값이 [true]인 경우
//JSP 문서로 포워드 이동하여 JSP 문서의 실행결과(HTML)로 클라이언트에게 전달하여 응답 처리
request.getRequestDispatcher(actionForward.getPath()).forward(request, response);
} else {//ActionForward 객체의 forward 필드값이 [false]인 경우
//서블릿에서 클라이언트에게 URL 주소(/XXX.do)를 전달하여 응답 처리
// => URL 주소를 전달받은 클라이언트는 브라우저의 URL 주소를 변경하여 서블릿 요청
response.sendRedirect(actionForward.getPath());
}
}
}
Map 객체 이용
package xyz.itwill.mvc;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//@WebServlet("*.do")
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
//여러개의 엔트리(Entry)를 추가할 수 있는 Map 객체를 저장하기 위한 필드
// => 하나의 엔트리는 요청정보(Key - String)와 모델객체(Value - Action)를 이용하여 생성
// => Map 객체를 사용하면 요청정보(Key)를 이용하여 모델객체(Value)를 빠르게 제공받아 사용
private Map<String, Action> actionMap = new HashMap<>();
//클라이언트가 서블릿(컨트롤러)을 최초로 요청할 경우 서블릿 클래스를 객체로 생성한 후
//가장 먼저 한번만 호출되는 메소드 - 생성자 대신 초기화 작업을 실행하기 위한 메소드
@Override
public void init() throws ServletException {
//Map 객체에 엔트리(Entry) 추가
// => 모델 객체를 하나만 생성하여 제공 - 메모리 효율 증가
actionMap.put("/loginform.do", new LoginFormModel());
actionMap.put("/login.do", new LoginModel());
actionMap.put("/logout.do", new LogoutModel());
actionMap.put("/writeform.do", new WriteFormModel());
actionMap.put("/wirte.do", new WriteModel());
actionMap.put("/list.do", new ListModel());
actionMap.put("/view.do", new ViewModel());
actionMap.put("/modifyform.do", new ModifyFormModel());
actionMap.put("/modify.do", new ModifyModel());
actionMap.put("/remove.do", new RemoveModel());
actionMap.put("/error.do", new ErrorModel());
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI=request.getRequestURI();
//System.out.println("requestURI = "+requestURI);//requestURI = /mvc/XXX.do
//HttpServletRequest.getContextPath() : 요청 URL 주소에서 컨텍스트 경로를 반환하는 메소드
String contextPath=request.getContextPath();
//System.out.println("contextPath = "+contextPath);//contextPath = /mvc
String command=requestURI.substring(contextPath.length());
//System.out.println("command = "+command);//command = /XXX.do
//Map 객체에 저장된 엔트리에서 요청정보(Key)를 이용하여 모델객체를 반환받아 저장
// => 가독성 증가
Action action=actionMap.get(command);
if(action==null) {
action=actionMap.get("/error.do");
}
//인터페이스 참조변수로 추상메소드를 호출하면 참조변수에 저장된 모델 객체의 요청
//처리 메소드를 호출하고 뷰 관련 정보를 반환받아 저장 - 오버라이드에 의한 다형성
ActionForward actionForward=action.execute(request, response);
//응답 관련 정보가 저장된 ActionForward 객체를 이용하여 응답 처리
if(actionForward.isForward()) {//ActionForward 객체의 forward 필드값이 [true]인 경우
//JSP 문서로 포워드 이동하여 JSP 문서의 실행결과(HTML)로 클라이언트에게 전달하여 응답 처리
request.getRequestDispatcher(actionForward.getPath()).forward(request, response);
} else {//ActionForward 객체의 forward 필드값이 [false]인 경우
//서블릿에서 클라이언트에게 URL 주소(/XXX.do)를 전달하여 응답 처리
// => URL 주소를 전달받은 클라이언트는 브라우저의 URL 주소를 변경하여 서블릿 요청
response.sendRedirect(actionForward.getPath());
}
}
}
[web.xml] 에서 load-on-startup 엘리먼트를 이용해 속도를 향상시킬 수 있다.
load-on-startup : 클라이언트 요청 없이 서블릿 클래스를 객체로 생성하기 위한 엘리먼트
→ WAS 프로그램이 실행될 때 서블릿 객체를 미리 생성하기 때문에, 서블릿 객체가 생성되고 init() 메소드가 자동으로 호출되어 초기화 작업을 실행한다.
→ 엘리먼트의 값은 0 이상의 정수값으로 설정하며 정수값이 작을수록 먼저 서블릿 객체로 생성한다.
<servlet>
<load-on-startup>1</load-on-startup>
</servlet>
요청정보에 대한 모델이 늘어날때마다 컨트롤러를 수정해주어야 하는 불편함이 생길 수 있다.
이 때 Properties 파일에 요청정보와 모델 클래스를 저장하고 Properties 파일을 읽어 Map 객체의 엔트리로 추가할 수 있도록 만들어 줄 수 있다.
이렇게 하면 컨트롤러를 변경하지 않고 Properties 파일만 변경하여 요청정보에 대한 모델 객체를 관리할 수 있기 때문에 유지보수의 효율성이 증가한다.
Properties 파일(XXX.properties) : 프로그램 실행에 필요한 값을 제공하기 위한 텍스트 파일

[model.properties]
#Command(Key) = ModelClass(Value)
/loginform.do = xyz.itwill.mvc.LoginFormModel
/login.do = xyz.itwill.mvc.LoginModel
/logout.do = xyz.itwill.mvc.LogoutModel
/writeform.do = xyz.itwill.mvc.WriteFormModel
/write.do = xyz.itwill.mvc.WriteModel
/list.do = xyz.itwill.mvc.ListModel
/view.do = xyz.itwill.mvc.ViewModel
/modifyform.do = xyz.itwill.mvc.ModifyFormModel
/modify.do = xyz.itwill.mvc.ModifyModel
/remove.do = xyz.itwill.mvc.RemoveModel
/error.do = xyz.itwill.mvc.ErrorModel
init-param 엘리먼트 내에 properties 파일의 경로 제공
<servlet>
<!-- init-param : 서블릿 클래스에 필요한 값을 제공하기 위한 엘리먼트 -->
<init-param>
<param-name>configFile</param-name>
<param-value>/WEB-INF/model.properties</param-value>
</init-param>
</servlet>
이렇게 하면 properties 파일의 경로 또는 이름이 바뀌어도, [web.xml]에서 내용을 변경하면 되므로 ControllerServlet은 변경하지 않아도 된다.
ControllerServlet 코드
package xyz.itwill.mvc;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//@WebServlet("*.do")
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Map<String, Action> actionMap=new HashMap<>();
//클라이언트가 서블릿(컨트롤러)를 최초로 요청할 경우 서블릿 클래스를 객체로 생성한 후
//가장 먼저 한번만 호출되는 메소드 - 생성자 대신 초기화 작업을 실행하기 위한 메소드
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("ControllerServlet 클래스의 init() 메소드 호출");
//Properties 파일에 요청정보와 모델 클래스를 저장하고 Properties 파일을 읽어 Map 객체의
//엔트리로 추가 - 유지보수의 효율성 증가
// => 컨트롤러를 변경하지 않고 Properties 파일만 변경하여 요청정보에 대한 모델 객체 관리
//Properties 파일(XXX.properties) : 프로그램 실행에 필요한 값을 제공하기 위한 텍스트 파일
//Properties 파일의 내용을 저장하기 위한 Properties 객체 생성
Properties properties=new Properties();
//ServletConfig.getInitParameter(String name) : [web.xml] 파일에서 init-param 엘리먼트로
//제공된 값을 읽어와 반환하는 메소드
String configFile=config.getInitParameter("configFile");
//Properties 파일의 파일 시스템 경로를 반환받아 저장
//String configFilePath=config.getServletContext().getRealPath("/WEB-INF/model.properties");
String configFilePath=config.getServletContext().getRealPath(configFile);
//System.out.println("configFilePath = "+configFilePath);
try {
//Properties 파일의 내용을 읽기 위한 파일 입력스트림 생성
FileInputStream in=new FileInputStream(configFilePath);
//Properties 파일의 내용을 읽어 Properties 객체의 엔트리로 저장
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}
//Properties 객체에 저장된 모든 엔트리의 이름(Key)이 저장된 Set 객체를 반환받아
//for 구문을 이용하여 반복 처리
for(Object key : properties.keySet()) {
//Properties 객체에 저장된 엔트리의 이름(Key)을 반환받아 저장 - 요청정보
String command=(String)key;
//Properties 객체에 저장된 엔트리의 값(Value)을 반환받아 저장 - 모델 클래스
String actionClass=(String)properties.get(key);
try {
//모델 클래스로 모델 객체를 생성하여 저장 - 리플렉션 기능 사용
//리플렉션(Reflection) : 프로그램의 명령 실행시 Class 객체(Clazz)로 객체를
//생성하여 객체의 필드 또는 메소드에 접근하도록 제공하는 기능
//Class.forName(String className) : 매개변수로 전달받은 문자열로 표현된 클래스를
//읽어 메모리에 저장하고 Class 객체(Clazz)를 반환하는 메소드
// => ClassNotFoundException 발생
//Class.getDeclaredConstructor() : Class 객체의 생성자가 저장된 Constructor
//객체를 반환하는 메소드
//Constructor.newInstance() : Constructor 객체에 저장된 생성자를 이용하여
//Object 타입의 객체를 생성하여 반환하는 메소드
Action actionObject=(Action)Class.forName(actionClass).getDeclaredConstructor().newInstance();
//Map 객체에 엔트리(Key : 요청정보, Value : 모델 객체) 추가
actionMap.put(command, actionObject);
} catch (Exception e) {
e.printStackTrace();
}
}
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//HttpServletRequest.getRequestURI() : 요청 URL 주소에서 URI 주소를 반환하는 메소드
String requestURI=request.getRequestURI();
//System.out.println("requestURI = "+requestURI);//requestURI = /mvc/XXX.do
//HttpServletRequest.getContextPath() : 요청 URL 주소에서 컨텍스트 경로를 반환하는 메소드
String contextPath=request.getContextPath();
//System.out.println("contextPath = "+contextPath);//contextPath = /mvc
String command=requestURI.substring(contextPath.length());
//System.out.println("command = "+command);//command = /XXX.do
//Map 객체에 저장된 엔트리에서 요청정보(Key)를 이용하여 모델객체(Value)를 반환받아 저장
// => 가독성 증가
Action action=actionMap.get(command);
if(action==null) {
action=actionMap.get("/error.do");
}
//인터페이스 참조변수로 추상메소드를 호출하면 참조변수에 저장된 모델 객체의 요청
//처리 메소드를 호출하고 뷰 관련 정보를 반환받아 저장 - 오버라이드의 의한 다형성
ActionForward actionForward=action.execute(request, response);
if(actionForward.isForward()) {//ActionForward 객체의 forward 필드값이 [true]인 경우
//JSP 문서로 포워드 이동하여 JSP 문서의 실행결과(HTML)로 클라이언트에게 전달하여 응답 처리
request.getRequestDispatcher(actionForward.getPath()).forward(request, response);
} else {//ActionForward 객체의 forward 필드값이 [false]인 경우
//서블릿에서 클라이언트에게 URL 주소(/XXX.do)를 전달하여 응답 처리
// => URL 주소를 전달받은 클라이언트는 브라우저의 URL 주소를 변경하여 서블릿 요청
response.sendRedirect(actionForward.getPath());
}
}
}
'학원 > 복기' 카테고리의 다른 글
[MVC] EL 연산자에서의 [] 사용 (0) | 2023.07.13 |
---|---|
[MVC] EL(expression Language) (0) | 2023.07.12 |
[MVC] Model2 (0) | 2023.07.11 |
[MVC] MVC Model1 / MVC Model2 (0) | 2023.07.09 |
[AJAX] RSS 서비스 활용하기 (0) | 2023.07.09 |