본문 바로가기
Dev/Java

MVC - 커맨드 패턴을 이용한 명령어 처리 분리

by vellahw 2022. 10. 4.

 

https://tavi.tistory.com/50?category=1086720 

 

MVC 패턴 정의

0. 모델 1 구조 모델 1 구조는 JSP를 이용한 단순한 모델이다. (기존에 했던 모든 것들..) JSP에서 요청 처리 및 뷰 생성을 처리해서 구현이 쉽지만 요청 처리 및 뷰 생성 코드가 뒤섞여 코드가 복잡

tavi.tistory.com


 

컨트롤러가 알맞은 로직을 수행하려면 클라이언트가 어떤 기능을 요청하는 지 구분할 수 있어야 하는데

웹 브라우저를 통해 명령어를 전달하는 방법에는 두 가지가 있다.

  1. 특정 이름의 파라미터에 명령어 정보를 전달 (ex. url에 ?type=date 를 입력)
  2. 요청 URI 자체를 명령어로 사용

 

특정 파라미터에 명령어 정보를 전달하는 방법은 컨트롤러 서블릿이 명렁어에 알맞은 로직 코드를 실행한 후 결과를 보여줄 뷰로 이동 해야 한다. 이때 명령어와 관련된 로직의 처리를 컨트롤러 서블릿에서 모두 다 처리 하게 되는데 컨트롤러 서블릿의 코드가 복잡해진다는 단점이 있다.

이런 복잡함을 줄일 수 있는 방법으로 커맨드 패턴을 사용한다.

 

 

💡. 커맨드 패턴을 이용한 요청 처리

커맨드 패턴이란 클라이언트의 각 요청을 처리하는 별도 클래스를 제공하는 구현 패턴을 말한다.

하나의 명령어를 하나의 클래스가 처리하게 된다.

명령어를 처리하는 클래스들은 공통의 부모 클래스를 상속 받거나 공통의 인터페이스를 구현하도록 함으로써 동일한 메소드로 실행될 수 있게 된다.

동일한 인터페이스 구현 예시

 

 

🎈.커맨드 패턴을 이용한 명령어 처리 분리 예제

1. 공통 인터페이스

package mvc.command;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface CommandHandler {
	//추상 메소드
	public String process(HttpServletRequest request, HttpServletResponse response)
		throws Throwable; 
}

 

2. 명령어를 처리하는 핸들러 클래스

(1)

package mvc.command;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloHandler implements CommandHandler {
	@Override
	public String process(HttpServletRequest request, HttpServletResponse response)
			throws Throwable {
		//1. 뷰 페이지에서 요청 처리 결과를 보여주기 위해 사용할 정보 저장
		request.setAttribute("hello", "안녕하세요!");
		//2. 처리 결과를 보여줄 뷰 페이지의 URI 리턴
		return "/view/hello.jsp";
	}
}

(2)

package mvc.command;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class NullHandler implements CommandHandler {

	@Override
	public String process(HttpServletRequest request, HttpServletResponse response)
			throws Throwable {
		return "/view/nullCommand.jsp";
	}
}

 

3. 매핑 정보 설정 파일 작성 

(1) 명령어 매핑 정보 properties 파일 ('명렁어=핸들러 클래스의 이름(패키지명 포함)' 의 형태)

hello=mvc.command.HelloHandler

 ▲ commandHandler.properties 

명령어 'hello'를 처리하는 mvc.command.HelloHandler 클래스

(2) web.xml 파일에 서블릿 설정 추가

<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>
</servlet>
<servlet-mapping>
		<servlet-name>ControllerUsingFile</servlet-name>
		<url-pattern>/controllerUsingFile</url-pattern>
</servlet-mapping>
  • configFile이란 이름으로 /WEB-INF/commandHandler.properties 파일로부터 설정 정보를 읽어옴

 

4. 컨트롤러 서블릿

컨트롤러 서블릿에서 설정 파일을 읽어오기 가장 좋은 위치는 init() 메소드인데 init() 메소드는 서블릿을 생성한 후 초기화 할 때 호출되는 메소드이다. 

package mvc.controller;

import java.io.FileInputStream;
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.ServletConfig;
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;

//커맨드 패턴 구현 - 컨트롤러 서블릿
//process 메소드가 있는 컨트롤러 서블릿

public class ControllerUsingFile extends HttpServlet {
	//매핑 정보 저장 <커맨드, 핸들러인스턴스>
	private Map<String, Object> commandHandlerMap = new HashMap<String, Object>();
	
	//설정 파일을 읽어오는 init() 메소드
	public void init(ServletConfig config) throws ServletException {
		//web.xml에 저장한 configFile 초기화 파라미터로부터 설정 파일의 경로 구함
		String configFile = config.getInitParameter("configFile");
		Properties prop = new Properties();
		FileInputStream fis = null;
		
		//설정 파일로부터 매핑 정보를 읽어와 Properties 객체에 저장
		try {
			//받아온 상대경로를 절대 경로로 바꿔줌
			String configFilePath = config.getServletContext().getRealPath(configFile);
			fis = new FileInputStream(configFilePath);
			prop.load(fis); //프로퍼티스 로딩
		} catch (IOException e) {
			throw new ServletException(e);
		} finally {
			if(fis != null)
				try {
					fis.close();
				} catch (IOException e2) {}
		}
	
		//Iterator 객체 생성
		Iterator keyIter = prop.keySet().iterator(); //키값 받아옴
		//반복
		while (keyIter.hasNext()) {
			String command = (String) keyIter.next();
			String handlerClassName = prop.getProperty(command);
			
			try {
				//문자열을 클래스로 변환
				Class handlerClass = Class.forName(handlerClassName);
				Object handlerInstance = handlerClass.newInstance();
				commandHandlerMap.put(command, handlerInstance);
			} catch (ClassNotFoundException e) {
				throw new ServletException(e);
			} catch (InstantiationException e) {
				throw new ServletException(e);
			} catch (IllegalAccessException e) {
				throw new ServletException(e);
			}
		}
	}
	
	//메소드 오버라이드1
	public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		process(request, response);
	}

	//메소드 오버라이드2
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		process(request, response);
	}
	
	//인터페이스의 process 메소드 사용
	public void process(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//"cmd" 파라미터를 이용해 클라이언트가 요청한 기능 구함
		String command = request.getParameter("cmd");
		//요청에 사용될 핸들러 인스턴스를 commandHandlerMap으로부터 구함
		CommandHandler handler = (CommandHandler) commandHandlerMap.get(command);
	
		if(handler == null) {
			handler = new NullHandler(); 
		}
		
		String viewPage = null;
		try {
			//구한 핸들러 인스턴스의 process메소드를 호출해 요청을 처리
			viewPage = handler.process(request, response);
		} catch (Throwable e) {
			throw new ServletException(e);
		}
		
		//파라미터 정보 유지를 위한 RequestDispatcher
		RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage);
		dispatcher.forward(request, response);
	}
}

 

5. 뷰 역할의 JSP 작성
(1) /view/hello.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
<%= request.getAttribute("hello") %>
</body>
</html>

 

(2) /view/nullCommand.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<html>
<head>
<title>에러</title>
</head>
<body>
잘못된 요청입니다.
</body>
</html>

 

6. 실행 결과

 

 

댓글