Servlet 이해하기 웹 MVC 524730-1 2019 년봄학기 4/29/2019 박경신 Servlet 자바플랫폼에서컴포넌트기반의웹애플리케이션개발기술 JSP는서블릿기술에기반함 Servlet의프리젠테이션문제를해결하기위해 JSP가등장 이로인해웹애플리케이션의유지보수어려움심각. JSP 모델2가주목받으며다시서블릿에대한중요성부각 Servlet 변천 1 서블릿문제점대두 2JSP 등장 3 JSP 스크립트기술의한계 프로그램에서 HTML 핸들링컨텐츠와비즈니스로직이분리되지않음개발과관리의어려움 HTML 에서프로그램핸들링이가능 JSP 스크립팅기술 HTML 에서프로그램핸들링컨텐츠와비즈니스로직이분리되지않음컨텐츠관리는쉬워졌지만프로그램관리는이전보다더복잡해짐 Servlet 이점 웹애플리케이션개발시 Servlet 이점 컨텐츠와비즈니스로직을분리할수있음 컨트롤러와뷰의역할분담 : 웹디자이너와개발자간의원활한공동작업이가능해짐 유지보수가수월함 기능의확장이용이함 개발자가 HTML, 자바스크립트스타일시트등복잡한기술을모두알아야할필요가없음 4MVC 패턴주목받기시작 애플리케이션구성요소단위로역할분담 Model: 자바클래스 (DAO, VO) View: JSP, JSTL Controller: 서블릿
Servlet API Servlet은자바클래스로제작됨 javax.servlet.genericservlet과 javax.servlet.http.httpservlet API 구조 Servlet 구조 HttpServlet 구조 일반적으로서블릿은 javax.servlet.http.httpservlet 을상속 service() 메서드는컨테이너에서호출 doget(), dopost() 메서드를오버라이드해서처리에필요한기능을구현 javax.servlet.genericservlet javax.servlet.http.httpservlet 클라이언트 요청 (request) 응답 (response) MyServlet extends HttpServlet service( ) doget( ) dopost( ) Servlet 구조 GET 방식 서버에있는정보를가져오기위해설계됨 240 바이트까지전달할수있음 QUERY_STRING 환경변수를통해전달 형식 : http://xxx.xxx.co.kr/servlet/login?id=hj&name=hong URL 노출로보안성이요구되는경우엔사용할수없음 검색엔진에서검색단어전송에많이이용함 POST 방식 서버로정보를올리기위해설계됨 데이터크기의제한은없음 URL 에 parameter 가표시되지않음 Servlet 구조 서블릿로딩 init() 메서드 최초클라이언트요청시 init() 메서드가호출되며메모리에적재됨. 요청처리 service() 메서드 service() 메서드가컨테이너에의해호출되며사용자 처리수행 doget(), dopost() 메서드 서블릿종료 destroy() 메서드
Servlet 생명주기 Servlet 이해하기 web.xml 에다음내용추가 실행시기 최초요청 / 컨테이너시작 기능 초기화이벤트 실행메서드 init() <servlet-mapping> <servlet-name>invoker</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping> 모든요청과응답 요청응답 service() doget() 와 dopost() 로분기 쓰레드를통한동시실행 컨테이너종료 / 애플리케이션종료 종료이벤트 destroy() HttpServletRequest 클래스 HttpServletRequest 클래스 HttpServlet 클래스의 doget(), gopost() 메서드호출시파라미터로전달됨. 사용자요청과관련된정보를제공. HTML 폼입력값을가져옴 쿠키, 세션정보에접근할수있음 클라이언트 IP주소를알수있음 HttpServletResponse 클래스 HttpServletResponse 클래스 HttpServlet 클래스의 doget(), gopost() 메서드호출시파라미터로전달 사용자응답을처리하기위한클래스 MIME Type 설정 HTTP 헤더정보설정 페이지전환
Servlet 코드로생성된 JSP 서블릿코드로생성된 JSP Servlet 코드로생성된 JSP 서블릿코드로생성된 JSP package org.apache.jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import org.apache.jasper.runtime.*; public class HelloWorld_jsp extends HttpJspBase { Servlet 코드로생성된 JSP 서블릿코드로생성된 JSP java.lang.object javax.servlet.genericservlet javax.servlet.http.httpservlet org.apache.jasper.runtime.httpjspbase Servlet 코드로생성된 JSP JSP 파일의모든내용은 _jspservice() 메서드에위치 public void _jspservice(httpservletrequest request, HttpServletResponse response) throws java.io.ioexception, ServletException {... out.write("\r\n\r\n"); out.write("<html>\r\n"); out.write("<head>"); out.write("<title>hello World"); out.write("</title>"); out.write("</head>\r\n\r\n"); out.write("<body>"); out.write("<h2>hello World : 헬로월드 "); out.write("</h2>\r\n 오늘의날짜와시간은 : "); out.print( new java.util.date() ); 이하중략
JSP 개발방법 디자인패턴 디자인패턴은기존환경에서반복적으로일어나는문제를설명하고, 그문제해법의핵심을설명하는것이다. 이렇게하면같은방법을두번반복하지않고, 이해법을백만번이상재사용할수있다. - 크리스토퍼알렉산더 객체지향소프트웨어개발에필요한기법을정리 소프트웨어유지보수에유리 개발표준화와유지보수의효율성 모델 1 모델1은초기 JSP개발에사용된모델 일반적으로사용되는 JSP개발방법 유지보수의어려움으로인해최근엔모델2가권장됨 웹브라우저 웹컨테이너 요청 JSP 서비스클래스자바빈클래스 응답 View Model Controller 모델 1 모델 1 소스코드예시 <% String user_passwd = request.getparameter("passwd"); if(user_passwd.equals("123456")) { %> <H2> 관리자메뉴 </H2> <HR> 1. <a href=> 사용자추가 </a><p> 2. <a href=> 비밀번호변경 </a><p> 3. <a href=> 리포트보기 </a><p> <% else { out.println("<h2> 비밀번호가잘못되었습니다.</H2>"); out.println("<a href=check_form.jsp> 돌아가기 </a>"); %> 모델 2 서블릿이요청을처리하고 JSP가뷰를생성 모든요청을단일서블릿에서처리 요청처리후결과를보여줄 JSP로이동 웹브라우저 요청 응답 서블릿 Controller JSP View 웹컨테이너 로직클래스 Model
MVC 패턴 Model-View-Controller 모델 - 비즈니스영역의상태정보를처리 뷰 - 비즈니스영역에대한프레젠테이션뷰 ( 즉, 사용자가보게될결과화면 ) 를담당 컨트롤러 - 사용자의입력및흐름제어를담당 MVC 컨트롤러 컨트롤러 (Controller) 뷰와뷰를연결하고데이터처리를위해모델영역과연동 비즈니스로직이복잡하면 BO(Business Object) 클래스를두어처리 서블릿을권장하며간단한작업에는 JSP로도가능 특징 로직을처리하는모델과결과화면을보여주는뷰가분리되 흐름제어나사용자의처리요청은컨트롤러에집중 모델 2 구조와매핑 : 컨트롤러-서블릿, 뷰-JSP MVC 모델 모델 (Model) 데이터처리부분담당. Value Object, Data Acess Object 으로분리하기도함. 데이터베이스와연동 빈즈, 자바클래스로구현 MVC 뷰 뷰 (View) 프리젠테이젼영역즉사용자에게보여지는화면을담당. 스크립트릿을통한로직제어 (for, if, while) 에따라화면을구성하면안됨. HTML, JSP로구현 커스텀태그라이브러리사용권장 (JSTL 등 )
모델 2 모델 2 소스코드예시 서블릿컨트롤러에서는일반적으로 forward 를이용 RequestDispatcher view = request.getrequestdispatcher( admin_menu.jsp ); view.forward(request,response); <% String user_passwd = request.getparameter("passwd"); if(user_passwd.equals("123456")) { response.sendredirect("admin_menu.jsp"); else { response.sendredirect("error.jsp"); %> MVC 패턴컨트롤러기본구현 public class ControllerServlet extends HttpServlet { // 1 단계, HTTP 요청받음 public void doget(httpservletrequest request, HttpServletResponse response) throws IOException, ServletException { processrequest(request, response); public void dopost(httpservletrequest request, HttpServletResponse response) throws IOException, ServletException { processrequest(request, response); private void processrequest(httpservlet request, HttpServletResponse response) throws IOException, ServletException { // 2 단계, 요청분석 // request 객체로부터사용자의요청을분석하는코드... // 3 단계, 모델을사용하여요청한기능을수행한다. // 사용자에요청에따라알맞은코드 // 4 단계, request 나 session 에처리결과를저장 request.setattribute("result", resultobject); // 이런형태의코드... // 5 단계, RequestDispatcher 를사용하여알맞은뷰로포워딩 RequestDispatcher dispatcher = request.getrequestdispatcher("/view.jsp"); dispatcher.forward(request, response); 컨트롤러구현예 private void processrequest(httpservletrequest request, HttpServletResponse response) throws IOException, ServletException { // 2 단계, 요청파악 // request 객체로부터사용자의요청을파악하는코드 String type = request.getparameter("type"); // 3 단계, 요청한기능을수행한다. // 사용자에요청에따라알맞은코드 Object resultobject = null; if (type == null type.equals("greeting")) { resultobject = " 안녕하세요."; else if (type.equals("date")) { resultobject = new java.util.date(); else { resultobject = "Invalid Type"; // 4 단계, request 나 session 에처리결과를저장 request.setattribute("result", resultobject); // 5 단계, RequestDispatcher 를사용하여알맞은뷰로포워딩 RequestDispatcher dispatcher = request.getrequestdispatcher("/simpleview.jsp"); dispatcher.forward(request, response); 클라이언트의요청명령을구분하는방식 컨트롤러가알맞은로직을수행하려면클라이언트가어떤기능을요청하는지구분할수있어야함 요청기능구분방식 특정이름의파라미터에명령어정보를전달 http://host/board/controller/controllerservlet?cmd=boardlist&... 요청 URI 를명령어로사용 http://host/board/boardlist?...
커맨드패턴을이용한요청처리 커맨드패턴 - 클라이언트의각요청을처리하는별도클래스를제공하는구현패턴 하나의명령어를하나의클래스가처리 클래스구성 명령어를처리하는클래스들은동일한인터페이스를구현 커맨드와처리클래스의매핑정보를담은설정파일 < 커맨드, 요청처리클래스 > 매핑정보를코드가아닌별도설정파일에저장 새로운커맨드추가나클래스변경시편리 설정파일예 BoardList = action.boardlisthandler BoardWriteForm = action.boardwriteformhandler 설정파일사용컨트롤러구현 - 설정파일로딩 public class ControllerUsingFile extends HttpServlet { // < 커맨드, 핸들러인스턴스 > 매핑정보저장 private Map commandhandlermap = new java.util.hashmap(); public void init(servletconfig config) throws ServletException { String configfile = config.getinitparameter("configfile"); Properties prop = new Properties(); FileInputStream fis = null; 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 ex) { 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 (Exception e) { throw new ServletException(e); 설정파일사용컨트롤러구현 - 요청처리 private void process(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { String command = request.getparameter("cmd"); CommandHandler handler = (CommandHandler)commandHandlerMap.get(command if (handler == null) { handler = new NullHandler(); String viewpage = null; try { viewpage = handler.process(request, response); catch (Throwable e) { throw new ServletException(e); RequestDispatcher dispatcher = request.getrequestdispatcher(viewpage); dispatcher.forward(request, response);
설정파일사용컨트롤러구현 - web.xml 요청 URI 사용시 <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> private void process(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { String command = request.getrequesturi(); if (command.indexof(request.getcontextpath()) == 0) { command = command.substring(request.getcontextpath().length()); CommandHandler handler = (CommandHandler) commandhandlermap.get(command); <servlet-mapping> <servlet-name>controllerusingfile</servlet-name> <url-pattern>/controllerusingfile</url-pattern> </servlet-mapping> http://localhost:8080/board/controllerusingfile?cmd=hello 모델 1 vs 모델 2 모델 장점 단점 모델 1 -배우기쉬움 -자바언어를몰라도구현가능 -기능과 JSP의직관적인연결 -하나의 JSP가하나의기능과연결 -로직코드와뷰코드가혼합되어 JSP 코드가복잡해짐 -뷰변경시논리코드의빈번한복사 -유지보수작업이불편함 모델 2 - 로직코드와뷰코드의분리에따른유지보수의편리함 - 컨트롤러서블릿에서집중적인작업처리가능 ( 권한 / 인증등 ) - 확장의용이함 - 자바언어에친숙하지않으면접근하기가쉽지않음 - 작업량이많음 ( 커맨드클래스 + 뷰 JSP)