1. 스프링(Spring) MVC 프레임워크(Model View Controller Framework)

- 스프링이 제공하는 트랜잭션 처리, DI, AOP를 손쉽게 사용

- 스트럿츠2와 같은 프레임워크와 연동이 쉬움




2. 스프링 MVC 주요 구성 요소 및 처리 흐름

- 다른 MVC 프레임워크와 마찬가지로 컨트롤러를 사용하여 요청을 처리

- 스프링에서는 DispatcherServlet이 MVC에서의 컨트롤러(Controller) 부분을 처리



 구성 요소

 설명 

 DispatcherServlet

 클라이언트의 요청을 전달받아 요청에 맞는 컨트롤러가 리턴한 결과값을 View에 전달하여 알맞은 응답을 생성 

 HandlerMapping

 클라이언트의 요청 URL을 어떤 컨트롤러가 처리할지 결정 

 Controller

 클라이언트의 요청을 처리한 뒤, 결과를 DispatcherServlet에게 리턴 

 ModelAndView

 컨트롤러가 처리한 결과 정보 및 뷰 선택에 필요한 정보를 담음 

 ViewResolver

 컨트롤러의 처리 결과를 생성할 뷰를 결정 

 View

 컨트롤러의 처리 결과 화면을 생성, JSP 또는 Velocity 템플릿 파일 등을 뷰로 사용 




3. 스프링 MVC 개발

- 클라이언트에 요청을 받을 DispatcherServlet을 web.xml 파일에 설정

- 클라이언트의 요청을 처리할 컨트롤러 작성

- ViewResolver 설정 (컨트롤러가 전달한 값을 이용해서 응답 화면을 생성할 뷰를 결정)

- JSP나 Velocity 등을 이용하여 뷰영역의 코드를 작성




3.1. DispatcherServlet 설정 및 스프링 컨텍스트 설정

- web.xml(자바 웹 어플리케이션 설정 파일)에 DipathcerServlet 설정 및 공통으로 사용할 어플리케이션 컨텍스트 설정

- <servlet> 태그를 이용하여 DispatcherServlet 설정

- <servlet-mapping> 태그를 이용하여 요청 URL 패턴 설정




	SpringBlog2
	
	
		dispatcher
		org.springframework.web.servlet.DispatcherServlet
	

	
		dispatcher
		*.do
	


3.2. 컨트롤러 구현 및 설정 추가

- @Controller 애노테이션을 클래스에 적용

- @RequestMapping 애노테이션을 이용해서 클라이언트의 요청을 처리할 메서드를 지정

- ModelAndView는 컨트롤러의 처리 결과를 보여줄 뷰와 뷰에서 출력할 모델을 지정

- DispatcherServlet은 스프링 컨테이너에서 컨트롤러 객체를 검색하기 때문에 스프링 설정 파일에 컨트롤러를 빈으로 등록해야 함

// HelloController.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

		@RequestMapping("/hello.do")
		public ModelAndView hello() {
			ModelAndView mv = new ModelAndView();
			mv.setViewName("hello");
			mv.addObject("message", "Hello Spring MVC");
			return mv;
		}
} // HelloController



	
	
	


3.3. 설정 파일 View Resolver 설정 추가

- 컨트롤러에서 ModelAndView.setViewName() 메서드로 뷰이름을 지정

- 이 뷰이름과 매칭되는 뷰 구현체를 찾기 위해 ViewResolver를 사용

- prefix 프로퍼티와 suffix 프로퍼티를 지정




	
	
	
	
		
		
	


3.4. 뷰 코드 구현

- ModelAndView.addObject() 메서드로 모델을 추가할 때 사용한 이름을 이용해서 모델의 값을 사용

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${ message }</title>
</head>
<body>
${ message }
</body>
</html>


3.5. 실행

- 컨트롤러의 지정된 URL 요청을 주소창에 입력




4. DispatcherServlet 설정과 ApplicationContext의 관계

- web.xml 파일에 한 개 이상의 DispatcherServlet 설정할 수 있으며, 각각 한 개의 WebApplicationContext를 갖음




4.1. DispatcherServlet 설정

- DipatcherServlet는 기본적으로 /WEB-INF/ 디렉터리에 위치한 [서블릿이름]-servlet.xml 파일로부터 스프링 설정 정보를 읽어 옴

- 한개 이상의 설정 파일, 또는 설정 다른 이름의 설정 파일을 사용할 경우 contextConfigLocation 초기화 파라미터에 설정 파일 목록을 지정

- 설정 파일 구분은 콤마(","), 공백 문자(" "), 탭(\t), 줄 바꿈(\n), 세미콜론(";")을 이용




	SpringBlog2
	
	
		dispatcher
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			
				/WEB-INF/beans.xml
				/WEB-INF/main.xml
			
		
	

	
		dispatcher
		*.do
	

4.2. ApplicationContext 설정

- 한 개 이상의 DispatcheerServlet을 사용할 때 별도의 WebApplicationContext를 생성

- 이 경우 공통 빈을 필요로 할 때 ContextLoaderListener를 사용하여 사용될 빈을 설정

- ServletListener로 등록, contextConfigLocation 컨텍스트 파라미터를 이용, 공통으로 사용될 빈 정보를 담고 있는 설정 파일 목록을 지정

- ContextLoaderListener가 생성하는 WebApplicationContext는 웹 어플리케이션의 루트 컨텍스트

- DispatcherServlet이 생성하는 WebApplicationContext는 루트 컨텍스트를 부모로 사용하는 자식 컨텍스트

- ContextLoaderListener는 contextConfigLocation 컨텍스트 파라미터를 명시하지 않으면 /WEB-INF/applicationContext.xml을 설정 파일로 사용




	SpringBlog2
	
	
	
	
		contextConfigLocation
		
			/WEB-INF/front.xml
			/WEB-INF/service.xml
		
	

	
		org.springframework.web.context.ContextLoaderListener
	
	
	
		front
		org.springframework.web.servlet.DispatcherServlet
	
	
	
		rest
		org.springframework.web.servlet.DispatcherServlet
	
	
	
		dispatcher
		*.do
	




5. 캐릭터 인코딩 처리 필터 설정

- 모든 컨트롤러에서 response.setCharacterEncoding() 코드를 실행하는 번거로움을 해소하기 위해 서블릿 필터를 이용 처리

- 서블릿 필터에서 캐틱터 인코딩을 설정할 수 있는 CharacterEncodingFilter 클래스를 제공

- web.xml 파일에 설정




	SpringBlog2
	
	
		dispathcer
		org.springframework.web.servlet.DispatcherServlet
	
	
	
		encodingFilter
		org.springframework.web.filter.CharacterEncodingFilter
		
			encoding
			UTF-8
		
	
	
	
		encodingFilter
		/*
	
	
	
		dispatcher
		*.do
	




6. 컨트롤러 구현

- 스프링 3.0 버전 부터는 @Controller 애노테이션을 이용해서 컨트롤러 클래스를 구현하도록 권장




6.1. @Controller 애노테이션, @RequestMapping 애노테이션

- 컨트롤러 클래스에 @Controller 애노테이션 적용

- 클라이언트의 요청을 처리할 메서드에 @RequestMapping 애노테이션 적용

- 설정 파일에 컨트롤러 클래스를 빈으로 등록

- @RequestMapping 애노테이션은 해당 메서드에서 처리할 URI를 값으로 갖음

- @RequestMapping 애노테이션의 메서드의 리턴 타입은 상황에 따라 알맞은 타입을 선택


6.2. 컨트롤러 메서드 HTTP 전송 방식(method) 한정

- 하나의 요청 URL에 대해 HTTP GET 요청과 POST 요청을 한 개의 컨트롤러에서 처리할 경우

- @RequestMapping 애노테이션의 method 속성을 이용해서 메서드가 처리할 HTTP 메서드를 제한

// HelloController.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HelloController {

		@RequestMapping(value="/write.do", method=RequestMethod.GET)
		public String writeForm() {
			return "writeForm";
		}
		
		@RequestMapping(value="/write.do", method=RequestMethod.POST)
		public String writeSub() {
			return "writeSub";
		}
} // HelloController

- 두 메서드가 동일한 URI를 처리하는 경우 @RequestMapping 애노테이션을 클래스에 적용해서 해당 클래스가 처리할 기본 URI를 지정가능

- @RequestMapping 애노테이션에 method 속성을 설정하지 않을 경우 모든 HTTP 전송방식(GET, POST, DELETE 등)을 처리

// HelloController2.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/write.do")
public class HelloController2 {

		@RequestMapping(method=RequestMethod.GET)
		public String writeForm() {
			return "writeForm";
		}
		
		@RequestMapping(method=RequestMethod.POST)
		public String writeSub() {
			return "writeSub";
		}
} // HelloController2

6.3. HTML 폼, 커맨드 객체

- HTML 폼에 입력한 데이터를 자바빈 객체를 이용해서 전달 받음

- HTML 폼의 항목 이름과 자바빈 클래스의 프로퍼티 이름이 일치할 경우 폼에 입력한 값을 프로퍼티 값으로 설정

- @RequestMapping 애노테이션이 적용된 메서드의 파라미터로 자바빈 타입을 추가

- 폼에 입력한 값은 모두 문자열이지만 스프링은 자바빈의 타입 변환 처리 기능으로 프로퍼티의 타입으로 알맞게 변환


<!-- writeForm.jsp -->

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>WriteForm</title>
</head>
<body>
	<form action="write.do" method="post">
		<input type="text" name="name" placeholder="폰 이름" required="required" size="50"><br>
		<input type="text" name="manufacturer" placeholder="폰 제조사" required="required" size="50"><br>
		<input type="text" name="price" placeholder="폰 가격" required="required" size="50"><br>
		<input type="submit" value="작성"><input type="reset" value="취소">
	</form>
</body>
</html>


// PhoneDTO.java

package com.tistory.gangzzang.model;

public class PhoneDTO {
	private String name;
	private String manufacturer;
	private int price;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getManufacturer() {
		return manufacturer;
	}

	public void setManufacturer(String manufacturer) {
		this.manufacturer = manufacturer;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}
} // PhoneDTO
// PhoneController.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.tistory.gangzzang.model.PhoneDTO;

@Controller
public class PhoneController {

	@RequestMapping(value = "/write.do", method = RequestMethod.GET)
	public String writeForm() {
		return "writeForm";
	}

	@RequestMapping(value = "/write.do", method = RequestMethod.POST)
	public String wirteSubmit(PhoneDTO phoneDTO) {
		return "writeSubmit";
	}
} // PhoneController

6.3.1. 뷰에서 커맨드 객체 접근

- 뷰에서 @RequestMapping 애노테이션 메서드에서 전달받은 커맨드 객체(자바빈 객체)의 클래스명을 이용해서 접근 가능(단 첫글자는 소문자)


<!-- writeSubmit.jsp -->

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>WriteSubmit</title>
</head>
<body>
	<input type="text" name="name" placeholder="폰 이름" required="required" readonly="readonly" size="50" value="${ phoneDTO.name }"><br>
	<input type="text" name="manufacturer" placeholder="폰 제조사" required="required" readonly="readonly" size="50" value="${ phoneDTO.manufacturer }"><br>
	<input type="text" name="price" placeholder="폰 가격" required="required" readonly="readonly" size="50" value="${ phoneDTO.price }"><br>
</body>
</html>


- 뷰에서 사용할 모델의 이름을 변경할 때는 @ModelAttribute 애노테이션을 이용, 커맨드 객체의 모델 이름을 지정

// PhoneController.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.tistory.gangzzang.model.PhoneDTO;

@Controller
public class PhoneController {

	@RequestMapping(value = "write.do", method = RequestMethod.GET)
	public String writeForm() {
		return "writeForm";
	}

	@RequestMapping(value = "write.do", method = RequestMethod.POST)
	public String wirteSubmit(@ModelAttribute("p") PhoneDTO phoneDTO) {
		return "writeSubmit";
	}
} // PhoneController

<!-- writeSubmit.jsp --> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>WriteSubmit</title> </head> <body> <input type="text" placeholder="폰 이름" required="required" readonly="readonly" size="50" value="${ p.name }"><br> <input type="text" placeholder="폰 제조사" required="required" readonly="readonly" size="50" value="${ p.manufacturer }"><br> <input type="text" placeholder="폰 가격" required="required" readonly="readonly" size="50" value="${ p.price }"><br> </body> </html>


6.3.2. 커맨드 객체로 List 받기

- 폼에서 "프로퍼티명[인덱스].프로퍼티" 같이 입력 폼의 이름을 구성

// PhoneVO.java

package com.tistory.gangzzang.model;

import java.util.List;

public class PhoneVO {
	private List<PhoneDTO> phoneItems;

	public List<PhoneDTO> getPhoneItems() {
		return phoneItems;
	}

	public void setPhoneItems(List<PhoneDTO> phoneItems) {
		this.phoneItems = phoneItems;
	}
} // PhoneVO
<!-- writeListForm.jsp -->

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>WriteListForm</title>
</head>
<body>
	<form action="writeList.do" method="post">
		<input type="text" name="phoneItems[0].name" placeholder="폰 이름1" required="required" size="50"><br>
		<input type="text" name="phoneItems[0].manufacturer" placeholder="폰 제조사1" required="required" size="50"><br>
		<input type="text" name="phoneItems[0].price" placeholder="폰 가격1" required="required" size="50"><br>
		<hr>
		<input type="text" name="phoneItems[1].name" placeholder="폰 이름2" required="required" size="50"><br>
		<input type="text" name="phoneItems[1].manufacturer" placeholder="폰 제조사2" required="required" size="50"><br>
		<input type="text" name="phoneItems[1].price" placeholder="폰 가격2" required="required" size="50"><br>
		<hr>
		<input type="submit" value="작성"><input type="reset" value="취소">
	</form>
</body>
</html>
// PhoneController.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.tistory.gangzzang.model.PhoneDTO;
import com.tistory.gangzzang.model.PhoneVO;

@Controller
public class PhoneController {

	@RequestMapping(value = "write.do", method = RequestMethod.GET)
	public String writeForm() {
		return "writeForm";
	}

	@RequestMapping(value = "write.do", method = RequestMethod.POST)
	public String wirteSubmit(@ModelAttribute("p") PhoneDTO phoneDTO) {
		return "writeSubmit";
	}
	
	@RequestMapping(value = "writeList.do", method = RequestMethod.GET)
	public String writeListForm() {
		return "writeListForm";
	}

	@RequestMapping(value = "writeList.do", method = RequestMethod.POST)
	public String wirteListSubmit(@ModelAttribute("phone") PhoneVO phoneVO) {
		return "writeListSubmit";
	}
} // PhoneController

6.4. 컨트롤러 메서드 파라미터 타입

- 컨트롤러의 @RequestMapping 애노테이션이 적용된 메서드는 커맨드 클래스외에도 다양한 타입의 파라미터를 가질 수 있음


 파라미터 타입

 설명 

 HttpServletRequest, HttpServletResponse, HttpSession 

 서블릿 API 

 java.util.Locale

 현재 요청에 대한 Locale 

 InputStream, Reader

 요청 컨텐츠에 직접 접근할 때 사용 

 OutputStream, Writer 

 응답 컨텐츠를 생성할 때 사용 

 @PathVariable 애노테이션 적용 파라미터 

 URI 템플릿 변수에 접근할 때 사용 

 @RequestParam 애노테이션 적용 파라미터 

 HTTP 요청 파라미터를 매핑 

 @RequestHeader 애노테이션 적용 파라미터 

 HTTP 요청 헤더를 매핑 

 @CookieValue 애노테이션 적용 파라미터 

 HTTP 쿠키 매핑 

 @RequestBody 애노테이션 적용 파라미터 

 HTTP 요청의 몸체 내용에 접근할 때 사용(HttpMessageConverter를 이용, HTTP 요청 데이터를 해당 타입으로 변환) 

 Map, Model, modelMap

 뷰에 전달할 모델 데이터를 설정할 때 사용 

 커맨드 객체 

 HTTP 요청 파라미터를 저장한 객체, 클래스이름을 모델명으로 사용(@ModelAttribute 애노테이션으로 모델명 설정)

 Errors, BindingResult

 HTTP 요청 파라미터를 커맨드 객체에 저장한 결과(커맨드 객체를 위한 파라미터 바로 다음에 위치) 

 SessionStatus 

 폼 처리 완료 처리하기 위해 사용(@SessionAttributes 애노테이션을 명시한 session 속성을 제거 이벤트 발생) 


6.4.1. @RequestParam 애노테이션 이용 파라미터 매핑

- HTTP 요청 파라미터를 메서드의 파라미터로 전달받을 때 사용

- 애노테이션이 적용된 파라미터가 String이 아닐 경우 실제 타입에 따라 알맞게 타입 변환 수행

- 필수가 아닌 파라미터인 경우 required 속성 값을 false로 지정 (기본 값은 true), 값은 null로 저장

- 필수가 아닌 파라미터인 경우 defaultValue 속성 값으로 기본값을 지정

// ParamController.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class ParamController {

		@RequestMapping(value = "param.do", method = RequestMethod.GET)
		public String param(
				@RequestParam(value="name", required=false) String name,
				@RequestParam(value="age", defaultValue="1") int age) {
			return "param";
		}
} // ParamController


6.4.2. @CookieValue 애노테이션 이용 쿠키 매핑

- 쿠키 값을 파라미터로 전달 받을 때 사용

- 해당 쿠키가 존재하지 않으면 500 응답 에러 코드 전송

- 쿠키가 필수가 아닌 경우 파라미터와 같이 required 속성의 값을 false로 지정 (기본값은 true), 값은 null로 저장

- 필수가 아닌 쿠키인 경우 defaultValue 속성 값으로 기본값을 지정

// CookieController.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class CookieController {
	
	@RequestMapping(value = "cookie.do", method = RequestMethod.GET)
	public String cookie(
			@CookieValue(value="name", required=false) String name,
			@CookieValue(value="age", defaultValue="1") int age) {
		return "cookie";
	}
} // CookieController

6.4.3. @RequestHeader 애노테이션 이용 헤더 매핑

- HTTP 요청 헤더의 값을 메서드의 파라미터로 전달 받을 때 사용

- 해당 헤더가 존재하지 않으면 500 응답 에러 코드 전송

- required 속성과 defaultValue 속성을 위와 같이 사용 가능

// HeaderController.java

package com.tistory.gangzzang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HeaderController {
	
	@RequestMapping(value = "header.do", method = RequestMethod.GET)
	public String header(
			@RequestHeader(value="Accept-Language", required=false) String languageHeader,
			@RequestHeader(value="user-agent", defaultValue="모름") String agentHeader) {
		return "header";
	}
} // HeaderController

6.4.4. 서블릿 API 직접 사용

- javax.servlet.http.HttpServletRequest / javax.servlet.ServletRequest

- javax.servelt.http.HttpServletResponse / javax.servlet.ServletResponse

- javax.servlet.http.HttpSession

- 스프링 MVC 제공 애노테이션을 이용 정보(파라미터, 헤더, 쿠키, 세션 등)에 접근 할 수 있기 때문에 직접적으로 서블릿 API를 사용해야 하는 경우는 드뭄

- 세가지 경우에 사용(HttpSession의 생성을 직접 제어, 컨트롤러에서 쿠키를 생성, 서블릿 API 사용을 선호)


6.5. 컨트롤러 메서드 리턴 타입

- ModelAndView : 뷰 정보 및 모델 정보를 담고 있는 ModelAndView 객체

- Model : 뷰에 전달할 객체 정보를 담고 있는 Model을 리턴, 뷰 이름은 요청 URL로 부터 결정(RequestTOViewNameTranslator를 통해 뷰 결정)

- Map : 뷰에 전달할 객체 정보를 담고 있는 Map을 리턴, 뷰 이름은 요청 URL로 부터 결정(RequestTOViewNameTranslator를 통해 뷰 결정)

- String : 뷰 이름을 리턴

- View 객체 : View 객체를 직접 리턴, 해당 View 객체를 이용해서 뷰를 생성

- void : 메서드가 ServletResponse나 HttpServletResponse 타입의 파라미터를 갖는 경우 메서드가 직접 응답을 처리한다고 가정, 그렇지 않을 경우 요청 뷰 이름은 요청 URL로부터 결정

- @ResponseBody : 메서드에서 @ResponseBody 애노테이션이 적용된 경우 리턴 객체를 HTTP 응답으로 전송, HttpMessageConverter를 이용해서 객체를 HTTP 응답 스트림으로 변환


6.6. 컨트롤러 클래스 자동 스캔

- @Controller 애노테이션은 @Component 애노테이션과 마찬가지로 컴포넌트 스캔 대상

- <context:component-scan> 태그를 이용 컨트롤러 클래스를 자동으로 로딩

- base-package 속성에 자동 스캔할 패키지 경로를 입력

- 복수의 패키지를 사용하고 싶은 경우 <context:component-scan> 태그를 복수개 작성




	


+ Recent posts