# Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋Šฅ - ์‘๋‹ต ๋งคํ•‘ @ResponseBody, MessageConverter, ์š”์ฒญ ๋งคํ•‘ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ๊ตฌ์กฐ
Study Repository

Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋Šฅ - ์‘๋‹ต ๋งคํ•‘ @ResponseBody, MessageConverter, ์š”์ฒญ ๋งคํ•‘ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ๊ตฌ์กฐ

by rlaehddnd0422

HTTP 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 ์–ด๋””์ฏค์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฑธ๊นŒ์š”?

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

 

Web on Servlet Stack

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, "Spring Web MVC," comes from the name of its source module (spring-webmvc), but it is more commonl

docs.spring.io

์—์„œ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


<์ฐธ๊ณ  ์ž๋ฃŒ>

https://blog.neonkid.xyz/238

 

[Spring] Argument Resolver๋ฅผ ์ด์šฉํ•œ ์œ ์—ฐ์„ฑ ์žˆ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ์ฒ˜๋ฆฌ

์„œ๋น„์Šค๋ฅผ ์šด์˜ํ•˜๋‹ค๋ณด๋ฉด ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿด ๋•Œ๋งˆ๋‹ค Controller ๋ถ€๋ถ„์—์„œ ์ด๋ฅผ ์ „์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๊ฐ Controller์— ์ „์ฒ˜๋ฆฌ ํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•จ์ˆ˜ํ™” ํ•˜๊ฑฐ๋‚˜ Utils ํด

blog.neonkid.xyz

 

https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types

 

Web on Servlet Stack

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, "Spring Web MVC," comes from the name of its source module (spring-webmvc), but it is more commonl

docs.spring.io

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard

 

์Šคํ”„๋ง MVC 1ํŽธ - ๋ฐฑ์—”๋“œ ์›น ๊ฐœ๋ฐœ ํ•ต์‹ฌ ๊ธฐ์ˆ  - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ ํ•„์š”ํ•œ ๋ชจ๋“  ์›น ๊ธฐ์ˆ ์„ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์ดํ•ดํ•˜๊ณ , ์™„์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง MVC์˜ ํ•ต์‹ฌ ์›๋ฆฌ์™€ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ณ , ๋” ๊นŠ์ด์žˆ๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค., -

www.inflearn.com

https://maenco.tistory.com/entry/Spring-MVC-Argument-Resolver%EC%99%80-ReturnValue-Handler

 

[Spring MVC] Argument Resolver์™€ ReturnValue Handler

(์ฐธ๊ณ : Spring Boot๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋œ ๊ธ€์ž…๋‹ˆ๋‹ค) Controller (Controller์™€ RestController)์™€ RequestMapping์˜ ๊ธ€์„ ์ •๋ฆฌํ•˜๋ฉด์„œ ํด๋ž˜์Šค์™€ ๋ฉ”์„œ๋“œ๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ๋งคํ•‘์ด ๋˜๋Š”์ง€ ์‚ดํŽด๋ณด์•˜๋‹ค ์ด๋ฒˆ์—๋Š” R

maenco.tistory.com

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ