본문 바로가기

학원/복기

[MVC] MVC 컨트롤러 (Map 객체 / load-on-startup 엘리먼트 / properties 파일)

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 생성

[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