Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - ์๋ต ๋งคํ @ResponseBody, MessageConverter, ์์ฒญ ๋งคํ ํธ๋ค๋ฌ ์ด๋ํฐ ๊ตฌ์กฐ
by rlaehddnd0422HTTP Response - ์ ์ ๋ฆฌ์์ค, ๋ทฐ ํ ํ๋ฆฟ
์คํ๋ง์์ ์๋ต ๋ฐ์ดํฐ๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ 3๊ฐ์ง์ ๋๋ค.
1. ์ ์ ๋ฆฌ์์ค
์น ๋ธ๋ผ์ฐ์ ์ ์ ์ ์ธ HTML, css, js๋ฅผ ์ ๊ณตํ ๋๋ ์ ์ ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์คํ๋ง ๋ถํธ๋ classpath์ /static, /public, /resources, /META-INF/resources ์ ์๋ ์ ์ ๋ฆฌ์์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ ์ ๋ฆฌ์์ค ๊ฒฝ๋ก : src/main/resources/static
src/main/resources๋ ๋ฆฌ์์ค๋ฅผ ๋ณด๊ดํ๋ ๊ณณ์ด๊ณ , ๋ classpath์ ์์ ๊ฒฝ๋ก์ ๋๋ค.
๋ฐ๋ผ์ src/main/resources/static์ ๋ฆฌ์์ค๋ฅผ ๋ฃ์ด๋๋ฉด ์คํ๋ง ๋ถํธ๊ฐ ์ ์ ๋ฆฌ์์ค๋ก ์๋น์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ ์ ๋ฆฌ์์ค โถ๏ธ ํด๋น ํ์ผ์ ๋ณ๊ฒฝ ์์ด ๊ทธ๋๋ก ์๋น์ค
2. ๋ทฐ ํ ํ๋ฆฟ ์ฌ์ฉ
์น ๋ธ๋ผ์ฐ์ ์ ๋์ ์ธ HTML์ ์ ๊ณตํ ๋๋ ๋ทฐ ํ ํ๋ฆฟ์ ์ฌ์ฉํฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก HTML์ ๋์ ์ผ๋ก ์์ฑํ๋ ์ฉ๋๋ก ์ฌ์ฉํ์ง๋ง, ๋ค๋ฅธ ๊ฒ๋ค๋ ๊ฐ๋ฅํฉ๋๋ค.
๋ทฐ ํ ํ๋ฆฟ ๊ฒฝ๋ก : 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>
์ด์ ๋ทฐ ํ ํ๋ฆฟ์ ํธ์ถํ๋ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
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.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class ResponseViewController {
@RequestMapping("/response-view-v1")
public ModelAndView responseViewV1()
{
ModelAndView mav = new ModelAndView("response/hello")
.addObject("data","hello!");
return mav;
}
// ๋ทฐ ๋ฆฌ์กธ๋ฒ ์ฌ์ฉ - @ResponseBody X
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model)
{
model.addAttribute("data","hello!!");
return "response/hello";
}
// ์ปจํธ๋กค๋ฌ์ ์ด๋ฆ๊ณผ ๋ทฐ ๊ฒฝ๋ก๊ฐ ๊ฐ์ผ๋ฉด
// ๊ถ์ฅํ์ง ์์ - ๋ช
์์ฑ์ด ๋๋ฌด ๋จ์ด์ง.
@RequestMapping("/response/hello")
public void responseViewV3(Model model)
{
model.addAttribute("data","hello!!");
}
}
1. ModelAndView๋ฅผ ๋ฆฌํดํ๋ ๊ฒฝ์ฐ - responseViewV1
@RequestMapping("/response-view-v1")
public ModelAndView responseViewV1()
{
ModelAndView mav = new ModelAndView("response/hello")
.addObject("data","hello!");
return mav;
}
ModelAndView์ Model์ {data}์ "hello"๋ฅผ ๋ฃ๊ณ ModelAndView ๋ฆฌํด
2. String์ ๋ฆฌํดํ๋ ๊ฒฝ์ฐ - responseViewV2
// ๋ทฐ ๋ฆฌ์กธ๋ฒ ์ฌ์ฉ - @ResponseBody X
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model)
{
model.addAttribute("data","hello!!");
return "response/hello";
}
@ResponseBody๊ฐ ์์ผ๋ฉด response/hello๋ก ๋ทฐ ๋ฆฌ์กธ๋ฒ๊ฐ ์คํ๋์ด์ ๋ทฐ๋ฅผ ์ฐพ๊ณ ๋ ๋๋ง ํฉ๋๋ค.
@ResponseBody๊ฐ ์์ผ๋ฉด ๋ทฐ ๋ฆฌ์กธ๋ฒ๋ฅผ ์คํํ์ง ์๊ณ HTTP ๋ฉ์์ง ๋ฐ๋์ ์ง์ response/hello๊ฐ ์ ๋ ฅ๋ฉ๋๋ค.
์ฌ๊ธฐ์์๋ Model์ {data}์ "hello"๋ฅผ ๋ฃ๊ณ ๋ทฐ์ ๋ ผ๋ฆฌ ์ด๋ฆ์ธ response/hello๋ฅผ ๋ฆฌํด
3. void๋ฅผ ๋ฆฌํดํ๋ ๊ฒฝ์ฐ - ์ปจํธ๋กค๋ฌ์ ์ด๋ฆ๊ณผ ๋ทฐ ๊ฒฝ๋ก๊ฐ ๊ฐ์ ๊ฒฝ์ฐ
// ์ปจํธ๋กค๋ฌ์ ์ด๋ฆ๊ณผ ๋ทฐ ๊ฒฝ๋ก๊ฐ ๊ฐ์ผ๋ฉด
// ๊ถ์ฅํ์ง ์์ - ๋ช
์์ฑ์ด ๋๋ฌด ๋จ์ด์ง.
@RequestMapping("/response/hello")
public void responseViewV3(Model model)
{
model.addAttribute("data","hello!!");
}
@Controller๋ฅผ ์ฌ์ฉํ๊ณ , HttpServletResponse, OutputStream(Writer) ๊ฐ์ HTTP ๋ฉ์์ง ๋ฐ๋๋ฅผ ์ฒ๋ฆฌํ๋ ํ๋ผ๋ฏธํฐ๊ฐ ์์ผ๋ฉด ์์ฒญ URL์ ์ฐธ๊ณ ํด์ ๋ ผ๋ฆฌ ๋ทฐ ์ด๋ฆ์ผ๋ก ์ฌ์ฉํฉ๋๋ค.
์ฐธ๊ณ ๋ก ์ด ๋ฐฉ๋ฒ์ ๋ช ์์ฑ์ด ๋๋ฌด ๋จ์ด์ง๊ธฐ ๋๋ฌธ์ ๊ถ์ฅํ์ง ์๋ ๋ฐฉ๋ฒ์ ๋๋ค.
3. HTTP ๋ฉ์์ง ์ฌ์ฉ
HTTP API๋ฅผ ์ ๊ณตํ๋ ๊ฒฝ์ฐ์๋ HTML์ด ์๋๋ผ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํด์ผ ํ๋ฏ๋ก, HTTP ๋ฉ์์ง ๋ฐ๋์ JSON ๊ฐ์ ํ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ค์ด ๋ณด๋ ๋๋ค.
@RepsonseBody, HttpEntity๋ฅผ ์ฌ์ฉํ๋ฉด ๋ทฐ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋๋ผ, HTTP ๋ฉ์์ง ๋ฐ๋์ ์ง์ ์๋ต ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅํ ์ ์์ต๋๋ค.
@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() throws IOException
{
return new ResponseEntity<>("ok", HttpStatus.OK);
}
@GetMapping("/response-body-string-v3")
@ResponseBody
public String responseBodyV3() throws IOException
{
return "ok!";
}
@ResponseBody
@GetMapping("response-body-json-v1")
public ResponseEntity<HelloData> responseBodyJsonV1()
{
HelloData data = new HelloData();
data.setUsername("Kdo6301");
data.setAge(25);
return new ResponseEntity<>(data,HttpStatus.OK);
}
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json-v2")
public HelloData responseBodyJsonV2()
{
HelloData data = new HelloData();
data.setUsername("Park");
data.setAge(34);
return data;
}
}
1. responseBodyV1
@GetMapping("/response-body-string-v1")
public void responseBodyV1(HttpServletResponse response) throws IOException
{
response.getWriter().write("ok");
}
์๋ธ๋ฆฟ์ ์ง์ ๋ค๋ฃฐ ๋ ์ฒ๋ผ HttpServletResponse ๊ฐ์ฒด๋ฅผ ํตํด์ HTTP ๋ฉ์์ง ๋ฐ๋์ ์ง์ ok ์๋ต๋ฉ์์ง๋ฅผ ์ ๋ฌ.
2. responseBodyV2
@GetMapping("/response-body-string-v2")
public ResponseEntity<String> responseBodyV2() throws IOException
{
return new ResponseEntity<>("ok", HttpStatus.OK);
}
ResponseEntity : HttpEntity๋ฅผ ์์๋ฐ์ ๊ฐ์ฒด๋ก, HttpEntity๋ HTTP ๋ฉ์์ง์ ํค๋, ๋ฐ๋ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
ResponseEntity๋ ์ฌ๊ธฐ์ ์ถ๊ฐ๋ก HTTP ์๋ต ์ฝ๋๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
3. responseBodyV3
@GetMapping("/response-body-string-v3")
@ResponseBody
public String responseBodyV3() throws IOException
{
return "ok!";
}
@ResponseBody๋ฅผ ์ฌ์ฉํ์ฌ view๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ํตํด HTTP ๋ฉ์์ง๋ฅผ ์ง์ ์ ๋ ฅํ ์ ์์ต๋๋ค. ResponseEntity๋ ๋์ผํ๊ฒ ๋์
4. responseBodyJsonV1 - return ResponseEntity
@ResponseBody
@GetMapping("response-body-json-v1")
public ResponseEntity<HelloData> responseBodyJsonV1()
{
HelloData data = new HelloData();
data.setUsername("Kdo6301");
data.setAge(25);
return new ResponseEntity<>(data,HttpStatus.OK);
}
ResponseEntity์ HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ํตํด์ JSONํ์์ผ๋ก ๋ณํ๋์ด ๋ฐํํฉ๋๋ค.
5. responseBodyJsonV2 - return ๊ฐ์ฒด
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json-v2")
public HelloData responseBodyJsonV2()
{
HelloData data = new HelloData();
data.setUsername("Park");
data.setAge(34);
return data;
}
ResponseEntity๋ ์๋ต์ฝ๋๋ฅผ ์ค์ ํ ์ ์๋๋ฐ, @ResponseBody๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฐ ๊ฒ์ ์ค์ ํ๊ธฐ ๊น๋ค๋กญ์ต๋๋ค.
@ResponseStatus(HttpStatus.OK) ์ ๊ฐ์ด @ResponseStatus ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด ์๋ต์ฝ๋๋ ์ค์ ํ ์ ์์ต๋๋ค.
+ @RestController
: @Controller๋์ ์ ์ฌ์ฉํ๋ฉด ํด๋น ์ปจํธ๋กค๋ฌ ๋ชจ๋ @ResponseBody๊ฐ ์ ์ฉ๋๋ ํจ๊ณผ๊ฐ ์์ต๋๋ค. ๋ฐ๋ผ์ ๋ทฐ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋๋ผ , HTTP ๋ฉ์์ง ๋ฐ๋์ ์ง์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฅํฉ๋๋ค. ์ด๋ฆ ๊ทธ๋๋ก REST API๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉํ๋ ์ปจํธ๋กค๋ฌ์ ๋๋ค.
+ ์ฐธ๊ณ ๋ก @ResponseBody๋ ํด๋์ค ๋ ๋ฒจ์ ๋๋ฉด ์ ์ฒด ๋ฉ์๋์ ์ ์ฉ๋๋๋ฐ,
@ResponseController ์ด๋ ธํ ์ด์ ์์ @ResponseBody๊ฐ ์ ์ฉ๋์ด ์์ต๋๋ค.
HTTP Message Converter
์คํ๋ง MVC๋ ๋ค์์ ๊ฒฝ์ฐ์ HttpMessageConverter๊ฐ ๋์ํฉ๋๋ค.
HTTP ์์ฒญ : @RequestBody, HttpEntity(RequestEntity)
HTTP ์๋ต : @ResponseBody, HttpEntity(ResponseEntity)
public interface HttpMessageConverter<T> {
/**
* Indicates whether the given class can be read by this converter.
* @param clazz the class to test for readability
* @param mediaType the media type to read (can be {@code null} if not specified);
* typically the value of a {@code Content-Type} header.
* @return {@code true} if readable; {@code false} otherwise
*/
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
/**
* Indicates whether the given class can be written by this converter.
* @param clazz the class to test for writability
* @param mediaType the media type to write (can be {@code null} if not specified);
* typically the value of an {@code Accept} header.
* @return {@code true} if writable; {@code false} otherwise
*/
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
/**
* Return the list of media types supported by this converter. The list may
* not apply to every possible target element type and calls to this method
* should typically be guarded via {@link #canWrite(Class, MediaType)
* canWrite(clazz, null}. The list may also exclude MIME types supported
* only for a specific class. Alternatively, use
* {@link #getSupportedMediaTypes(Class)} for a more precise list.
* @return the list of supported media types
*/
List<MediaType> getSupportedMediaTypes();
/**
* Return the list of media types supported by this converter for the given
* class. The list may differ from {@link #getSupportedMediaTypes()} if the
* converter does not support the given Class or if it supports it only for
* a subset of media types.
* @param clazz the type of class to check
* @return the list of media types supported for the given class
* @since 5.3.4
*/
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
/**
* Write a given object to the given output message.
* @param t the object to write to the output message. The type of this object must have previously been
* passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
* @param contentType the content type to use when writing. May be {@code null} to indicate that the
* default content type of the converter must be used. If not {@code null}, this media type must have
* previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
* returned {@code true}.
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
canRead(), canWrite() : ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ํด๋น ํด๋์ค, ๋ฏธ๋์ด ํ์ ์ ์ง์ํ๋ ์ง ์ฒดํฌํ๊ณ ํด๋น ํด๋์ค, ๋ฏธ๋์ดํ์ ์ ์ง์ํ๋ ๊ฒฝ์ฐ์๋ read(), write()๋ฅผ ํตํด ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ํตํด ๋ฉ์์ง๋ฅผ ์ฝ๊ณ ์๋๋ค.
์คํ๋ง ๋ถํธ ๊ธฐ๋ณธ ๋ฉ์์ง ์ปจ๋ฒํฐ์๋
0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter
๊ฐ ์์ต๋๋ค.
๋์ ํด๋์ค ํ์ ๊ณผ ๋ฏธ๋์ด ํ์ ๋ ๊ฐ๋ฅผ ์ฒดํฌํด์ ์ฌ์ฉ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํฉ๋๋ค. ๋ง์ฝ ๋ง์กฑํ์ง ์์ผ๋ฉด ๋ค์ ๋ฉ์์ง ์ปจ๋ฒํฐ๋ก ์ฐ์ ์์ ๋๊น.
0 = ByteArrayHttpMessageConverter
- ํด๋์ค ํ์ : byte[]
- ๋ฏธ๋์ด ํ์ : */* (๋ชจ๋)
- ์์ฒญ ์์ : @RequestBody byte[] data , ์๋ต ์์ : @ResponseBody return byte[]
- ์ฐ๊ธฐ ๋ฏธ๋์ด ํ์ : application/octet-stream
1 = StringHttpMessageConverter
- ํด๋์ค ํ์ : String
- ๋ฏธ๋์ด ํ์ : */* (๋ชจ๋)
- ์์ฒญ ์์ : @RequestBody String data, ์๋ต ์์ : @ResponseBody return "OK"
- ์ฐ๊ธฐ ๋ฏธ๋์ด ํ์ : text/plain
2 = MappingJackson2HttpMessageConverter
- ํด๋์ค ํ์ : ๊ฐ์ฒด, HashMap
- ๋ฏธ๋์ด ํ์ : application/json ๊ด๋ จ
- ์์ฒญ ์์ : @RequestBody HelloData data, ์๋ต ์์ : @ResponseBody return data;
- ์ฐ๊ธฐ ๋ฏธ๋์ด ํ์ : application/json ๊ด๋ จ
๋์ ์๋ฆฌ
HTTP ์์ฒญ์ด ๋ค์ด์ต๋๋ค. (์ปจํธ๋กค๋ฌ์์ @RequestBody, HttpEntity ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉ)
โถ๏ธ ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ๋ฉ์์ง๋ฅผ ์ฝ์ ์ ์๋์ง ํ์ธํ๊ธฐ ์ํด canRead() ํธ์ถ
โถ๏ธโถ๏ธ 0๋ฒ ๋ฉ์์ง ์ปจ๋ฒํฐ๋ถํฐ ์์ฒญ ๋ฉ์์ง ํด๋์ค ํ์ , ์์ฒญ์ ๋ฏธ๋์ด ํ์ ์ ์ง์ํ๋์ง ํ์ธํ๊ณ canRead()์กฐ๊ฑด ๋ง์กฑ ์ read() ํธ์ถ
(๋ง์ฝ ์ฝ์ง ๋ชปํ๋ฉด ๋ค์ ๋ฉ์์ง ์ปจ๋ฒํฐ๋ก ์ฐ์ ์์ pass)
โถ๏ธโถ๏ธโถ๏ธ ๊ฐ์ฒด ์์ฑ ๋ฐ ๋ฐํ
์ปจํธ๋กค๋ฌ์์ @ResponseBody, HttpEntity๋ก ๊ฐ์ด ๋ฐํ ๋๋ฉด,
โถ๏ธ ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ๋ฉ์์ง๋ฅผ ์ธ ์ ์๋์ง ํ์ธํ๊ธฐ ์ํด canWrite() ํธ์ถ
โถ๏ธโถ๏ธ 0๋ฒ ๋ฉ์์ง ์ปจ๋ฒํฐ๋ถํฐ ์์ฒญ ๋ฉ์์ง ํด๋์ค ํ์ , ์์ฒญ์ ๋ฏธ๋์ด ํ์ ์ ์ง์ํ๋์ง ํ์ธํ๊ณ canWrite()์กฐ๊ฑด ๋ง์กฑ ์ write() ํธ์ถ
(๋ง์ฝ ์ฝ์ง ๋ชปํ๋ฉด ๋ค์ ๋ฉ์์ง ์ปจ๋ฒํฐ๋ก ์ฐ์ ์์ pass)
โถ๏ธโถ๏ธโถ๏ธ HTTP ์๋ต ๋ฉ์์ง ๋ฐ๋์ ๋ฐ์ดํฐ ์์ฑ
์์ฒญ ๋งคํ ํธ๋ค๋ฌ ์ด๋ํฐ ๊ตฌ์กฐ
HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ๋ MVC ์ด๋์ฏค์์ ์ฌ์ฉ๋๋ ๊ฑธ๊น์?
๋ฐ๋ก @RequestMapping์ ์ฒ๋ฆฌํ๋ ํธ๋ค๋ฌ ์ด๋ํฐ์ธ RequestMappingHandlerAdapter(์์ฒญ ๋งคํ ํธ๋ค๋ฌ ์ด๋ํฐ)์์ HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ๋์ํฉ๋๋ค.
RequestMappingHandlerAdapter ๋์ ๋ฐฉ์
Request์ ๊ฒฝ์ฐ
@RequestBody๋ฅผ ์ฒ๋ฆฌํ๋ ArgumentResolver๊ฐ ์๊ณ , HttpEntity๋ฅผ ์ฒ๋ฆฌํ๋ ArgumentResolver๊ฐ ์์ต๋๋ค.
์ด ArgumentResolver๋ค์ด HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ์ฌ์ฉํด์ ํ์ํ ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.
@ResponseBody ์์
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody)
{
log.info("message body = {}",messageBody);
return "OK";
}
HttpEntity ์์
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
String messagebody = httpEntity.getBody();
log.info("messageBody = {}", messagebody);
return new HttpEntity<String>("ok");
// return new ResponseEntity<>("ok", HttpStatus.CREATED);
}
1. RequestMapping ํธ๋ค๋ฌ ์ด๋ํฐ๊ฐ ArgumentResolver๋ฅผ ํธ์ถํด ์ปจํธ๋กค๋ฌ์ ํ๋ผ๋ฏธํฐ, ์ด๋ ธํ ์ด์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ๋ฌ ๋ฐ์ดํฐ๋ฅผ ์์ฑํฉ๋๋ค.
- ArgumentResolver
ArgumentResolver๋ก ๋ค์ด์จ ๋ฐ์ดํฐ(ํ๋ผ๋ฏธํฐ)๋ฅผ ๊ฐ๊ณตํ์ฌ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ฝ์ ์๋ฒ์ ์ ๋ฌํด์ฃผ๋ ์ญํ ์ ํฉ๋๋ค.
(๋จ, ์ํธํ ๋ ๋ฐ์ดํฐ๋ ๋ถ๊ฐ)
ArgumentResolver๋ HandlerMethodArgumentResolver์ ๊ตฌํ์ฒด๋ก ๋ ๊ฐ์ ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
boolean supportsParameter(MethodParameter parameter);
/**
* Resolves a method parameter into an argument value from a given request.
* A {@link ModelAndViewContainer} provides access to the model for the
* request. A {@link WebDataBinderFactory} provides a way to create
* a {@link WebDataBinder} instance when needed for data binding and
* type conversion purposes.
* @param parameter the method parameter to resolve. This parameter must
* have previously been passed to {@link #supportsParameter} which must
* have returned {@code true}.
* @param mavContainer the ModelAndViewContainer for the current request
* @param webRequest the current request
* @param binderFactory a factory for creating {@link WebDataBinder} instances
* @return the resolved argument value, or {@code null} if not resolvable
* @throws Exception in case of errors with the preparation of argument values
*/
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
- supportsParameter() : ์ฃผ์ด์ง ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ๊ฐ ์ด Argument Resolver์์ ์ง์ํ๋ ํ์ ์ธ์ง ๊ฒ์ฌ ( true or false )
- resolveArgument() : ์ด ๋ฉ์๋์ ๋ฐํ ๊ฐ์ด ๋์์ด ๋๋ ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ์ ๋ฐ์ธ๋ฉ ๋ฉ๋๋ค.
ArgumentResolver๋ WebMvcConfigurer๋ฅผ ์์๋ฐ์ ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์์ต๋๋ค. (ํ์ฅํ ์ผ์ด ๊ฑฐ์ ์๊ธด ํฉ๋๋ค๋ง.)
public interface WebMvcConfigurer {
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
2. ArgumentResolver๊ฐ HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ์ฌ์ฉํด์ ์ ๋ฌ๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.
Response
์ปจํธ๋กค๋ฌ๊ฐ ReturnValueHandler๋ฅผ ํตํด ์๋ต ๋ฐ์ดํฐ๋ฅผ HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ํธ์ถํด์ Response ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ญ๋๋ค.
์คํ๋ง MVC๋
@RequestBody, @ResponseBody๊ฐ ์์ผ๋ฉด RequestResponseBodyMethodProcessor(ArgumentResolver),
HttpEntity๊ฐ ์์ผ๋ฉด HttpEntityMethodProcessor(ArgumentResolver)๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ArgumentResolver์ ReturnValueHandler๊ฐ ์ฒ๋ฆฌ ๊ฐ๋ฅํ ๊ฐ ๋ชฉ๋ก์
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
์์ ์กฐํํ ์ ์์ต๋๋ค.
<์ฐธ๊ณ ์๋ฃ>
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
https://maenco.tistory.com/entry/Spring-MVC-Argument-Resolver%EC%99%80-ReturnValue-Handler
'๐ Backend > MVC Pattern' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๊ฒ์ฆ(Validation) ์ฒ๋ฆฌ ๋ฐฉ๋ฒ (0) | 2023.03.20 |
---|---|
๋ฉ์์ง, ๊ตญ์ ํ ๊ธฐ๋ฅ (0) | 2023.03.20 |
Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - ์์ฒญ ๋งคํ @RequestMapping, @RequestParam (0) | 2023.03.12 |
Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - Logging (0) | 2023.03.10 |
Spring MVC - @RequestMapping, @RequestParam, ModelAndView (0) | 2023.03.09 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422