MVC 패턴

이진아·2020년 5월 6일
0

1. 모델 1 구조

: jsp를 이용한 단순한 모델
: 비즈니스 로직을 처리하기 위한 코드와 웹 브라우저에 결과를 출력하는 코드가 섞여있음


2. 모델 2 구조

: 웹 브라우저의 모든 요청을 하나의 서블릿이 받아 처리함
: 서블릿은 클라이언트의 요청을 처리한 후, 결과를 보여줄 JSP 페이지로 포워딩함
: 포워딩으로 요청 흐름을 받은 JSP 페이지는 결과 화면을 클라이언트에 전송
: 즉, 결과 화면을 보여주는 뷰와 비즈니스 로직 처리가 분리된 MVC 패턴


3. MVC 패턴

1) mvc 패턴의 핵심

  • 비즈니스 로직을 처리하는 모델과 결과 화면을 보여주는 뷰를 분리한다.
  • 어플리케이션의 흐름 제어나 사용자의 요청은 컨트롤러에 집중된다.

2) 장점

  • 모델의 내부 로직이 변경되더라도 뷰는 영향을 받지 않는다.
  • 내부 구현 로직에 상관없이 뷰를 변경할 수 있다.
  • [따라서] 유지보수와 확장이 편리해진다.

3. 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의 것


4. MVC의 뷰: jsp

jsp는 웹 브라우저가 요청할 결과를 화면에 보여주는 역할을 할 뿐만 아니라, 웹 브라우저가 지속적으로 컨트롤러에 요청을 보낼 수 있도록 링크나 폼을 제공하는 즉, 웹 브라우저의 요청을 컨트롤러에 전달해주는 매개체의 역할을 한다.


5. 커맨드 패턴 기반의 코드

사용자가 어떤 기능을 요구했는지 판단하기 위해 명령어를 사용하는 것이다. 즉, 사용자가 명령을 컨트롤러 서블릿에 전송하면, 서블릿은 명령어에 해당하는 기능을 수행한 후 뷰를 통해 결과를 보여주는 방식을 사용하는 것이다. 이때, 각 명령어에 해당하는 로직 처리 코드를 별도 클래스로 작성하여 하나의 명령어를 하나의 클래스에서 처리하도록 구현한다.

  • 특정 이름의 파라미터에 명령어 정보를 전달
  • 요청 URL 자체를 명령어로 사용

1) CommandHandler 인터페이스

  • 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 인터페이스

  • 명령어에 해당하는 로직 실행 코드를 담고 있는 핸들러 클래스

2) 설정 파일: <명령어, 핸들러 클래스> 매핑정보 이용

앞선 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);

3) 요청 URL를 명령어로 사용하기

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

0개의 댓글