@RequestMapping
@RequestMapping 애노테이션은 요청 url을 특정 컨트롤러로 맵핑한다.
기본 매핑
package hello.springmvc.basic;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class TestController {
/**
* 기본 요청
* 둘 다 허용한다 /hello-basic, /hello-basic/
* HTTP 메서드 모두 허용 GET, POST, HEAD, PUT, PATCH, DELETE
* {"/hello-basic", "/hello-go"}와 같이 다중 설정도 가능하다.
* method 속성으로 HTTP 메서드를 지정하지 않으면 모든 메서드에 무관하게 호출된다.
*/
@RequestMapping("/hello-basic")
public String helloBasic() {
log.info("helloBasic");
return "ok";
}
}
HTTP 메서드 매핑 축약
@Slf4j
@RestController
public class TestController {
/**
* PathVariable 사용
* 변수명이 같으면 생략 가능
*
* @PathVariable("userId") String userid -> @PathVariable userId
*/
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String userId) {
log.info("mappingPath userId={}", userId);
return "ok";
}
}
- 다른 메서드로 요청하면 405 에러 코드(Method Not Allowd)가 반환된다.
- 축약 애노테이션 내부에 @RequestMapping과 method를 미리 지정해 두었다.
PathVariable(경로 변수)를 사용한 매핑
@Slf4j
@RestController
public class TestController {
/**
* PathVariable 사용
* 변수명이 같으면 생략 가능
*
* @PathVariable("userId") String userid -> @PathVariable userId
*/
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String userId) {
log.info("mappingPath userId={}", userId);
return "ok";
}
}
PathVariable 다중 사용
@Slf4j
@RestController
public class TestController {
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable String userId, @PathVariable String orderId) {
log.info("mappingPath userId={}, orderId={}", userId, orderId);
return "ok";
}
}
특정 헤더 조건 매핑
/**
* 특정 헤더로 추가 매핑
* headers="mode",
* headers="!mode",
* headers="mode=debug",
* headers="mode!=debug"
*/
@GetMapping(value = "/mapping-header", headers = "mode-debug")
public String mappingHeader() {
log.info("mappingHeader");
return "ok";
}
미디어 타입 조건 매핑 - Content-type
/**
* Content-Type 헤더 기반 추가 매핑 Media Type
* consumes="application/json",
* consumes="!application/json",
* consumes="application/*",
* consumes="*\/*"
* MediaType.APPLICATION_JSON_VALUE
*/
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
log.info("mappingConsumes");
return "ok";
}
미디어 타입 조건 매핑 - Accept
/**
* Accept 헤더 기반 Media Type
* produces="text/html",
* produces="!text/html",
* produces="text/*",
* produces="*\/*"
*/
@PostMapping(value = "/mapping-produces", produces = "text/html")
public String mappingProduces() {
log.info("mappingProduces");
return "ok";
}
HTTP 요청 정보 가져오기
애노테이션 기반의 스프링 컨트롤러는 아래와 같이 다양한 매개변수를 지원한다. 다양한 매개변수를 통해 HTTP 요청 정보를 편리하게 가져올 수 있다.
package hello.springmvc.basic;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.util.MultiValueMap;
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.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
@Slf4j
@RestController
public class TestController {
@RequestMapping("/headers")
public String headers(HttpServletRequest request,
HttpServletResponse response,
HttpMethod httpMethod,
Locale locale,
@RequestHeader MultiValueMap<String, String> headerMap,
@RequestHeader("host")String host,
@CookieValue(value = "myCookie", required = false)String cookie) {
log.info("request = {}", request);
log.info("response = {}", response);
log.info("httpMethod = {}", httpMethod);
log.info("locale = {}", locale);
log.info("headerMap = {}", headerMap);
log.info("host = {}", host);
log.info("cookie = {}", cookie);
return "ok";
}
}
- HttpMethod : HTTP 메서드
- Locale locale : Locale 정보(ko-kr, euc-kr ···)
- @RequestHeader MultiValueMap<String, String> headerMap : 모든 HTTP 요청 헤더를 MultiValueMap 형식으로 조회한다.
- @RequestHeader("host") String host : 특정 HTTP 헤더를 조회한다. 속성으로 필수 값 여부를 지정하는 required와 기본 값을 지정하는 defaultValue 등이 있다.
- @CookieValue(value="myCookie", required=false)String cookie : 특정 쿠키를 조회한다.
HTTP 요청 파라미터 - @RequestParam
GET와 POST HTML Form으로 전송한 요청 파라미터는 아래의 방법으로 가져올 수 있다.
1. HttpServletRequest 객체의 getParameter() 메서드
@RequestMapping("/request-param-v1")
public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
log.info("username={}, age={}", username, age);
response.getWriter().write("ok");
}
2. 스프링이 제공하는 애노테이션인 @RequestParam을 사용하면 매개변수 레벨에서 바로 파라미터를 꺼낼 수 있다.
@RequestMapping("/request-param-v2")
@ResponseBody
public String requestParamV2(@RequestParam("username") String memberName, @RequestParam("age") int memberAge) {
log.info("username={}, age={}", memberName, memberAge);
return "ok";
}
- @ResponseBody : @RestController와 같이 View 조회를 무시하고, HTTP 응답 메시지 바디에 직접 내용을 입력
아래와 같이 HTTP 파라미터 이름이 변수 이름과 같을 경우 파라미터 속성 생략이 가능하다.
@RequestMapping("/request-param-v3")
@ResponseBody
public String requestParamV3(@RequestParam String username, @RequestParam int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
아래와 같이 변수 타입이 String, int, Interger 등의 단순 타입이면 @RequestParam도 생략 가능하다.
@RequestMapping("/request-param-v4")
@ResponseBody
public String requestParamV4(String username, int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
@RequestParam 애노테이션은 required 속성과 defaultValue 속성 등이 있다. required 속성이 true이면 해당 파라미터가 반드시 존재해야 된다는 제약이 걸린다.
- 해당 파라미터를 보내주지 않으면 400 에러 코드(BAD_REQUEST)가 발생한다.
- 문자열 파라미터를 공백(ex : username=)으로 전송하면 빈 문자열로 통과된다.
- 원시 타입은 NULL이 들어갈 수 없어서 required 속성이 false여도 500 에러가 발생하므로 Integer 같은 wrapper 타입을 사용해야 한다.
- 속성의 디폴트 값은 true이다.
@RequestMapping("/request-param-required")
@ResponseBody
public String requestParamRequired(@RequestParam(required = true) String username,
@RequestParam(required = false) Integer age) {
log.info("username={}, age={}", username, age);
return "ok";
}
defaultValue 속성은 요청 파라미터 값을 전달하지 않은 경우 기본값을 넣어준다.
@RequestMapping("/request-param-default")
@ResponseBody
public String requestParamDefault(@RequestParam(defaultValue = "catsbi") String username,
@RequestParam(defaultValue = "20") int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
- 기본 값이 있기에 required 속성은 의미가 없어 빼도 된다.
- 공백(ex : age=)의 경우에도 설정한 기본 값이 적용된다. 위의 코드에서 age 파라미터를 공백으로 보내면 int 자료형은 NULL을 받아들일 수 없어 에러가 나야 하지만 defaultValue로 설정한 값이 적용되어 age에 20이 주입된다.
Map 또는 MultiValueMap으로 요청 파라미터의 값을 한번에 받을 수 있다.
@RequestMapping("/request-param-map")
@ResponseBody
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
HTTP 요청 파라미터 - @ModelAttribute
@RequestParam을 사용하면 요청 파라미터를 하나씩 받을 수 있다. 하지만 요청 파라미터가 하나의 객체가 돼야 하는 경우 각각의 파라미터를 조회해 객체에 값을 넣어서 생성해 주는 작업이 번거롭다. 이럴 때 @ModelAttribute 애노테이션을 사용하면 편리하다.
HelloDate 객체
package hello.springmvc.basic;
import lombok.Data;
@Data
public class HelloData {
private String username;
private int age;
}
@ModelAttribute 애노테이션을 이용해 요청 파라미터를 객체로 바인딩
@RequestMapping("/model-attribute-v1")
@ResponseBody
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return helloData.toString();
}
스프링 MVC는 @ModelAttribute가 있으면 다음 과정을 수행한다.
- HelloData 객체 생성
- 요청 파라미터의 이름으로 HelloData 객체의 setter 메서드를 호출해 파라미터의 값을 바인딩한다. (파라미터 이름이 username이면 setUsername() 메서드를 찾아 호출한다.)
따라서 바인딩 객체는 setter 메서드를 가지고 있어야한다.
HTTP 요청 메시지 바디 - 단순 텍스트
HTTP 요청을 보낼 때 데이터를 요청 파라미터로 보내는 경우도 있지만 HTTP 메시지 바디에 데이터를 직접 담아서 요청하는 경우도 많다. HTTP API에서 주로 사용하며 JSON, XML, TEXT 형식의 데이터를 HTTP 메시지 바디에 담는다. 아래의 방법으로 HTTP 요청 메시지 바디를 문자열로 가져올 수 있다.
HttpServletRequest 객체로 요청 메세지 바디를 문자열로 변환해 가져올 수 있다.
@PostMapping("/request-body-string-v1")
public void requestBodyString(HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletInputStream inputStream = request.getInputStream();
String string = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody={}", string);
response.getWriter().write("ok");
}
매개변수에서 바로 InputStream 객체와 Writer 객체를 받을 수도 있다.
@PostMapping("/request-body-string-v2")
public void requestBodyStringV2(InputStream inputStream, Writer writer) throws IOException {
String string = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody={}", string);
writer.write("ok");
}
HttpEntity를 사용해서 더 편리하게 조회가 가능하다.
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity){
log.info("messageBody={}", httpEntity.getBody());
return new HttpEntity<>("ok");
}
@RequestBody 애노테이션을 사용해 요청 메시지 바디만을 받을 수 있다. 바디가 아니라 헤더 정보가 필요하면 HttpEntity나 @RequestHeader 애노테이션을 사용하면 된다.
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String body){
log.info("messageBody={}", body);
return "ok";
}
HTTP 요청 메시지 바디 - JSON
HTTP 요청 메시지 바디에는 주로 JSON 데이터를 넣는다. 아래의 방법으로 바디에 담긴 JSON 데이터를 객체로 바인딩 할 수 있다.
요청 메시지 바디를 문자열로 읽어와 Jackson 라이브러리인 ObjectMapper를 사용해 객체로 변환한다.
@PostMapping("/request-body-json-v1")
public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
ObjectMapper objectMapper = new ObjectMapper();
log.info("messageBody={}", messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("helloDate = {}", helloData.toString());
response.getWriter().write("ok");
}
@RequestBody를 사용하면 바로 객체로 바인딩 할 수 있다. 이때 요청의 content-type이 application/json 이어야 JSON을 처리하는 HTTP 메시지 컨버터인 MappingJackson2HttpMessageConverter가 동작하고 컨버터는 내부적으로 ObjectMapper를 사용해 객체로 변환시켜준다.
@PostMapping("/request-body-json-v2")
@ResponseBody
public String requestBodyJsonV3(@RequestBody HelloData helloData){
log.info("helloDate = {}", helloData.toString());
return "ok";
}
바인딩 객체는 setter 메서드 없이 getter 메서드만 있어도 된다. setter 메서드 없이도 ObjectMapper가 변환을 해준다. 따라서 JSON을 받을 DTO 객체에 굳이 setter를 넣어주지 않아도 된다.
HTTP 응답 - 정적 리소스, 뷰
스프링에서 HTTP 응답 메시지를 만드는 방식은 크게 세 가지가 있다.
- 정적 리소스 : 정적 리소스 (HTML, CSS, JS···)를 보낸다.
- 뷰 템플릿 : 뷰 템플릿 엔진으로 동적 HTML을 만들어 보낸다.
- HTTP API : 응답 메시지 바디에 데이터를 넣어 보낸다.
1. 정적 리소스
스프링 부트는 클래스패스에 다음 디렉터리에 있는 정적 리소스를 제공한다. 해당 디렉터리에 있는 리소스는 컨트롤러를 통하지 않고 바로 가져올 수 있다.
- /static
- /public
- /resources
- /META-INF/resources
src/main/resources는 리소스를 보관하는 곳이며 클래스패스의 시작 경로이다.
2. 뷰 템플릿
뷰가 HTML을 동적으로 생성하며 이를 응답한다. Thymeleaf, jsp 등의 뷰 템플릿 엔진을 사용한다. Thymleaf로 간단한 페이지를 만드는 과정을 살펴보자
뷰 템플릿 생성
스프링 부트는 기본적으로 src/main/resources/templates 위치에서 뷰 템플릿을 찾는다.
경로 : src/main/resources/templates/response/hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p th:text="${data}">empty</p>
</body>
</html>
ResponseViewController (위의 hello 뷰를 호출하는 컨트롤러)
package hello.springmvc.basic.response;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class ResponseViewController {
@RequestMapping("/response-view-v1")
public ModelAndView responseViewV1() {
ModelAndView mv = new ModelAndView("/response/hello")
.addObject("data", "hello");
return mv;
}
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model) {
model.addAttribute("data", "hello");
return "response/hello";
}
}
- ModelAndView 반환 : 뷰 리졸버가 ModelAndView 객체의 논리적 뷰 이름(response/hello)으로부터 뷰를 찾은 뒤 렌더링한다.
- String 반환 : @ResponseBody 또는 @RestController가 없으면 논리적 뷰 이름(리턴 문자열)으로부터 뷰를 찾은 뒤 렌더링한다. @ResponseBody 또는 @RestController가 있으면 HTTP 메시지 바디에 response/hello 문자를 넣어 응답한다. (뷰를 찾고 렌더링 하는 과정이 없다.)
스프링 부트는 자동으로 ThymeleafViewResolver와 필요한 객체들을 스프링 빈으로 등록한다. 스프링 설정을 통해 해당 뷰 리졸버에서 물리적 뷰 이름을 완성할 때 사용하는 접두사나 접미사를 변경할 수도 있다.
#아래 설정은 기본값이기에 변경이 필요할때만 사용한다.
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
HTTP 응답 - HTTP API
HTTP API를 응답하는 경우 HTTP 응답 메시지 바디에 데이터를 넣어 보낸다.
package hello.springmvc.basic.response;
import hello.springmvc.basic.HelloData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Controller
public class ResponseBodyController {
@GetMapping("/response-body-string-v1")
public void responseBodyV1(HttpServletResponse response) throws IOException {
response.getWriter().write("ok");
}
@GetMapping("/response-body-string-v2")
public ResponseEntity<String> responseBodyV2() {
return new ResponseEntity<>("ok", HttpStatus.OK);
}
@GetMapping("/response-body-string-v3")
@ResponseBody
public String responseBodyV3() {
return "ok";
}
@GetMapping("/response-body-json-v1")
public ResponseEntity<HelloData> responseBodyJsonV1() {
HelloData helloData = new HelloData();
helloData.setUsername("catsbi");
helloData.setAge(20);
return new ResponseEntity<>(helloData, HttpStatus.OK);
}
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json-v2")
public HelloData responseBodyJsonV2() {
HelloData helloData = new HelloData();
helloData.setUsername("catsbi");
helloData.setAge(20);
return helloData;
}
}
- responseBodyV1 : HttpServletResponse 객체를 통해 응답 메시지를 만든다.
- responseBodyV2 : HttpEntity 객체를 반환해 응답 메시지를 만든다. ResponseEntity는 HttpEntity를 상속받았는데 HTTP 응답 코드가 추가되었다.
- responseBodyV3 : @ResponseBody 애노테이션으로 응답 메세지 바디에 데이터를 넣어준다.
- responseBodyJsonV1 : ResponseEntity 객체를 반환해 응답 메세지를 만든다.
- responseBodyJsonV2 : @ResponseBody 애노테이션으로 응답 메세지 바디에 데이터를 넣어준다. ResponseEntity는 HTTP 응답 코드를 설정할 수 있는 반면 @ResponseBody는 설정하기가 까다롭다. 그래서 @ResponseStatus 애노테이션으로 상태코드를 설정할 수 있지만 정적으로 상태코드를 작성해 유연하지 못하다.
HTTP 메시지 컨버터
@RequestBody, HttpEntity 등으로 요청 메시지 바디의 내용을 객체로 변환하고 컨트롤러의 인자로 받아 사용할 수 있었다. 또한 @ResponseBody, HttpEntity 등으로 객체를 리턴해 응답 메시지를 생성할 수 있었다. 이것은 HTTP 메시지 컨버터 덕분이다. HTTP 메시지 컨버터는 요청 메시지로부터 객체로 또는 객체를 응답 메시지로 변환하는 역할을 맡는다.
HTTP 메시지 컨버터 인터페이스
org.springframework.http.converter.HttpMessageConverter
package org.springframework.http.converter;
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
- canRead(), canWrite() : 메시지 컨버터가 해당 클래스, 미디어 타입을 지원하는지 체크하는 메서드
- read(), write() : 메시지를 변환하는 메서드
스프링 부트는 아래의 메시지 컨버터를 빈 객체로 등록한다. (일부만 표시했다.)
0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter
ByteArrayHttpMessageConverter
- 클래스 타입 : byte[], 미디어타입 : */*(전체)
- 요청 예) @RequestBody byte[] date
- 응답 예) @ResponsseBody return byte[], 미디어 타입 : application/octet-stream
StringHttpMessageConverter
- 클래스 타입 : String, 미디어 타입 : */*(전체)
- 요청 예) @RequestBody String data
- 응답 예) @ResponseBody return "ok", 미디어 타입 : text/plain
MappingJackson2HttpMessageConverter
- 클래스 타입 : 객체 또는 HashMap, 미디어 타입 : application/json 관련
- 요청 예) @RequestBody HelloData data
- 응답 예) @ResponseBody return helloData, 미디어 타입 : application/json
HTTP 요청 데이터 읽기, HTTP 응답 데이터 생성
HTTP 요청 데이터 읽기
- 컨트롤러에서 @RequestBody, HttpEntity 등의 인자를 사용한다.
- 등록된 메시지 컨버터를 순회하며 canRead() 메서드를 호출한다. → 대상 클래스 타입과 Content-Type 미디어 타입을 지원하는지 체크한다.
- canRead() 메서드의 조건을 만족하면 read() 메서드를 호출해 객체를 생성 및 반환한다.
HTTP 응답 데이터 생성
- 컨트롤러에서 @ResponseBody, HttpEntity 등으로 값을 반환한다.
- 등록된 메시지 컨버터를 순회하며 canWrite()를 호출한다. → 대상 클래스 타입과 Accept 미디어 타입(정확히는 @RequestMapping의 Produces)을 지원하는지 체크한다.
- canWrite() 조건을 만족하면 write() 메서드를 호출해 리턴 값을 적절하게 변환시켜 HTTP 응답 메세지 바디에 넣는다.
메세지 컨버터 동작 시점
메세지 컨버터는 언제 동작될까? 아래 그림은 @RequestMapping 애노테이션을 처리해 주는 핸들러 어댑터인 RequestMappingHandlerAdapter의 동작 방식이다. RequestMappingHandlerAdapter가 컨트롤러를 호출하기 전에 ArgumentResolver를 호출하고 컨트롤러에서 핸들러 어댑터로 반환되기 전에 ReturnValueHandler가 호출된다.
ArgumentResolver
애노테이션 기반의 컨트롤러는 HttpServletRequest, Model, @RequestParam, @RequestBody 등 매우 다양한 인자를 사용할 수 있다. 이것이 가능한 이유는 바로 ArgumentResolver 덕분이다. ArgumentResolver는 컨트롤러가 필요로 하는 다양한 인자의 값을 생성한다. 그리고 인자의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다. 스프링은 30개가 넘는 ArgumentResolver를 기본으로 제공한다.
ArgumentResolver 인터페이스
정확히는 HandlerMethodArgumentResolver지만 줄여서 ArgumentResolver라고 한다.
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) throws Exception;
}
동작 방식
- ArgumentResolver의 supportsParameter() 메서드를 호출해 해당 파라미터를 지원하는지 체크
- (지원할 경우) resolveArgument() 메서드로 호출해 실제 객체 생성
- (지원하지 않을 경우) 다음 ArgumentResolver로 우선순위가 넘어감
ReturnValueHandler
정확히는 HandlerMethodReturnValueHandler지만 줄여서 ReturnValueHandler라 한다.
- 응답 값을 변환해 처리한다.
- 스프링은 10개가 넘는 ReturnValueHanlder를 지원한다. 예) ModelAndView, @ResponseBody, HttpEntity, String
- 컨트롤러에서 String으로 뷰 이름을 반환해도 동작하는 이유는 ReturnValueHandler 덕분이다.
HTTP 메시지 컨버터
ArgumentResolver와 ReturnValueHandler 동작 시 HTTP 메세지 컨버터를 사용한다.
요청의 경우 @RequestBody를 처리하는 ArgumentResolver가 있고 HttpEntity를 처리하는 ArgumentResolver가 있는데, 이 ArgumentResolver들이 HTTP 메세지 컨버터를 사용해 필요한 객체를 생성한다. 응답의 경우 @ResponseBody와 HttpEntity를 처리하는 ReturnValueHandler가 있는데 여기서 HTTP 메세지 컨버터를 호출해 응답 결과를 얻는다.
확장
스프링은 이러한 ArgumentResovler, ReturnValueHandler, MessageConverter를 모두 인터페이스로 제공하기에 언제든지 확장해 사용할 수 있다.
- HandlerMethodArgumentResolver
- HandlerMethodReturnValueHandler
- HttpMessageConverte
'Spring > Spring MVC' 카테고리의 다른 글
스프링 검증 (2) (0) | 2023.04.02 |
---|---|
스프링 검증 (1) (0) | 2023.03.27 |
스프링 MVC (2) (0) | 2023.02.21 |
스프링 MVC (1) (0) | 2023.02.17 |
Spring Boot에서 Servlet 사용하기 (0) | 2023.02.16 |