: jsp를 이용한 단순한 모델
: 비즈니스 로직을 처리하기 위한 코드와 웹 브라우저에 결과를 출력하는 코드가 섞여있음
: 웹 브라우저의 모든 요청을 하나의 서블릿이 받아 처리함
: 서블릿은 클라이언트의 요청을 처리한 후, 결과를 보여줄 JSP 페이지로 포워딩함
: 포워딩으로 요청 흐름을 받은 JSP 페이지는 결과 화면을 클라이언트에 전송
: 즉, 결과 화면을 보여주는 뷰와 비즈니스 로직 처리가 분리된 MVC 패턴
서블릿은 웹브라우저의 요청에 따라 알맞게 모델을 사용하여 요청 기능을 수행하고 그 결과를 뷰인 jsp에 전달한다.
① 웹 브라우저가 전송한 HTTP 요청을 받는다. 서블릿의 doGet() 메서드나 doPost() 메서드가 호출된다.
② 웹 브라우저가 어떤 기능을 요청했는지 분석한다.
ex)
String type = request.getParameter("type");
③ 모델을 사용해 요청한 기능을 수행한다.
ex)
if("a".equals(type)){ }else if("b".equals(type)){ }
⑤ 모델로부터 전달받은 결가물을 알맞게 가공한 후, request나 session의 setAttribute() 메서드를 사용하여 결과값을 속성에 저장한다. 저장한 결과는 뷰인 jsp에서 사용한다.
ex)
Object resultObject; request.setAttribute("result",resultObject)
⑥ 웹 브라우저에 결과를 전송할 jsp를 선택하여 해당 jsp로 포워딩한다.
ex)
RequestDispatcher dispatcher = request.getRequestDispatcher("./view.jsp"); dispatcher.forward(request, response);
※ 톰캣 서버가 요청의 흐름이 "./view,jsp"로 이어진다고 생각
=> url은 servlet 것인데, 페이지 이름은 jsp의 것
jsp는 웹 브라우저가 요청할 결과를 화면에 보여주는 역할을 할 뿐만 아니라, 웹 브라우저가 지속적으로 컨트롤러에 요청을 보낼 수 있도록 링크나 폼을 제공하는 즉, 웹 브라우저의 요청을 컨트롤러에 전달해주는 매개체의 역할을 한다.
사용자가 어떤 기능을 요구했는지 판단하기 위해 명령어를 사용하는 것이다. 즉, 사용자가 명령을 컨트롤러 서블릿에 전송하면, 서블릿은 명령어에 해당하는 기능을 수행한 후 뷰를 통해 결과를 보여주는 방식을 사용하는 것이다. 이때, 각 명령어에 해당하는 로직 처리 코드를 별도 클래스로 작성하여 하나의 명령어를 하나의 클래스에서 처리하도록 구현한다.
CommandHandler 인터페이스를 이용한 커맨드 패턴이 적용된 servlet 클래스
//[1] 사용자가 요청한 명령어를 가져온다. String command = request.getParmeter("cmd"); //[2] 각 명령어에 맞는 로직을 수행할 클래스를 생성한다. CommandHandler handler = null; if(command==null){ hander = new NullHandler(); }else if("BookList".equals(command)){ hander = new BookListHandler(); }else if("BookWriteForm".equals(command)){ hander = new BookWriteFormHandler(); } //[3] 로직 클래스로 부터 요청 결과를 보여줄 뷰 페이지를 리턴 받는다. String viewPage = handler.process(request, response); //[4] 해당 뷰 페이지로 포워딩한다. RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage); dispatcher.forward(request, response);
CommandHandler 인터페이스
명령어에 해당하는 로직 실행 코드를 담고 있는 핸들러 클래스
앞선 servlet 파일의 코드는 새로운 명령어가 추가되면 클래스의 코드를 직접 변경해야 한다는 단점이 있다. 이 단점을 해결하기 위해 설정 파일에 커맨드와 핸들러 클래스의 관계를 명시하는 것이다.
※ Properties
- Properties 클래스는 프로퍼티(이름, 값) 목록을 파일에서 읽어올 수 있다. 이러한 파일을 프로퍼티 파일이라고도 한다.
Properties prop = new Properties(); prop.setPreoperty("name1","value1"); String name1 = prop.getProperty("name1");
#주석 BookList=mvjsp.command.BookListHandler BookWriteForm=mvjsp.command.BookWriteFormHandler
- load() 메서드를 이용하면 설정 파일로부터 프로퍼티 정보를 읽어올 수 있다.
prop.load(파일)
컨트롤러 서블릿에서 설정 파일을 읽어오기 가장 좋은 위치는 init() 메서드(서블릿을 생성하고 초기화할 때 호출되는 메서드)이다.
//[1] <커맨드, 핸들러인스턴스> 매핑 정보 저장할 map 선언 private Map<String, CommandHandler> commandHandlerMap = new HashMap<>(); public void init() throws ServeltException{/////////init() 메서드 재정의 //[2] web.xml의 init-param에 설정한 설정파일 경로 가져오기 String configFile = getInitParameter("configFile"); //[3] 설정파일의 물리적인 경로 읽어오기 String configFilePath = getServletContext().getRealPath(configFile); //* Properties: (이름, 값) 목록을 갖는 클래스 Properties prop = new Properties(); //[4] (매핑정보)설정파일을 읽어 properties에 로드하기 try(FileInputStream fis = new fileReader(configFilePath)){ prop.load(fis); }catch(IOException e){ throw new ServletException(e); } //[5] porperties의 키값들을 가져와서 와일문 실행 Iterator keyIter = prop.keySet().iterator(); while(keyIter.hasNext()){ //[6] 커맨드와 핸들러 클래스 가져오기 String command = (String)keyIter.next(); String handlerClassName = prop.getProperty(command); try{ //[7] 핸들러 클래스를 등록하고 인스턴스를 얻는다. Class<?> handlerClass = Class.forName(handlerClassName); CommandHandler handlerInstance = (CommandHandler)handlerClass.newInstance(); //[8] map에 <커맨드, 핸들러인스턴스> 매핑 정보를 저장 commandHandlerMap.put(command, handlerInstance); }catch(ClassNotFoundException|InstantiationException |IllegalAccessException e){ throw new ServletException(e); } } }
[따라서] 커맨드에 매핑된 핸들러 클래스를 인스턴스화 할 수 있게 된다.
CommandHandler handler = commandhandlerMap.get(command);
ex) url: /ch17/test.do contextpath: /ch17 String command = request.getRequestURL(); if(command.indexOf(request.getContextPath()==0)){ command = command.substring(request.getContextPath().length()); } // command = /test.do