■ 서블릿 사용자의 요청을 명령어로 전달
-. 모델 2 기반의 MVC 패턴에서 컨트롤러의 역할을 하는 서블릿은 사용자가 어떤 요청을 했는지 분석해야 한다.
즉, 사용자가 어떤 페이지를 요청하였는지 구분할 수 있어야 이에 알맞은 모델의 기능을 수행할 수 있게 된다.
-. MVC 패턴에서 컨트롤러는 사용자가 어떤 요청을 했는지를 명령어를 사용하여 파악한다.
-. 웹 브라우저를 통해서 명령어를 전달하기 위한 방법은 2가지로 구분된다.
① 요청 파라미터로 명령어를 전달하는 방법
② 요청 URI 자체를 명령어로 사용하는 방법
01. 요청 파라미터로 명령어를 전달하는 방법
01) 특정한 이름의 파라미터에 명령어 정보를 담아서 전달
-. 요청 파라미터로 명령어를 전달하는 방법은 특정한 이름의 파라미터에 명령어 정보를 담아서 전달하는 것이다.
CommandController01.java |
packge ch21.controller;
import java.io.IOException;
import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class CommandController01 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { proRequest(request, response); }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { proRequest(request, response); }
protected void proRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Request 객체로부터 사용자의 요청을 파악하는 코드 String cmd = request.getParameter("cmd"); String view = null; // 사용자의 요청에 따른 알맞은 작업을 처리하는 코드 if(cmd.equals("list")) { request.setAttribute("message", "글목록 보기"); // 글 목록 보기 작업 수행 view = "/ch21/list.jsp"; } else if(cmd.equals("write")) { request.setAttribute("message", "글 쓰기"); // 글 쓰기 수행 view = "/ch21/write.jsp"; } RequestDispatcher dispatcher = request.getRequestDispatcher(view); dispatcher.forward(request, response); } } |
설명 |
-. 파라미터의 이름을 cmd로 하여 명령어를 전달한 것이다.(파라미터 이름은 다른 이름을 사용하여도 상관없다.) -. 컨트롤러 역할을 하는 서블릿의 이름을 CommandController01 라고 한다면 파라미터 cmd에 저장된 값에 따라 해당 명령어를 수행하도록 한다. |
list.jsp |
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>글 목록</title> </head> <body> <%-- message 속성의 값을 msg 변수에 저장한다.--%> <c:set var = "msg" value = "${message}" />
<%-- msg 변수의 내용을 화면에 출력한다. --%> <c:out value = "${msg}" /> </body> </html> |
write.jsp |
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>글 목록</title> </head> <body> <%-- message 속성의 값을 msg 변수에 저장한다.--%> <c:set var = "msg" value = "${message}" />
<%-- msg 변수의 내용을 화면에 출력한다. --%> <c:out value = "${msg}" /> </body> </html> |
출력결과① |
|
출력결과② |
|
02) 커맨드(Command) 패턴으로 작업
-. 각 명령어에 따른 로직 처리 코드를 별도의 클래스로 작성하는 커맨드(Command) 패턴으로 작업을 처리할 수 있다.
-. 커맨드 패턴으로 명령어를 처리하기 위해서는 명령 처리 클래스가 있어야 하는데 이는 요청 파라미터를
동일한 메소드로 처리하도록 하기 위해서 인터페이스를 슈퍼 클래스로 갖도록 한다.
-. 다음은 명령 처리 클래스의 슈퍼 클래스로 사용할 인터페이스를 정의한 문장이다.
CommandAction.java |
package ch21.controller;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public interface CommandAction {
public String proRequest(HttpServletRequest request, HttpServletResponse response) throws Throwable; }
|
ListAction.java
|
package ch21.controller;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class ListAction implements CommandAction {
public String proRequest(HttpServletRequest request, HttpServletResponse response) throws Throwable { request.setAttribute("message", "글 목록 보기");
// ListAction 명령 처리 객체는 뷰 페이지로 "/ch21/list.jsp"를 리턴하고 있다. return "/ch21/list.jsp"; } }
|
WriteAction.java |
package ch21.controller;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class WriteAction implements CommandAction {
public String proRequest(HttpServletRequest request, HttpServletResponse response) throws Throwable { request.setAttribute("message", "글 목록 보기"); // WriteAction 명령 처리 객체는 뷰 페이지로 "/ch21/write.jsp"를 리턴하고 있다. return "/ch21/write.jsp"; } }
|
CommandController02.java |
package ch21.controller;
import java.io.IOException; import java.util.HashMap; import java.util.Map;
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;
public class CommandController02 extends HttpServlet {
// 명령어와 명령 처리 클래스를 쌍으로 저장할 HashMap 객체를 생성한다. private Map commandMap = new HashMap();
// init 메소드는 서블릿이 실행될 때 가장 먼저 단 한번 자동으로 실행된느 메소드이다. // init 메소드에서는 명령어와 명령 처리 핸들러의 인터페이스를 HashMap인 commandMap에 키와 값으로 매핑해 둔다. public void init(ServletConfig config) throws ServletException { Object handlerInstance; String command; // list란 명령어를 키로 ListAction 명령 처리 핸들러 인터페이스를 값으로 해쉬맵에 저장한다. command = "list"; handlerInstance = new ListAction(); commandMap.put(command, handlerInstance); // write란 명령어를 키로 WriteAction 명령 처리 핸들러 인터페이스를 값으로 해쉬맵에 저장한다. command = "write"; handlerInstance = new WriteAction(); commandMap.put(command, handlerInstance); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { proRequest(request, response); }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { proRequest(request, response); }
protected void proRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 사용자의 요청을 분석한다. 즉 "..CommandController02?cmd=list"와 같이
// cmd란 쿼리스트링에 list란 값이 저장되어 있다면 cmd 변수에 list를 저장해 둔다. String cmd = request.getParameter("cmd"); String view = null; CommandAction com = null; try { // 문자열 변수 cmd에 저장된 값을 키로 하여 해쉬 맵에서 이에 해당되는 값을 얻어온다. // 즉 cmd에 저장된 값이 "list"이면 ListAction 명령 처리 핸들러 레퍼런스가 얻어진다. // CommandAction 인터페이스로 선언된 레퍼런스인 com에 저장한다. com = (CommandAction) commandMap.get(cmd); // 해쉬 맵에서 얻어온 명령 처리 핸들러로 proRequest 메소드를 호출한다. // 만일 com에 ListAtion 레퍼런스 값이 저장되어 있다면 이 메소드 내에 기술된 비즈니스 로직이 수행된 후 // 리턴값인 "ch21/list.jsp"가 view에 저장된 값은 proRequest 메소드의 리턴값은 포워딩될 JSP 페이지다. view = com.proRequest(request, response); } catch(Throwable e) { e.printStackTrace(); } // 해당 뷰로 포워딩 하기 위해서 RequestDispatcher 객체를 생성하여 포워딩 한다. RequestDispatcher dispatcher = request.getRequestDispatcher(view); dispatcher.forward(request, response); } }
|
출력결과①
|
|
출력결과② |
|
03) 설정 파일 명령어 정보를 담아서 전달
① 설정파일에 명령어와 클래스의 관계 명시하기
-. CommandController02 컨트롤러 서블릿은 명령어와 명령 처리 클래스를 요청이 있을 때 직접 매핑해 주었지만,
일반적으로 명령어에 따라 처리할 서블릿 클래스가 달라질 수 있도록 하기 위해서 따로 설정 파일을 작성한다.
-. WEB_INF 폴더에 파일명을 Command.properties로 하여 명령어와 이에 대응하는 명령어처리 클래스를 매핑시킨다.
Command.properties |
list = ch21.controller.ListAction write = ch21.controller.WriteAction
|
-. <명령어, 명령어 처리 클래스>와 같은 매핑 구조로 설정 파일에 저장해 두면 새로운 명령어가 추가될 경우 명령어와
이를 처리할 명령어 핸들러 클래스를 설정 파일에 추가만 시키면 되므로 쉽게 매핑시킬 수 있다는 장점이 있다.
② 설정 파일의 정보를 web.xml 파일에 저장하기
-. Command.properties 파일을 컨트롤러인 CommandController에서 읽어올 수 있도록 설정 파일의 정보를 web.xml 파일에 기록한다.
-. web.xml 파일에 아래의 문장을 추가해야 한다. 이때 주의할 점은 반드시 이 문장이
<web_app> 태그 바로 다음에 제일 먼저 나와야 한다는 점이다.
web.xml |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<!-- 요청 파라미터로 명령어를 전달하는 방법 -->
<!-- <servlet> 태그는 서블릿의 설정 정보를 처리할 수 있도록 한다. -->
<servlet> <!-- <servlet-name> 태그에는 서블릿명을 기술한다. --> <!-- 이 태그를 사용하면 해당 서블릿이 ch21.controller.CommandController에 위치하더라도 --> <!-- http://localhost:8181/ch21/servlet/ch21.controller.CommandController 대신 --> <!-- http://localhost:8181/ch21/servlet/CommandController로 기술할 수 있다. --> <servlet-name>CommandController</servlet-name> <!-- <servlet-class> 태그에는 실제 서블릿 경로를 기술해 준다. --> <servlet-class>ch21.controller.CommandController</servlet-class> <!-- <init-param> 태그는 initial parameter를 설정한느 부분으로 --> <!-- 이 태그는 config.getInitParameter("configFile") 메소드를 통해 --> <!-- 파라미터의 이름을 읽어들여 파라미터의 값을 얻어낸다. --> <init-param> <!-- <param-name> 태그에는 config.getInitParameter 메소드의 전달인자로 --> <!-- 사용할 파라미터의 이름을 기술한다. --> <param-name>configFile</param-name> <!-- <param-value>는 설정 파일인 Command.properties 파일의 경로명을 기술한다. --> <param-value>C:\Workspace\ch21\WebContent\WEB-INF\Command.properties</param-value> </init-param> </servlet>
~~~~~~~~
</web-app>
|
③ 컨트롤러 서블릿 작성하기
-. 컨트롤러 서블릿은 명령어와 이를 처리할 클래스가 매핑되어 있는 properties 파일에서 읽어 온다.
-. 명령어와 명령 처리 클래스를 읽어와서 명령 처리 클래스로 인스턴스를 생성하여
HashMap 객체에 명령어를 키로 명령 처리 인스턴스에 대한 레퍼런스를 값으로 저장해 둔다.
-. proRequest 메소드에서 명령어에 따른 명령 처리 인스턴스를 접근해서 해당 비즈니스 로직을 수행한 후
뷰 페이지를 알아내서 해당 페이지로 포워딩 한다.
CommandController.java |
package ch21.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;
public class CommandController extends HttpServlet { // 명령어와 명령 처리 클래스를 쌍으로 저장할 해쉬 맵 객체 생성 private Map commandMap = new HashMap(); public void init(ServletConfig config) throws ServletException { // web.xml 파일의 configFile 태그의 초기화 파라미터(init-param)로 부터 // 매핑정보를 저장하고 있는 설정 파일(Command.properties)의 경로를 구한다. String configFile = config.getInitParameter("configFile"); if(configFile == null) { configFile = "C:/Workspace/ch21/WebContent/WEB-INF/Command.properties"; } // 명령어와 이를 처리할 클래스의 매핑 정보를 저장할 Properties 객체 생성 Properties prop = new Properties(); FileInputStream fis = null; try { // 설정 파일(Command.properties)로 부터 파일 내용(매핑 정보)을 읽어온다. fis = new FileInputStream(configFile); // Command.properties 파일의 정보를 Properties 객체에 저장 prop.load(fis); } catch(IOException e) { throw new ServletException(e); } finally { if(fis != null) { try { fis.close(); } catch(IOException e) { e.printStackTrace(); } } } // Properties 객체에서 키값들을 Set으로 얻어와서 iterator 메소드를 호출한다. Iterator keyIter = prop.keySet().iterator();
// Iterator 객체로 다음 명령어가 있는지 판단해서 있다면 명령어를 얻어옴 while(keyIter.hasNext()) { String command = (String)keyIter.next(); // 해당 명령어랑 매핑된 클래스 이름을 문자열 형태로 얻어옴 String className = prop.getProperty(command); try { // 클래스의 이름이 문자열 형식이므로 이에 해당되는 클래스 생성 Class handlerClass = Class.forName(className); // 해당 클래스로 객체 생성 Object handlerInstance = handlerClass.newInstance(); // <명령어, 핸들러 인스턴스>의 매핑정보를 해쉬맵에 저장한다. commandMap.put(command, handlerInstance); } catch(ClassNotFoundException e) { throw new ServletException(e); } catch(InstantiationException e) { throw new ServletException(e); } catch(IllegalAccessException e) { throw new ServletException(e); } } } // GET 방식으로 요청을 받음 : proRequest 메소드로 전달 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { proRequest(request, response); } // GET 방식으로 요청을 받음 : proRequest 메소드로 전달 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { proRequest(request, response); } protected void proRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Request 객체로부터 사용자의 요청을 파악하는 코드 String cmd = request.getParameter("cmd"); CommandAction com = null; // 사용자의 요청에 따른 알맞은 작업을 처리하는 코드 String view = null; try { // 사용자의 요청인 명령어가 저장된 cmd 변수로 이에 해당되는 명령 처리 com = (CommandAction) commandMap.get(cmd); // 인스턴스의 레퍼런스 값을 얻어와서 해당 명령처리 클래스의 proRequest 메소드를 수행 후 // 포워딩 할 JSP 페이지를 리턴값으로 얻어와 view에 저장한다. view = com.proRequest(request, response); } catch(Throwable e) { throw new ServletException(e); } // RequestDispatcher를 사용하여 알맞은 뷰(JSP 페이지)로 포워딩 RequestDispatcher dispatcher = request.getRequestDispatcher(view); dispatcher.forward(request, response); } }
|
출력결과① |
|
출력결과② |
|