Spring MVC 기초 - 강사김현오 -
1. Spring MVC 개요 1.1 SpringMVC 개요 1.2 SpringMVC 시작하기 MVC 패턴 DispatcherServlet SpringMVC 요청처리과정
MVC 패턴 (1/2) Model 어플리케이션상태의캡슐화 상태쿼리에대한응답 어플리케이션의기능표현 변경을뷰에통지 View 모델을화면에시각적으로표현 모델에게업데이트요청 사용자의입력을컨트롤러에전달 컨트롤러가뷰를선택하도록허용 Controller 어플리케이션의행위정의 사용자액션을모델업데이트와매핑 응답에대한뷰선택 View Model Controller 메소드호출이벤트 2
MVC 패턴 (2/2) MVC(Model View Controller) 패턴 어플리케이션의확장을위해 Model, View, Controller 세가지영역으로분리 컴포넌트의변경이다른영역컴포넌트에영향을미치지않음 컴포넌트간의결합성이낮아프로그램수정이용이 장점 - 화면과비즈니스로직을분리해서작업가능 - 영역별개발로인하여확장성이뛰어남 - 표준화된코드를사용하므로공동작업이용이하고유지보수성이좋음 단점 - 개발과정이복잡해초기개발속도가늦음 - 초보자가이해하고개발하기에다소어려움 3
DispatherServlet DispatcherServlet 프리젠테이션계층의제일앞에서서버로들어오는모든요청을먼저받아서처리하는서블릿 web.xml 에등록 (1) HTTP 요청 (2) 요청 Dispatcher Servlet 컨트롤러 (C) (7) HTTP 응답 (4) 모델 & 뷰 (5) 모델 (6) 생성 (3) 생성 뷰 (V) (6) 참조 모델 (M) 4
Spring MVC 요청처리과정 Spring MVC 스프링이직접제공하는서블릿기반의 MVC 프레임워크 2 Handler Mapping Spring MVC 요청처리순서 DispatcherServlet 이요청을수신 - 단일 Front Controller Servlet - 요청을수신하여처리를다른컴포넌트에위임 - 어느컨트롤러에요청을전송할지결정 DispatcherServlet 은 Handler Mapping 에어느컨트롤러를사용할것인지문의 - URL 과매핑 DispatcherServlet 은요청을컨트롤러에게전송하고컨트롤러는요청을처리한후결과리턴 - 비즈니스로직수행후결과정보 (Model) 가생성되어 JSP 와같은뷰에서사용됨 ModelAndView 오브젝트에수행결과가포함되어 DispatcherServlet 에리턴 1 Dispatcher Servlet ModelAndView 6 3 5 4 Controller ViewResolver ModelAndView 는실제 JSP 정보를갖고있지않으며, ViewResolver 가논리적이름을실제 JSP 이름으로변환 View View 는결과정보를사용하여화면을표현함 5
1. Spring MVC 개요 1.1 SpringMVC 개요 1.2 SpringMVC 시작하기 DispatcherServlet 설정 애플리케이션컨텍스트구성전략 Spring MVC 애플리케이션작성절차
DispatcherServlet 설정 (1/3) DispatcherServlet 설정 web.xml에등록 디폴트컨텍스트설정파일 : <servlet name> servlet.xml <url pattern> 은 DispatcherServlet이처리하는 URL 매핑패턴을정의 서블릿이기때문에하나이상의 DispatcherServlet 설정가능 <load on startup> 값이 1인경우 WAS start시초기화작업을함 <servlet> <servlet-name>appservlet</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>contextconfiglocation</param-name> <param-value>/web INF/spring/appServlet/servlet context.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>appservlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> 7
DispatcherServlet 설정 (2/3) 루트애플리케이션컨텍스트등록 각 DiapatcherServlet 이공통으로사용하는빈이있는경우 ContextLoaderListener 를사용하여공통으로사용할빈을설정한다. 디폴트설정파일 : /WEB INF/applicationContext.xml DispatcherServlet 이생성한컨텍스트는자식컨텍스트가된다. 보통웹기술과무관한서비스계층이나데이터접근계층의빈들을등록하기위해사용한다. <listener> <listener-class>org.springframework.web.context.contextloaderlistener</listener-class> </listener> <context-param> <param-name>contextconfiglocation</param-name> <param-value>/web-inf/board-service.xml /WEB-INF/board-data.xml /WEB-INF/board-security.xml </param-value> </context-param> 8
애플리케이션컨텍스트구성전략 애플리케이션레이어에따라애플리케이션컨텍스트를분리하여관리한다. Security Layer board security.xml Web Layer board servlet.xml Service Layer board service.xml Persistence Layer board data.xml 9
Spring MVC 애플리케이션작성절차 Spring MVC 를이용한애플리케이션작성단계 컨트롤러작성 JSP 코드작성 컨트롤러작성 좋은설계는컨트롤러가많은일을하지않고서비스에처리를위임하는것이다. Web Request Response Dispatcher Servlet Request Response Controller dosomething() Service 10
컨트롤러작성 컨트롤러작성 package com.namoo.mvc.controller;... import com.namoo.mvc.domain.post; import com.namoo.mvc.service.blogservice; @Controller @RequestMapping("/blog") public class BlogController { 서비스클래스주입 @Autowired private BlogService blogservice; @RequestMapping("/post/list") public ModelAndView listpost() { // List<Post> posts = blogservice.findallposts("1"); 서비스를이용하여조회 return new ModelAndView("post/list", "posts", posts); ModelAndView 생성하여반환 11
ModelAndView ModelAndView 컨트롤러는논리적뷰와뷰에전달할데이터를포함하는 ModelAndView 객체를리턴한다. ModelAndView("post/list","posts",posts); 논리적인뷰이름모델객체의이름모델객체 12
JSP 작성하기 모델을받아 HTML 을구성하는 JSP 코드 <%@ page language="java" contenttype="text/html; charset=utf-8" pageencoding="utf-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>namooblog</title> </head> <body> <table border="1"> <tr> <th> 제목 </th> <th> 내용 </th> </tr> <c:foreach var="post" items="${posts"> <tr> <td>${post.subject</td> <td>${post.contents</td> </tr> </c:foreach> </table> 모델 (posts, 목록데이터 ) 을화면에보여줌. ${ 프로퍼티명 형식으로모델데이터사용가능 </body> </html> 13
결과화면 14
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 Spring @MVC 소개 컨트롤러작성및빈등록 View Resolver 설정
Spring @MVC Spring @MVC spring 2.5에서도입되어 3.0에서강화된 annotation 활용전략 어노테이션기반 MVC 최신 SpringMVC의핵심전략 @ 은어노테이션을의미함 스프링에서도 @MVC를밀고있음 ( 예전꺼는 deprecate 하고향후삭제한다고협박 ) 16
컨트롤러작성및빈등록 컨트롤러클래스작성 public class HelloController { @RequestMapping( hello.do") public String sayhello(model model) { model.addattribute( message, 안녕하세요 ) return hello" HelloController.java 컨텍스트설정파일 ([servletname] servlet.xml) 에컨트롤러설정 <beans:bean class="com.kosta.hellocontroller /> dispatcher-servlet.xml 17
View Resolver 설정 컨트롤러와 JSP 의연결위해 View Resolver 설정 WEB INF/spring/appServlet/servlet context.xm <beans:bean class="com.kosta.hellocontroller /> <beans:bean class="org.springframework.web.servlet.view.internalresourceviewresolver"> <beans:property name="prefix" value="/web INF/views/" /> <beans:property name="suffix" value=".jsp" /> </beans:bean> JSP 코드작성 dispatcher-servlet.xml <%@ page language="java" contenttype="text/html charset=utf 8" pageencoding= UTF 8"%> <!DOCTYPE html PUBLIC " //W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/tr/html4/loose.dtd"> <html> <head> <meta http equiv="content Type" content="text/html charset=utf 8"> </head> <body> 음... ${message </body> </html> hello.jsp 18
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 @Controller @RequestMapping
@Controller 컨트롤러클래스에 @Controller 선언 클라이언트의요청을처리 @Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class) /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = RequestMethod.GET) public String home(locale locale, Model model) { logger.info("welcome home! The client locale is {.", locale) Date date = new Date() DateFormat dateformat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale) String formatteddate = dateformat.format(date) model.addattribute("servertime", formatteddate ) return "home" 20
@Controller DispatcherServlet 이사용하는스프링설정파일에해당컨트롤러클래스를등록 WEB INF/spring/appServlet/servlet context.xml <context:component scan base package="kr.kosta.myspring" /> <! Resolves views selected for rendering by @Controllers to.jsp resources in the /WEB INF/views directory > <beans:bean class="org.springframework.web.servlet.view.internalresourceviewresolver"> <beans:property name="prefix" value="/web INF/views/" /> <beans:property name="suffix" value=".jsp" /> </beans:bean> 21
@RequestMapping 컨트롤러메소드의 HTTP 메소드한정 같은 URL 의요청에대하여 HTTP 메소드 (POST, GET ) 에따라서로다른메소드를 Mapping 할수있음. @Controller public class HelloController { @RequestMapping(value= hello.do, method=requestmethod.get) public String hello() { return helloget ; @RequestMapping(value= hello.do, method=requestmethod.post) public String hello2() { return hellopost ; 22
@RequestMapping @RequestMapping 어노테이션을클래스에적용하여 URI 지정가능 @Controller @RequestMapping( /hello.do ) public class HelloController { @RequestMapping(method=RequestMethod.GET) public String hello() { return helloget ; @RequestMapping(method=RequestMethod.POST) public String hello2() { return hellopost ; 23
@RequestMapping URL Ant 스타일의 URL 패턴지원 @RequestMapping( hello ) @RequestMapping( main* ) @RequestMapping( view.* ) @RequestMapping( admin/**/user ) URI 템플릿사용, path variable @RequestMapping( users/{userid/delete ) 여러개지정가능 @RequestMapping({ hello, hi, wassup ) 24
Ant-Style Pattern 25
@RequestMapping params String[] params() 엘리먼트는파라메터값과비교해서매핑 URL Request Param : /user/edit?type=admin @RequestMapping(value= user/edit, params= type=admin ) @RequestMapping(value= user/edit, params= type=member ) Form Param <jsp> <form action= ${ctx/user/edit" method="post"> <input type="hidden" name= type value= admin" </form> 26
@RequestMapping headers String[] headers() 엘리먼트는 HTTP 헤더값과비교해서매핑 @RequestMapping(value= user/edit, headers= content type=text/* ) 27
@RequestMapping 클래스상속 @RequestMapping 은하위클래스에도상속됨 단, 하위클래스에서 @RequestMapping 을재정의한경우는상위클래스 @RequestMapping 은무시 @RequestMapping 을인터페이스에작성한경우도마찬가지 28
컨트롤러메소드의파라메터타입 컨트롤러메서드의파라메터로다양한오브젝트를받을수있음 HttpServletRequest HttpServletResponse HttpSession java.util.locale InputStream, Reader OutputStream, Writer @PathVariable @RequestParam @RequestHeader @CookieValue @RequestBody Map, Model, ModelMap 파라미터타입 필요시 servlet api를사용할수있음현재요청에대한 Locale 요청컨텐츠에직접접근할때사용응답컨텐츠를생성할때사용 URI 템플릿변수에접근할때사용 HTTP 요청파라미터를매핑 HTTP 요청헤더를매핑 HTTP 쿠키매핑 HTTP 요청의몸체내용에접근할때사용뷰에전달할모델데이터를설정할때사용 설명 @ModelAttribute HTTP 요청파라미터를저장한객체, 기본적으로클래스이름을모델명으로사용 Errors, BindingResult SessionStatus HTTP 요청파라미터를커맨드객체에저장한결과, 커맨드객체를위한파라미터바로다음에위치 폼처리를완료했음을처리하기위해사용. @SessionAttributes 어노테이션을명시한 session 속성을제거하도록이벤트를발생시킨다. 29
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 HTML 폼과커맨드객체 View 에서커맨드객체에접근하기 @ModelAttribute 컨트롤러메소드의파라메터타입 Validator @Valid
HTML 폼과커맨드객체 Spring MVC 는폼에입력한데이터를자바빈객체를이용해서전송할수있도록함. <form method= post action= saveboard.do > 제목 : <input type= text name= title /><br/> 내용 : <textarea name= content ></textarea><br/> <input type= submit /> </form> public class Board { private String title; private String content; public void settitle(string title) { this.title = title; @Controller public class BoardController { @RequestMapping( board/saveboard.do ) public String save(board board) { public void setcontent(string content) { this.content = content; 31
View 에서커맨드객체에접근하기 View 코드에서는컨트롤러의 @RequestMapping 어노테이션메소드에서전달받은커맨드객체에접근할수있다. @Controller public class BoardController { @RequestMapping( board/saveboard.do ) public String hello(board command) { <body> ${board.title.jsp @ModelAttribute 를사용하여 View 에서사용할커맨드객체의이름변경 @Controller @RequestMapping( /hello.do ) public class HelloController { @RequestMapping(method=RequestMethod.GET) public String hello(@modelattribute( faq ) Board board) { <body> ${faq.title.jsp 32
@ModelAttribute 요청파라메터를객체형태로받기위해사용되는어노테이션 (@RequestParam 은메서드파라메터를요청파라메터에서 1:1 로받을경우 ) 검색조건과같은여러파라메터를객체형태로받거나 form submit 으로넘어오는파라메터를바로객체로받는데유용하게사용 @ModelAttribute 가붙은파라미터는따로 Model, ModelMap 에담지않아도뷰에서바로사용가능 컨트롤러가리턴하는모델에별도로 add 하지않아도 recipe 라는키로자동으로추가되기때문에뷰에서사용할수있음 @RequestMapping(method=RequestMethod.POST) public String regist(@modelattribute("recipe") Recipe recipe) { return "recipe" 33
@ModelAttribute 실습 목표 레시피목록조회, 등록기능구현 34
@ModelAttribute 실습 간단한실습모델 35
@ModelAttribute 실습 Recipe 클래스 package kr.or.kosta.spring.cookbook.recipe /** * 레시피를표현 */ public class Recipe { /** 시피명 */ private String name /** 재료 */ private String ingredients /** 조리법설명 */ private String description /** 기본생성자 */ public Recipe() { getters and setters 36
@ModelAttribute 실습 Chef 클래스 package kr.or.kosta.spring.cookbook import java.util.arraylist import java.util.list import kr.or.kosta.spring.cookbook.recipe.recipe public class Chef { private List<Recipe> recipes public Chef() { recipes = new ArrayList<>() /** * 전제레시피목록을가져온다. * * @return 전체레시피목록 */ public List<Recipe> showallrecipes() { return this.recipes /** * 레시피생성 * * @param recipe */ public void createrecipe(recipe recipe) { this.recipes.add(recipe) 37
@ModelAttribute 실습 레시피목록 JSP <%@ page contenttype="text/html charset=utf 8" pageencoding="utf 8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page session="false" %> <c:set var="ctx" value="${pagecontext.request.contextpath" scope="request" /> <html> <head><title>recipe Home</title></head> <body> <h1> 레시피목록 </h1> <P> 레시피목록입니다.</P> <table border="1"> <thead> <tr> <th> 레시피명 </th> <th> 재료 </th> <th> 조리법 </th> </tr> </thead> <c:foreach var="recipe" items="${recipes"> <tbody> <tr> <td>${recipe.name</td> <td>${recipe.ingredients</td> <td><pre>${recipe.description</pre></td> </tr> </tbody> </c:foreach> </table> <br/> <input type="button" value=" 레시피등록 " onclick="javascript:location.href='${ctx/recipe/new' "> </body> </html> 38
@ModelAttribute 실습 레시피등록 JSP <%@ page contenttype="text/html charset=utf 8" pageencoding="utf 8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page session="false" %> <c:set var="ctx" value="${pagecontext.request.contextpath" scope="request" /> <html> <head><title>recipe Home</title></head> <body> <h1> 레시피등록 </h1> <P> 레시피를만드세요.</P> <form action="${ctx/recipe" method="post"> <table border="1"> <tr> <td> 레시피명 </td> <td><input type="text" name="name"></td> </tr> <tr> <td> 재료 </td> <td><input type="text" name="ingredients"></td> </tr> <tr> <td> 조리법 </td> <td><textarea rows="10" cols="30" name="description"></textarea></td> </tr> </table> <input type="submit" value=" 레시피등록 " /> <input type="button" value=" 취소 " onclick="javascript:history.back( 1) "> </form> </body> </html> 39
View 에서커맨드객체에접근하기 커맨드객체 List 로받기 public class OrderCommand { private List<OrderItem> orderitems; public void setorderitems(list<orderitem> orderitems) { this.orderitems = orderitems; @Controller @RequestMapping( /hello.do ) public class HelloController { @RequestMapping(method=RequestMethod.GET) public String hello(ordercommand command) { <form action= hello.do method= post > <input type= text name= orderitems[0].itemid /> <input type= text name= orderitems[0].number /> <input type= text name= orderitems[0].remark /> <br/> <input type= text name= orderitems[1].itemid /> <input type= text name= orderitems[1].number /> <input type= text name= orderitems[1].remark /> <br/> <input type= submit /> </form> 40
컨트롤러메소드의파라메터타입 @RequestParam 어노테이션을이용한파라메터매핑 http://localhost:8080/hello.do?name=michael&age=31 @Controller public class HelloController { @RequestMapping( /hello.do ) public String hello(@requestparam( name ) String name, @RequestParam( age ) int age) { system.out.println(name); system.out.println(age); @Controller public class HelloController { 필수여부 @RequestMapping( /hello.do ) public String hello( @RequestParam(value= name, required=false) String name, @RequestParam(value= age, defaultvalue= 0 ) int age) { system.out.println(name); system.out.println(age); 기본값 41
컨트롤러메소드의파라메터타입 @CookieValue 어노테이션을이용한쿠키매핑 @Controller public class HelloController { @RequestMapping( /hello.do ) public String hello(@cookievalue( auth ) String authvalue) { @Controller public class HelloController { @RequestMapping( /hello.do ) public String hello(@cookievalue(value= auth, required = false, defaultvalue= 0 ) String authvalue) { @RequestHeader 어노테이션을이용한헤더매핑 @Controller public class HelloController { @RequestMapping( /hello.do ) public String hello(@requestheader( Accept-Language ) String headerlanguage) { 42
컨트롤러메소드의파라메터타입 @RequestBody HTTP 요청 Body가그대로객체에전달됨 XML이나 JSON 전문을사용하는경우유용함 AnnotationMethodHandlerAdapter에는 HttpMessageConverter 타입의메시지변환기가기본으로여러개등록되어있음 @RequestBody가붙은파라미터가있으면해당미디어타입을확인후처리가능한변환기 (Converter) 가자동으로객체로변환시켜줌 주로 @ResponseBody와함께사용됨 @RequestMapping(value="json", method=requestmethod.post) public void registwithjsonmessage(@requestbody Recipe recipe) { chef.createrecipe(recipe) 43
컨트롤러메소드의파라메터타입 서블릿 API 직접사용 javax.servlet.http.httpservletrequest/javax.servlet.servletrequest javax.servlet.http.httpservletresponse/javax.servlet.servletresponse javax.servlet.http.httpsession HttpSession 의생성을직접제어해야하는경우 컨트롤러에서쿠키를생성해야하는경우 서블릿 API 를선호하는경우 @Controller public class HelloController { @RequestMapping( /hello.do ) public String hello(httpservletrequest request, HttpServletResponse response) { 44
폼입력값검증 @ModelAttribute 로바인딩된객체를검증하는데사용 폼입력값검증 스프링은유효성검사를위한 Validator 인터페이스와검사결과를저장할 Errors 인터페이스제공 package kr.or.kosta.controller public interface Validator { boolean supports(class<?> clazz) // 해당클래스 validation 지원여부 void validate(object target, Errors errors) // 검증결과묹제가있는경우 error 객체에정보를저장 45
RecipeValidator.java 레시피입력값검사후통과하지못한경우페이지에검증오류메시지를보여주도록구현 package kr.or.kosta.validator import kr.or.kosta.spring.cookbook.recipe.recipe import org.springframework.validation.errors import org.springframework.validation.validator public class RecipeValidator implements Validator { @Override public boolean supports(class<?> clazz) { return (Recipe.class.isAssignableFrom(clazz)) @Override public void validate(object target, Errors errors) { Recipe recipe = (Recipe) target if (recipe.getname() == null recipe.getname().length() == 0) { errors.reject("recipename", " 레시피이름은반드시입력되어야합니다.") <servlet context.xml> <beans:bean class="kr.or.kosta.validator.recipevalidator"/> //validator 스프링빈등록 46
RecipeController.java 레시피입력값검사후통과하지못한경우페이지에검증오류메시지를보여주도록구현 @Autowired public RecipeValidator recipevalidator @RequestMapping(method=RequestMethod.POST) public String regist(@modelattribute("recipe") Recipe recipe, BindingResult bindingresult, Model model) { // validation 체크 this.recipevalidator.validate(recipe, bindingresult) if (bindingresult.haserrors()) { // validation 오류가있는경우 bindingresult 에서에러정보를가지고처리함 model.addattribute("errors", bindingresult) return "recipe/new" else { // validation 을통과한경우 chef.createrecipe(recipe) return "redirect:/recipe" 47
new.jsp <%@ page contenttype="text/html charset=utf 8" pageencoding="utf 8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page session="false" %> <c:set var="ctx" value="${pagecontext.request.contextpath" scope="request" /> <html> <head><title>recipe Home</title></head> <body> <h1> 레시피등록 </h1> <P> 레시피를만드세요.</P> <form action="${ctx/recipe" method="post"> <table border="1"> <tr> <td> 레시피명 </td> <td><input type="text" name="name"></td> </tr> <tr> <td> 재료 </td> <td><input type="text" name="ingredients"></td> </tr> <tr> <td> 조리법 </td> <td><textarea rows="10" cols="30" name="description"></textarea></td> </tr> </table> <input type="submit" value=" 레시피등록 " /> <input type="button" value=" 취소 " onclick="javascript:history.back( 1) "> </form> <c:foreach var="error" items="${errors.allerrors"> ${error.defaultmessage <br/> </c:foreach> </body> </html> 48
결과 49
@Valid 를이용한자동검증 Recipecontroller.java @Valid : 프레임워크가 validation 을호출하도록어노테이션으로설정 @InitBinder : Validator 를컨트롤러에등록 @Autowired public RecipeValidator recipevalidator @InitBinder public void initbinder(webdatabinder databinder) { databinder.setvalidator(this.recipevalidator) @RequestMapping(method=RequestMethod.POST) public String regist(@modelattribute("recipe") @Valid Recipe recipe, BindingResult bindingresult, Model model){ if (bindingresult.haserrors()) { // validation 오류가있는경우 bindingresult 에서에러정보를가지고처리함 model.addattribute("errors", bindingresult) return "recipe/new" else { // validation 을통과한경우 chef.createrecipe(recipe) return "redirect:/recipe" 50
컨트롤러메소드의리턴타입 컨트롤러메서드의리턴타입종류 리턴타입 설명 ModelAndView 뷰정보및모델정보를담고있는 ModelAndView 객체 Model Map String 뷰이름을리턴한다. 뷰에전달할객체정보를담고있는 Model 을리턴한다. 이때뷰이름은요청 URL 로부터결정된다.(RequestToViewNameTranslator) 뷰에전달할객체정보를담고있는 Map 을리턴한다. 이때뷰이름은요청 URL 로부터결정된다.(RequestToViewNameTranslator) View View 객체를직접리턴, 해당 View 객체를이용해서뷰를생성한다. void 메서드가 ServletResponse 나 HttpServelResponse 타입의파라미터를갖는경우메서드가직접응답을처리한다고가정한다. 그렇지않을경우요청 URL 로부터결정된뷰를보여준다. (RequestToViewNameTranslator) @ResponseBody 어노테이션적용 메소드에서 @ResponseBody 어노테이션이적용된경우, 리턴객체를 HTTP 응답으로전송한다. HttpMessageConverter 를이용해서객체를 HTTP 응답스트림으로변환한다. 51
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 View 지정 명시적 View 지정 자동 View 지정 리다이렉트 View
View 지정 컨트롤러에서는처리결과를보여줄 View 이름이나객체를리턴하고, DispatcherServlet 은해당정보를통해뷰를생성 명시적지정 자동지정 53
명시적 View 지정 View 이름명시적지정 ModelAndView 와 String 리턴타입 @Controller public class HelloController { @RequestMapping( /hello.do ) public ModelAndView hello() { ModelAndView mav = new ModelAndView( hello ); return mav; @Controller public class HelloController { @RequestMapping( /hello.do ) public ModelAndView hello() { ModelAndView mav = new ModelAndView(); mav.setviewname( hello ); @Controller public class HelloController { @RequestMapping( /hello.do ) public String hello() { return hello ; 54
자동 View 지정 View 자동지정 RequestToViewNameTranslator를이용하여 URL로부터 View이름을결정한다. 리턴타입이 Model이나 Map인경우 리턴타입이 void 이면서 ServletResponse나 HttpServletResponse 타입의파라미터가없는경우 @Controller public class HelloController { hello 가뷰이름이됨 @RequestMapping( /hello.do ) public Map<String, Object> hello() { HashMap<String, Object> model = new HashMap<String, Object>(); return model; 55
리다이렉트 View 리다이렉트뷰 View 이름에 redirect: 접두어를붙이면, 지정한페이지로리다이렉트된다. redirect:/product/productlist.do redirect:http://localhost/product/productlist.do // 레시피를등록하고전체목록조회 view 로리다이렉트 @RequestMapping(method=RequestMethod.POST) public String regist(@modelattribute("recipe") Recipe recipe) { chef.createrecipe(recipe) return "redirect:/recipe" 56
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 Model 생성 Model 인터페이스 ModelAndView @ModelAttribute
Model 생성 (1/2) 뷰에전달하는데이터 @RequestMapping 어노테이션이적용된메서드의 Map, Model, ModelMap을통한설정 @RequestMapping 메서드가리턴하는 ModelAndView @ModelAttribute 어노테이션이적용된메서드가리턴한객체 58
Model 생성 (2/2) Map, Model, ModelMap 을통한설정 파라미터로받는방식 @Controller public class HelloController { @RequestMapping( /hello.do ) public String hello(map model) { model.addattribute(, ); @Controller public class HelloController { @RequestMapping( /hello.do ) public String hello(model model) { 59
Model 인터페이스 Model 인터페이스 Model addattribute(string name, Object value) Model addattribute(object value) Model addallattributes(collection<?> values) Model addallattributes(map<string,?> attributes) Model mergeattributes(map<string,?> attributes) boolean containsattribute(string name) 60
ModelAndView ModelAndView 를통한모델설정 컨트롤러에서처리결과를보여줄 View 와 View 에전달할값 ( 모델 ) 을저장하는용도로사용 setviewname( viewname ) addobject(string name, Object value) @Controller public class HelloController { @RequestMapping( /hello.do ) public ModelAndView hello() { ModelAndView mav = new ModelAndView(); mav.setviewname( hello ); mav.addobject( message, 안녕하세요 ); return mav; 61
@ModelAttribute @ModelAttribute 어노테이션을이용한모델데이터처리 @RequestMapping 어노테이션이적용되지않은별도메서드로모델에추가될객체를생성 @Controller public class HelloController { @ModelAttribute( modelattrmessage") public String getmodelattrmessage() { return "bye bye..." </html> <body> ${message ${modelattrmessage </body> </html> @RequestMapping("hello.do") public String sayhello(model model) { model.addattribute("message", " 안녕하세요 ~") return "hello" 62
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 전체경로와서블리기반경로매칭 @PathVariable @RequestMapping
전체경로와서블릿기반경로매칭 (1/2) 전체경로와서블릿기반경로매칭 DispatcherServlet 은 DefaultAnnotationHandlerMapping 클래스를기본으로 HandlerMapping 구현체로사용 Default 로컨텍스트내의경로가아닌서블릿경로를제외한나머지경로에대해매핑 <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.do</url-pattern> <url-pattern>/game/*</url-pattern> </servlet-mapping> @RequestMapping( /search/game.do ) @RequestMapping( /game/info ) 매칭안됨 64
전체경로와서블릿기반경로매칭 (2/2) 서블릿경로를포함한전체경로를이용해서매칭하려는경우 <bean class= org.springframework.web.servlet.mvc.annotation.defaultannotationhandlermapping p:alwaysusefullpath= true /> <bean class= org.springframework.web.servlet.mvc.annotation.annotationmethodhandlermapping p:alwaysusefullpath= true /> 65
@PathVariable @PathVariable 어노테이션을이용한 URI 템플릿 RESTful 방식 - http://somehost/users/madvirus - http://somehost/games/ - http://somehost/forum/board1/10 @RequestMapping 어노테이션값으로 { 템플릿변수 를사용한다. @PathVariable 어노테이션을이용해서 { 템플릿변수 와동일한이름을갖는파라메터를추가한다. @Controller Public class CharaterInfoController { @RequestMapping( /game/users/{userid/characters/{characterid ) public String characterinfo(@pathvariable String userid, @PathVariable int characterid, ModelMap model){ 66
@RequestMapping @RequestMapping 어노테이션의추가설정방법 @RequestMapping 어노테이션을클래스와메소드에함께적용하는경우 @Controller @RequestMapping( game/users/{userid ) Public class CharaterInfoController { @RequestMapping( characters/{characterid ) public String characterinfo(@pathvariable String userid, @PathVariable int characterid, ModelMap model){ Ant 스타일의 URI 패턴지원 @RequestMapping( /members/*.do ) @RequestMapping( /game/*/items/{itemid ) 67
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 RequestBody, @ResponseBody
@RequestBody, @ResponseBody 웹시스템간의 XML, JSON 형식의데이터를주고받는경우 XML, JSON Java Object (unmarshalling) Java Object XML, JSON (marshalling) @RequestBody, @ResponseBody 어노테이션은 HTTP 메시지 Body 에 Java 객체를 XML 이나 JSON 등의타입으로변환하여담고, 역으로변환하는데사용 69
@RequestBody, @ResponseBody HttpMessageConverter 를이용한변환처리 주요 HttpMessageConverter 구현클래스 구현클래스 ByteArrayHttpMessageConverter (*) 설명 HTTP 메시지와 byte 배열사이의변환을처리. 컨텐츠타입은 application/octet-stream StringHttpMessageConverter (*) FormHttpMessageConverter (*) SourceHttpMessageConverter (*) MarshallingHttpMessageConverter MappingJacksonHttpMessageConverter HTTP 메시지와 String 사이의변환을처리한다. 컨텐츠타입은 text/plain:charset=iso-8859-1 HTML 폼데이터를 MultiValueMap 으로전달받을때사용컨텐츠타입은 application/x-wwwform-urlencoded HTTP 메시지와 javax.xml.transform.source 사이의변환을처리. 컨텐츠타입은 application/xml 또는 text/xml 스프링의 Marshaller 와 Unmarshaller 를이용해서 XML HTTP 메시지와객체사이의변환을처리한다. application/xml 또는 text/xml Jackson 라이브러리를이용해서 JSON HTTP 메시지와객체사이의변환을처리한다. 컨텐츠타입은 application/json AnnotationMethodHandlerAdapter 는 (*) 표시된클래스를기본적으로사용 70
@RequestBody, @ResponseBody Content Type 과 Accept 헤더기반의변환처리 @AnnotationMethodHandlerAdapter 가 HttpMessageConverter 를이용해서요청몸체데이터를 @RequestBody 어노테이션이적용된자바객체로변환할때에는 HTTP 요청헤더의 Content Type 헤더에명시된미디어타입 (MIME) 을지원하는 HttpMessageConverter 를구현체로사용 @ResponseBody 어노테이션을이용해서리턴하는객체를 HTTP 메시지의 body 로변환할때에는 HTTP 요청헤더의 Accept 헤더에명시된미디어타입을지원하는 HttpMessageConverter 구현체를선택한다. <script type= > xmlhttp.open( GET, json.do, true); xmlhttp.setrequestheader( Content-Type, application/x-www-form-rulencoded ); xmlhttp.setrequestheader( Accept, application/json ); xmlhttp.send(); json 타입의 response를 Accept함 </script> @RequestMapping(value= guestmessage/json.do, method=requestmethod.get, headers = accept=application/json ) @ResponseBody public GuestMessageList listjson() { 71
@ResponseBody 실습 전체레시피목록을 JSON 문자열로리턴하도록처리 72
@ResponseBody 실습 컨트롤러구현 @RequestMapping(value="json", produces={mediatype.application_json_value) public @ResponseBody List<Recipe> jsonlist() { List<Recipe> recipes = chef.showallrecipes() return recipes @ResponseBody 리턴하는객체가 HTTP 응답본문으로변환될것이라고설정 produces={mediatype.application_json_value 응답 MediaType 명시적지정 ( 여러개지정가능함 ) 생략하면변환가증한형태로디폴트로동작함 73
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 HandlerInterceptor 를통한요청가로채기 인터셉터호출순서지정 인터셉터 URL 패턴정의
HandlerInterceptor 를통한요청가로채기 컨트롤러가요청을처리하기전 / 후처리 로깅, 모니터링정보수집, 접근제어처리등의실제비즈니스로직과는분리되어처리해야하는기능들을넣고싶을때유용함 인터셉터를여러개껴놓을수있음 ( 순서주의!!) HandlerInterceptor 제공메서드 boolean prehandle(httpservletrequest request, HttpServletResponse response, Object handler) - false 를리턴하면 request 를바로종료한다. void posthandle(httpservletrequest request, HttpServletResponse response, Object handler, ModelAndView modelandview) - 컨트롤러수행후호출 void aftercompletion(httpservletrequest request, HttpServletResponse response, Object handler, Exception ex) - 뷰를통해클라이언트에응답을전송한뒤에실행 - 예외가발생하여도실행 75
HandlerInterceptor 를통한요청가로채기 HandlerInterceptor 인터페이스구현 HandlerInterceptorAdaptor 클래스제공 LoggingInterceptor.java package kr.or.kosta.interceptor import javax.servlet.http.httpservletrequest import javax.servlet.http.httpservletresponse import org.springframework.web.servlet.handlerinterceptor import org.springframework.web.servlet.modelandview public class LoggingInterceptor implements HandlerInterceptor { @Override public boolean prehandle(httpservletrequest request,httpservletresponse response, Object handler) throws Exception { System.out.println("intercept!! prehandle") return true @Override public void posthandle(httpservletrequest request,httpservletresponse response, Object handler,modelandview modelandview) throws Exception { System.out.println("intercept!! posthandle") @Override public void aftercompletion(httpservletrequest request,httpservletresponse response, Object handler, Exception ex) throws Exception { System.out.println("intercept!! aftercompletion") 76
HandlerInterceptor 를통한요청가로채기 인터셉터등록 : servlet context.xml <interceptors> <beans:bean class="kr.or.kosta.interceptor.logginginterceptor" /> </interceptors> 컨트롤러메소드전 / 후 / 응답완료후호출됨을확인 77
HandlerInterceptor 를통한요청가로채기 AnotherInterceptor.java 인터셉터추가 package kr.or.kosta.interceptor import javax.servlet.http.httpservletrequest import javax.servlet.http.httpservletresponse import org.springframework.web.servlet.handlerinterceptor import org.springframework.web.servlet.modelandview public class AnotherInterceptor implements HandlerInterceptor { @Override public boolean prehandle(httpservletrequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("another intercept!! prehandle") return true @Override public void posthandle(httpservletrequest request, HttpServletResponse response, Object handler, ModelAndView modelandview) throws Exception { System.out.println("another intercept!! posthandle") @Override public void aftercompletion(httpservletrequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("another intercept!! aftercompletion") 78
HandlerInterceptor 를통한요청가로채기 인터셉터등록 : servlet context.xml <interceptors> <beans:bean class="kr.or.kosta.interceptor.logginginterceptor" /> <beans:bean class="kr.or.kosta.interceptor.anotherinterceptor" /> </interceptors> 인터셉터 2 개등록수행결과 순서확인 79
인터셉터호출순서정리 인터셉터등록 : servlet context.xml <interceptors> <beans:bean class= AInterceptor /> <beans:bean class= BInterceptor /> <beans:bean class= CInterceptor /> </interceptors> 80
인터셉터 URL 패턴지정 인터셉터등록 : servlet context.xml <interceptors> <interceptor> <mapping path="/recipe*"/> <beans:bean class="kr.or.kosta.interceptor.logginginterceptor" /> </interceptor> <beans:bean class="kr.or.kosta.interceptor.anotherinterceptor" /> </interceptors> 81
2. Spring MVC 활용 2.1 Spring @MVC 2.2 애노테이션기반 2.3 HTML 폼과커맨드객체 2.4 View 2.5 Model 2.6 요청 URI 매칭 2.7 @RequestBody, @ResponseBody 2.8 인터셉터 2.9 예외처리 예외처리 @ExceptionHandler 에러페이지 SimpleMappingExceptionHandler
예외처리 @RequestMapping 메서드는모든타입의예외를발생시킬수있음. 웹브라우저에는 500 응답코드와서블릿컨테이너가출력한에러페이지출력 SpringMVC 를이용해서원하는에러페이지를보여주고싶다면 SpringMVC 가제공하는 HandlerExceptionResolver 인터페이스를사용한다. SpringMVC 가제공하는 HandlerExceptionResolver 인터페이스의구현체는 - AnnotationMethodHandlerExceptionResolver : @ExceptionHandler 어노테이션이적용된메서드를이용 - DefaultHandlerExceptionResolver : 스프링관련예외타입처리 - SimpleMappingExceptionResolver : 예외타입별로뷰이름을지정 - ResponseStatusExceptionResolver : 예외를특정 HTTP 응답상태코드로전환하여다순한 500 에러가아닌의미있는 HTTP 응답상태를리턴하는방법 83
@ExceptionHandler @ExceptionHandler 어노테이션을이용한예외처리 파라메터로받을수있는타입 - HttpServletRequest, HttpServletResponse, HttpSession - Locale, InputStream/Reader, OutputStream/Writer - 예외타입 리턴타입 - ModelAndView, Model, Map, View, String, void @Controller public class MyController { @RequestMapping( /user/{userid ) public ModelAndView getuser(@pathvariable String userid) { @ExceptionHandler(NullPointerException.class) public String handlenullpointerexception(nullpointerexception e) { return error/nullexception ; 84
에러페이지 error.jsp <%@ page contenttype="text/html charset=utf 8" pageencoding="utf 8"%> <html> <head> <title>error Page</title> </head> <body> <h1> Error!! </h1> <P> ${errormessage </P> </body> </html> 85
SimpleMappingExceptionResolver SimpleMappingExeceptionResolver 를이용한예외처리 예외타입별로에러페이지를지정할수있음 <beans:bean class="org.springframework.web.servlet.handler.simplemappingexceptionresolver"> <beans:property name="exceptionmappings"> <beans:props> <beans:prop key="java.lang.nullpointerexception">nullerror</beans:prop> <beans:prop key="java.lang.exception">error</beans:prop> </beans:props> </beans:property> </beans:bean> 86
토의 질의응답 토론 넥스트리소프트 ( 주 ) 김현오선임 (hyunohkim@nextree.co.kr) 87