@Controller
는 반환 값이 String이면 뷰 이름으로 인식된다. 그래서 뷰를 찾고 뷰가 랜더링 된다.@RestController
는 반환 값으로 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다.
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data)
@PathVariable("userId") String userId
) -> @PathVariable userId
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable String userId, @PathVariable Long orderId)
@PostMapping(value = "/mapping-consume", consumes = "application/json")
@PostMapping(value = "/mapping-produce", produces = "text/html")
Content-Type 헤더와 Accept 헤더 둘 다 데이터 타입(MIME)을 다루는 헤더이다. 하지만 Content-Type 헤더는 현재 전송하는 데이터가 어떤 타입인지에 대한 설명을 하는 개념이고, Accept 헤더는 클라이언트가 서버에게 어떤 특정한 데이터 타입을 보낼 때 클라이언트가 보낸 특정 데이터 타입으로만 응답을 해야한다.
애노테이션 기반의 스프링 컨트롤러는 다양한 파라미터를 지원한다.
HttpServletRequest
HttpServletResponse
HttpMethod
: HTTP 메서드를 조회한다. org.springframework.http.HttpMethodLocale
: Locale 정보를 조회한다.@RequestHeader MultiValueMap<String, String> headerMap
@RequestHeader("host") String host
required
defaultValue
@CookieValue(value = "myCookie", required = false) String cookie
클라이언트에서 서버로 요청 데이터를 전달할 때는 주로 다음 3가지 방법을 사용한다.
/url?username=hello&age=20
content-type: application/x-www-form-urlencoded
username=hello&age=20
GET 쿼리 파리미터 전송 방식이든, POST HTML Form 전송 방식이든 둘 다 형식이 같으므로 구분 없이 조회할 수 있다.
이것을 간단히 요청 파라미터(request parameter) 조회라 한다.
스프링이 제공하는 @RequestParam
을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다
@RequestMapping("/request-param-v2")
public String requestParamV2(@RequestParam("username") String memberName,
@RequestParam("age") int age)
HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx")
생략 가능
public String requestParamV4(String username, int age)
@RequestParam
이 있으면 명확하게 요청 파리미터에서 데이터를 읽는다는 것을 알 수 있다.@RequestMapping("/request-param-required")
public String requestParamRequired(@RequestParam(required = false) String username,
@RequestParam(required = false) int age)
@RequestParam.required
/request-param?username=
/request-param
요청@RequestParam(required = false) int age
Integer
로 변경하거나, 또는 다음에 나오는 defaultValue
사용@RequestMapping("/request-param-default")
public String requestParamDefault(@RequestParam(defaultValue = "guest") String username,
@RequestParam(defaultValue = "-1") int age)
defaultValue
를 사용하면 기본값을 적용할 수 있다.required
는 의미가 없다.defaultValue
는 빈 문자의 경우에도 설정한 기본값이 적용된다.@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
실제 개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어주어야 한다.
스프링은 이 과정을 완전히 자동화해주는 @ModelAttribute
기능을 제공한다.
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
스프링MVC는 @ModelAttribute
가 있으면 다음을 실행한다.
HelloData
객체를 생성한다.HelloData
객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter
를 호출해서 파라미터의 값을 입력(바인딩) 한다.username
이면 setUsername()
메서드를 찾아서 호출하면서 값을 입력한다.public String modelAttributeV2(HelloData helloData) {
스프링은 해당 생략시 다음과 같은 규칙을 적용한다.
String
, int
, Integer
같은 단순 타입 = @RequestParam
@ModelAttribute
(argument resolver로 지정해둔 타입 외)@PostMapping("/add")
public String addItemV2(@ModelAttribute("item") Item item) {
@ModelAttribute
는 중요한 한 가지 기능이 더 있는데, 바로 모델(Model)에 @ModelAttribute
로 지정한 객체를 자동으로 넣어준다.@ModelAttribute
에 지정한 name(value)
속성을 사용한다.@ModelAttribute
의 이름을 생략할 수 있다.요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 데이터가 넘어오는 경우는 @RequestParam
, @ModelAttribute
를 사용할 수 없다. (물론 HTML Form 형식으로 전달되는 경우는 요청 파라미터로 인정된다.)
@ResponseBody @PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) throws IOException {
}
@RequestBody
@RequestBody
를 사용하면 HTTP 메시지 바디 정보를 편리하게 조회할 수 있다.HttpEntity
를 사용하거나 @RequestHeader
를 사용하면 된다.@RequestParam
, @ModelAttribute
와는 전혀 관계가 없다.@ResponseBody
@ResponseBody
를 사용하면 응답 결과를 HTTP 메시지 바디에 직접 담아서 전달할 수 있다. 물론 이 경우에도 view를 사용하지 않는다.
@ResponseBody @PostMapping("/request-body-json-v2")
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {
HelloData data = objectMapper.readValue(messageBody, HelloData.class);
return "ok";
}
objectMapper
를 사용해서 자바 객체로 변환한다.
@ResponseBody @PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData helloData) throws IOException {
return "ok";
}
@RequestBody
객체 파라미터
@RequestBody HelloData data
@RequestBody
에 직접 만든 객체를 지정할 수 있다@RequestBody는 생략 불가능
@ModelAttribute
에서 학습한 내용을 떠올려보자.
스프링은 @ModelAttribute
, @RequestParam
해당 생략 시 다음과 같은 규칙을 적용한다.
따라서 이 경우 HelloData
에 @RequestBody
를 생략하면 @ModelAttribute
가 적용되어버린다.
HelloData data
-> @ModelAttribute HelloData data
따라서 생략하면 HTTP 메시지 바디가 아니라 요청 파라미터를 처리하게 된다.
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
return data;
}
@ResponseBody
응답의 경우에도 @ResponseBody
를 사용하면 해당 객체를 HTTP 메시지 바디에 직접 넣어줄 수 있다
@RequestBody
요청
@ResponseBody
응답
스프링(서버)에서 응답 데이터를 만드는 방법은 크게 3가지이다.
다음 경로에 파일이 들어있으면
src/main/resources/static/basic/hello-form.html
웹 브라우저에서 다음과 같이 실행하면 된다.
http://localhost:8080/basic/hello-form.html
정적 리소스는 해당 파일을 변경 없이 그대로 서비스하는 것이다.
뷰 템플릿을 거쳐서 HTML이 생성되고, 뷰가 응답을 만들어서 전달한다.
일반적으로 HTML을 동적으로 생성하는 용도로 사용하지만, 다른 것들도 가능하다. 뷰 템플릿이 만들 수 있는 것이라면 뭐든지 가능하다.
String을 반환하는 경우 - View or HTTP 메시지
@RequestMapping("/response-view-v2")
public String responseViewV1(Model model) {
model.addAttribute("data", "hello!");
return "/response/hello";
}
@ResponseBody
가 없으면 response/hello로 뷰 리졸버가 실행되어서 뷰를 찾고, 렌더링 한다.@ResponseBody
가 있으면 뷰 리졸버를 실행하지 않고, HTTP 메시지 바디에 직접 response/hello라는 문자가 입력된다.HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 하므로, HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 실어 보낸다
뷰 템플릿으로 HTML을 생성해서 응답하는 것이 아니라, HTTP API처럼 JSON 데이터를 HTTP 메시지 바디에서 직접 읽거나 쓰는 경우 HTTP 메시지 컨버터를 사용하면 편리하다
@ResponseBody
사용 원리
@ResponseBody
를 사용
viewResolver
대신에 HttpMessageConverter
가 동작StringHttpMessageConverter
MappingJackson2HttpMessageConverter
HttpMessageConverter
가 기본으로 등록 되어 있음스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용한다.
@RequestBody
, HttpEntity(RequestEntity)
@ResponseBody
, HttpEntity(ResponseEntity)
스프링 부트는 다양한 메시지 컨버터를 제공하는데, 대상 클래스 타입과 미디어 타입 둘을 체크해서 사용 여부를 결정한다. 만약 만족하지 않으면 다음 메시지 컨버터로 우선순위가 넘어간다.
몇 가지 주요한 메시지 컨버터
ByteArrayHttpMessageConverter
: byte[] 데이터를 처리한다
byte[]
, 미디어타입: / ,@RequestBody byte[] data
@ResponseBody return byte[]
쓰기 미디어타입 application/octet-streamStringHttpMessageConverter
: String
문자로 데이터를 처리한다.
String
, 미디어타입: */*
@RequestBody String data
@ResponseBody return "ok"
쓰기 미디어타입 text/plainMappingJackson2HttpMessageConverter
: application/json
HashMap
, 미디어타입 application/json@RequestBody HelloData data
@ResponseBody return helloData
쓰기 미디어타입 application/json 관련HTTP 요청 데이터 읽기
@RequestBody
, HttpEntity
파라미터를 사용한다.canRead()
를 호출한다.
대상 클래스 타입을 지원하는가.
` • 예) @RequestBody
의 대상 클래스 (byte[]
, String
, HelloData
)
HTTP 요청의 Content-Type
미디어 타입을 지원하는가.
canRead()
조건을 만족하면 read()
를 호출해서 객체 생성하고, 반환한다HTTP 응답 데이터 생성
@ResponseBody
, HttpEntity
로 값이 반환된다.canWrite()
를 호출한다.
byte[]
, String
, HelloData
)@RequestMapping
의 produces)
canWrite()
조건을 만족하면 write()
를 호출해서 HTTP 응답 메시지 바디에 데이터를 생성한다HTTP 메시지 컨버터는 스프링 MVC 어디쯤에서 사용되는 것일까?
모든 비밀은 애노테이션 기반의 컨트롤러, @RequestMapping
을 처리하는 핸들러 어댑터인 RequestMappingHandlerAdapter
(요청 매핑 헨들러 어뎁터)에 있다.
ArgumentResolver (HandlerMethodArgumentResolver)
애노테이션 기반의 컨트롤러는 HttpServletRequest
, Model
은 물론이고, @RequestParam
, @ModelAttribute
같은 애노테이션 그리고 @RequestBody
, HttpEntity
같은 HTTP 메시지를 처리하는 부분까지 매우 큰 유연함을 보여주었다.
애노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdaptor
는 바로 이 ArgumentResolver
를 호출해서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성한다.
그리고 이렇게 파리미터의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다.
ReturnValueHandler (HandlerMethodReturnValueHandler)
컨트롤러에서 String
으로 뷰 이름을 반환해도, 동작하는 이유가 바로 ReturnValueHandler
덕분이다.
HTTP 메시지 컨버터를 사용하는 @RequestBody
도 컨트롤러가 필요로 하는 파라미터의 값에 사용된다.
@ResponseBody
의 경우도 컨트롤러의 반환 값을 이용한다
@RequestBody
를 처리하는 ArgumentResolver
있고, HttpEntity
를 처리하는 ArgumentResolver
가 있다. 이 ArgumentResolver
들이 HTTP 메시지 컨버터를 사용해서 필요한 객체를 생성한다.@ResponseBody
와 HttpEntity
를 처리하는 ReturnValueHandler
가 있다. 그리고 여기에서 HTTP 메시지 컨버터를 호출해서 응답 결과를 만든다.