룽쓰의 개발도구

MVC 모델을 구현하자 part1 본문

CODE_ZIP/MVC

MVC 모델을 구현하자 part1

디벨로퍼룽쓰 2021. 4. 26. 18:03

지금 적어두지 않으면 많은 것을 잊어먹을 것 같아서 두서없이 적으려고 한다.

적으면서 기억하자.

 

[ A. ControllerUsingURI.java ]

package mvc.controller;

import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import mvc.command.CommandHandler;
import mvc.command.NullHandler;


// 클래스 안에서 서블릿의 내용을 사용하기 위해서 상속받았다.
public class ControllerUsingURI extends HttpServlet{

	// 1. 가장 많이 사용하는 것이 Map계열이다. Map은 키와 밸류를 설정할 수 있다.
	// key는 String으로 두고 value는 인터페이스로 넣었다. 변수는 commandHandlerMap을 설정했다.
	private Map<String, CommandHandler> commandHandlerMap = new HashMap<>();
    // 이 클래스 안에서만 사용할 수 있는 commandHandlerMap을 사용할 것이다.
	
	// 2. init()이름을 가진 메서드를 구현했다.
	public void init() throws ServletException{
    
		// 3. web.xml에 설정해둔 init param인 configFile을 들고온다.
        // configFile의 값으로는 /WEB-INF/commandHandler.properties가 있다.
        // 즉, 그 안의 값인 키와 밸류 값을 configFile 변수에 다 넣게 됐다.
		String configFile = getInitParameter("configFile");
        
        // 새로운 프로퍼티 객체를 만들었다.
		Properties prop = new Properties();
        
        // 4. getServletContext()에서 getRealPath를 사용한 것을 볼 수 있는데, 이는 서블릿의 가상 디렉토리 상에서 실제 경로를 읽어오는 것을 말한다.
		// 즉, configFile에는 /WEB-INF/commandHandler.properties 주소 값이 저장되어있기 때문에 그것을 불러와서 configFilePath에 집어넣는 것이다.
		String configFilePath = getServletContext().getRealPath(configFile);

		// 5. 특정 경로에 있는 파일을 읽어올 수 있는 FileReader를 사용해서 configFilePath에 있는 파일을 fis에 담았다.
		try(FileReader fis = new FileReader(configFilePath)){

		// 6. 프로퍼티스 확장자기 때문에, 위에서 만든 properties의 prop로 load했다.
        	prop.load(fis);
		}catch(IOException e) {
			throw new ServletException(e);
		}
        
        // 7. prop로 불러온 properties파일 안에 있는 key를 반복자(Iterator) keyIter에 삽입했다.
		Iterator keyIter = prop.keySet().iterator();
        
        // 8. 반복문을 출력할 수 있는 while문을 만들었고, keyIter의 다음 값이 있으면 참으로 진행하도록 했다.
		while(keyIter.hasNext()) {
        
        	// 만약 다음 key값이 있어서 참값이라면, 그 키를 받아서 command에 넣을 것이다.
			String command = (String) keyIter.next();
            
            // 그 키를 다시 prop.getProperty("키")에 넣어서 키와 쌍으로 이루는 값을 handlerClassName에 입력했다.
            String handlerClassName = prop.getProperty(command);

            
            // 현재 불러온 properties의 내용
            // /hello.do = mvc.hello.HelloHandler 
			// 즉, hello.do는 키값으로 command에 입력됐고, handlerClassName에는 mvc.hello.HelloHandler가 입력됐다.
            
			try {
            	// 9. mvc.hello.HelloHandler이 위치에 이 클래스가 있으면 handlerClass에 입력한다는 문구를 넣었다.
				Class<?> handlerClass = Class.forName(handlerClassName);
                
               	// 10. CommandHandler 인터페이스를 이용해서 받아온 handlerClass를 구현했다.
                // 여기서 handlerClass는 CommandHandler 인터페이스를 구현시킨 클래스라서 가능하다.
				CommandHandler handlerInstance = (CommandHandler)handlerClass.newInstance();
                
                // 11. 1번에서 만들었던 Map계열의 commandHandlerMap<String, CommandHandler>에 key인 command와 class인 handlerInstance를 입력했다.
				commandHandlerMap.put(command, handlerInstance);
			}catch(ClassNotFoundException | InstantiationException | IllegalAccessException e) {
				throw new ServletException(e);
			}
		}
	}
    
    // 즉, 위의 메소드에서 할 수 있는 것은 기존에는 주소 끝에 page=1이런 식으로 하드코딩된 상태를 주소창에 보여줘야 했었지만,
    // 위와 같이 설계함으로써 내가 properties파일에 입력해둔 키와 밸류에 따라서 간편하게 사용자들이 들어올 수 있는 것을 알 수 잇다.

 

위 코드에서 initparam을 불러왔는데 그 내용을 살펴보자

 

[ B. web.xml ]

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>Ch18_MVC</display-name>
  
  <servlet>
  	<servlet-name>ControllerUsingFile</servlet-name>
  	<servlet-class>mvc.controller.ControllerUsingFile</servlet-class>
  	<init-param>
  		<param-name>configFile</param-name>
  		<param-value>/WEB-INF/commandHandler.properties</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>ControllerUsingFile</servlet-name>
  	<url-pattern>/controllerUsingFile</url-pattern>
  </servlet-mapping>

<!--   <servlet>
  	<servlet-name>SimpleController</servlet-name>
  	<servlet-class>mvc.simple.SimpleController</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>SimpleController</servlet-name>
  	<url-pattern>/simple</url-pattern>
  </servlet-mapping> -->
  
  <servlet>
  	<servlet-name>ControllerUsingURI</servlet-name>
  	<servlet-class>mvc.controller.ControllerUsingURI</servlet-class>
  	<init-param>
  		<param-name>configFile</param-name>
  		<param-value>/WEB-INF/commandHandlerURI.properties</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>ControllerUsingURI</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

 

 

[ C. A의 추가 부분 ]

	// 1. GET, POST방식 둘다 process 메서드를 실행하도록 했다.
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		process(request,response);
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		process(request,response);
	}
	
	private void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// String command = request.getParameter("cmd");
		// 원래는 이렇게 파라미터 값을 받아서 사용했는데 이제 그러지 않을 예정이다.

		// 1. getRequestURI로 현재 위치한 주소값을 받아서 command 변수에 넣었다.
		String command = request.getRequestURI();
		// 현재 command의 값을 출력하면 /Ch18_MVC/*.do가 나오게 된다.
        
		// 커맨드라는 String의 index값이 어떻게 되냐?
		// request.getContextPath가 의미하는 것은 => /Ch18_MVC를 의미한다.
        // 여기서 indexOf란 command에서 getContextPath()가 처음으로 발견되는 위치의 index를 반환하게 된다.
        // 즉, command는 /Ch18_MVC/*.do이고, getContextPath()는 /Ch18_MVC이라서 == 0은 참이 된다.
		if(command.indexOf(request.getContextPath())== 0) {
			
            // substring은 괄호 안에 들어간 문자열 위치부터 끝까지 문자열을 잘라 내는 것이다.
            // 즉, getContextPath()는 /Ch18_MVC인데, 이것의 length를 구했기 때문에 9이고, command에서 9번째의 뒤에서부터 시작이면 /*.do결과 값이 반환되서 command에 입력된다.
			command = command.substring(request.getContextPath().length());
		}
        
        // 위에서 나온 결과로 /*.do를 가진 command를 map에 입력해서 그 값을 handler 변수에 넣었다.
		CommandHandler handler = commandHandlerMap.get(command);
        
        // 만약 그 값이 없으면 Null을 만들고, viewPage도 null로 없다.
		if(handler == null) {
			handler = new NullHandler();
		}
		String viewPage = null;
		try {
			viewPage = handler.process(request, response);
		}catch(Throwable e) {
			throw new ServletException(e);
		}
        
        // 만약 그 값이 null이 아니라는 말은 넘어갈 page가 있다는 것이고,
        // 그 페이지로 현재 받은 request, response를 함께 넘겨준다는 것이다.
		if(viewPage!= null) {
			RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage);
			dispatcher.forward(request, response);
		}
	}
}

 

이 코드는 책에 기재되어 있는 토대로 작성한 코드이다.

지금 해석해두지 않으면 빨리 까먹을 수 있고, 내 것으로 만들기 힘들 것 같아서 작성했다.

언제나 선택의 연속이라고 한다.

오늘 수업 들었던 것을 다시 보는 것도, 아니 수업을 들을 때 마저 집중을 하냐 안 하냐의 선택까지.

조금 더 노력해서 좋은 결과를 얻어야지

 

내 선택으로 나아가자