[Spring] 6.์Šคํ”„๋ง MVC - ๊ธฐ๋ณธ ๊ธฐ๋Šฅ

 

1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

ํŒŒ์ผ์ฐธ๊ณ 

์ฃผ์˜)

> Packaging๋Š” War๊ฐ€ ์•„๋‹ˆ๋ผ Jar๋ฅผ ์„ ํƒ
JSP๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— Jar๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด
์ข‹์Œ
์•ž์œผ๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด ๋ฐฉ์‹์„ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ๋จ

>
Jar๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ญ์ƒ ๋‚ด์žฅ ์„œ๋ฒ„(ํ†ฐ์บฃ๋“ฑ)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , webapp ๊ฒฝ๋กœ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
๋‚ด์žฅ ์„œ๋ฒ„ ์‚ฌ์šฉ์—
์ตœ์ ํ™” ๋˜์–ด ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. ์ตœ๊ทผ์—๋Š” ์ฃผ๋กœ ์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉ

> War๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‚ด์žฅ ์„œ๋ฒ„๋„ ์‚ฌ์šฉ๊ฐ€๋Šฅ ํ•˜์ง€๋งŒ, ์ฃผ๋กœ ์™ธ๋ถ€ ์„œ๋ฒ„์— ๋ฐฐํฌํ•˜๋Š” ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉ

build.gradle ์„ค์ • -> ํŒŒ์ผ์ฐธ๊ณ 

annotaion processors -> enable annotation processing์ฒดํฌ (๋กฌ๋ณต๋•Œ๋งค)

๋™์ž‘ํ™•์ธ -> hello - springmvc - SpringmvcApplication ์‹คํ–‰

 

WelcomPage ๋งŒ๋“ค๊ธฐ

์Šคํ”„๋ง๋ถ€ํŠธ์— 'Jar' ์‚ฌ์šฉํ•˜๋ฉด /resources/static/ ์œ„์น˜์— index.html ํŒŒ์ผ ๋‘๋ฉด ์›ฐ์ปดํŽ˜์ด์ง€๋กœ ์ฒ˜๋ฆฌํ•ด์คŒ

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body> <ul>
<li>๋กœ๊ทธ ์ถœ๋ ฅ <ul>
<li><a href="/log-test">๋กœ๊ทธ ํ…Œ์ŠคํŠธ</a></li> </ul>
</li>
<!-- --> <li>์š”์ฒญ ๋งคํ•‘
          <ul>
              <li><a href="/hello-basic">hello-basic</a></li>
              <li><a href="/mapping-get-v1">HTTP ๋ฉ”์„œ๋“œ ๋งคํ•‘</a></li>
<li><a href="/mapping-get-v2">HTTP ๋ฉ”์„œ๋“œ ๋งคํ•‘ ์ถ•์•ฝ</a></li>
<li><a href="/mapping/userA">๊ฒฝ๋กœ ๋ณ€์ˆ˜</a></li>
<li><a href="/mapping/users/userA/orders/100">๊ฒฝ๋กœ ๋ณ€์ˆ˜ ๋‹ค์ค‘</a></li> <li><a href="/mapping-param?mode=debug">ํŠน์ • ํŒŒ๋ผ๋ฏธํ„ฐ ์กฐ๊ฑด ๋งคํ•‘</a></li> <li><a href="/mapping-header">ํŠน์ • ํ—ค๋” ์กฐ๊ฑด ๋งคํ•‘(POST MAN ํ•„์š”)</a></
<li><a href="/mapping-consume">๋ฏธ๋””์–ด ํƒ€์ž… ์กฐ๊ฑด ๋งคํ•‘ Content-Type(POST MAN ํ•„์š”)</a></li>
<li><a href="/mapping-produce">๋ฏธ๋””์–ด ํƒ€์ž… ์กฐ๊ฑด ๋งคํ•‘ Accept(POST MAN ํ•„์š”)</a></li>
</ul> </li>
<li>์š”์ฒญ ๋งคํ•‘ - API ์˜ˆ์‹œ <ul>
<li>POST MAN ํ•„์š”</li> </ul>
</li>
<li>HTTP ์š”์ฒญ ๊ธฐ๋ณธ
<ul>
<li><a href="/headers">๊ธฐ๋ณธ, ํ—ค๋” ์กฐํšŒ</a></li>
</ul> </li>
<li>HTTP ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ <ul>
<li><a href="/request-param-v1?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐv1</a></li>
<li><a href="/request-param-v1?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐv2</a></li>
<li><a href="/request-param-v1?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐv3</a></li>
<li><a href="/request-param-v1?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐv4</a></li>
<li><a href="/request-param-required?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ ํ•„์ˆ˜</a></li>
<li><a href="/request-param-default?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธฐ๋ณธ ๊ฐ’</a></li>
<li><a href="/request-param-map?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ</a></li>
 <li><a href="/model-attribute-v1?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ @ModelAttribute v1</a></li>
<li><a href="/model-attribute-v2?username=hello&age=20">์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ @ModelAttribute v2</a></li>
</ul> </li>
<li>HTTP ์š”์ฒญ ๋ฉ”์‹œ์ง€ <ul>
                <li>POST MAN</li>
            </ul>
</li>
<li>HTTP ์‘๋‹ต - ์ •์  ๋ฆฌ์†Œ์Šค, ๋ทฐ ํ…œํ”Œ๋ฆฟ
<ul>
<li><a href="/basic/hello-form.html">์ •์  ๋ฆฌ์†Œ์Šค</a></li> <li><a href="/response-view-v1">๋ทฐ ํ…œํ”Œ๋ฆฟ v1</a></li> <li><a href="/response-view-v2">๋ทฐ ํ…œํ”Œ๋ฆฟ v2</a></li>
</ul> </li>
<li>HTTP ์‘๋‹ต - HTTP API, ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ง์ ‘ ์ž…๋ ฅ <ul>
                <li><a href="/response-body-string-v1">HTTP API String v1</a></li>
                <li><a href="/response-body-string-v2">HTTP API String v2</a></li>
                <li><a href="/response-body-string-v3">HTTP API String v3</a></li>
                <li><a href="/response-body-json-v1">HTTP API Json v1</a></li>
                <li><a href="/response-body-json-v2">HTTP API Json v2</a></li>
</ul> </li>
    </ul>
    </body>
    </html>

2. ๋กœ๊น… ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณด๊ธฐ

๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

์Šคํ”„๋ง ๋ถ€ํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง ๋ถ€ํŠธ ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ( spring-boot-starter-logging )๊ฐ€ ํ•จ๊ป˜ ํฌํ•จ๋œ๋‹ค.
์Šคํ”„๋ง ๋ถ€ํŠธ ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ธฐ๋ณธ์œผ๋กœ ๋‹ค์Œ ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

๋กœ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Logback, Log4J, Log4J2 ๋“ฑ ์ˆ˜๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋Š”๋ฐ ๊ทธ๊ฒƒ์„ ํ†ตํ•ฉํ•ด์„œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ SLF4J

์‹ค๋ฌด์—์„œ๋Š” SLF4J์˜ ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด Logback ๋งŽ์ด ์”€

 

๋กœ๊ทธ ์„ ์–ธ ๋ฐ ํ˜ธ์ถœ

๋กœ๊ทธ ์ปจํŠธ๋กค๋Ÿฌ

//@Slf4j
@RestController
public class LogTestController {
  
  private final Logger log = LoggerFactory.getLogger(getClass());
  
  @RequestMapping("/log-test")
  public String logTest() {
    String name = "Spring";
    log.trace("trace log={}", name);
    log.debug("debug log={}", name);
    log.info(" info log={}", name);
    log.warn(" warn log={}", name);
    log.error("error log={}", name);

    //๋กœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ a+b ๊ณ„์‚ฐ ๋กœ์ง์ด ๋จผ์ € ์‹คํ–‰๋จ, ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด X log.debug("String concat log=" + name);
    return "ok";
  } 
}

 

@RestController

@Controller ๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์ด String ์ด๋ฉด ๋ทฐ ์ด๋ฆ„์œผ๋กœ ์ธ์‹๋œ๋‹ค.
๊ทธ๋ž˜์„œ ๋ทฐ๋ฅผ ์ฐพ๊ณ  ๋ทฐ๊ฐ€ ๋žœ๋”๋ง ๋œ๋‹ค. @RestController ๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ ๋ทฐ๋ฅผ ์ฐพ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ๋ฐ”๋กœ ์ž…๋ ฅํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์‹คํ–‰ ๊ฒฐ๊ณผ๋กœ ok ๋ฉ”์„ธ์ง€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
@ResponseBody ์™€ ๊ด€๋ จ์ด ์žˆ๋Š”๋ฐ, ๋’ค์—์„œ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•œ๋‹ค.

 

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜๋Š” ํฌ๋ฉง ํ™•์ธ
์‹œ๊ฐ„
, ๋กœ๊ทธ ๋ ˆ๋ฒจ, ํ”„๋กœ์„ธ์Šค ID, ์“ฐ๋ ˆ๋“œ ๋ช…, ํด๋ž˜์Šค๋ช…, ๋กœ๊ทธ ๋ฉ”์‹œ์ง€


๋กœ๊ทธ ๋ ˆ๋ฒจ ์„ค์ •์„ ๋ณ€๊ฒฝํ•ด์„œ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ํ™•์ธ ๊ฐ€๋Šฅ

LEVEL:
TRACE > DEBUG > INFO > WARN > ERROR

๊ฐœ๋ฐœ ์„œ๋ฒ„๋Š”
debug ์ถœ๋ ฅ
์šด์˜ ์„œ๋ฒ„๋Š”
info ์ถœ๋ ฅ

@Slf4j ๋กœ ๋ณ€๊ฒฝ

 

๋กœ๊ทธ ๋ ˆ๋ฒจ ์„ค์ •

application.properties

#์ „์ฒด ๋กœ๊ทธ ๋ ˆ๋ฒจ ์„ค์ •(๊ธฐ๋ณธ info)
logging.level.root=info

#hello.springmvc ํŒจํ‚ค์ง€์™€๊ทธ ํ•˜์œ„ ๋กœ๊ทธ ๋ ˆ๋ฒจ ์„ค์ •
logging.level.hello.springmvc=debug

 

@SLf4j ๋กฌ๋ณต ์‚ฌ์šฉํ•˜๋ฉด ์„ ์–ธ์—†์ด ๋ฐ”๋กœ log ์‚ฌ์šฉ๊ฐ€๋Šฅ

 

์˜ฌ๋ฐ”๋ฅธ ๋กœ๊ทธ ์‚ฌ์šฉ๋ฒ•

log.debug("data="+data)
๋กœ๊ทธ ์ถœ๋ ฅ ๋ ˆ๋ฒจ์„
info๋กœ ์„ค์ •ํ•ด๋„ ํ•ด๋‹น ์ฝ”๋“œ์— ์žˆ๋Š” "data="+data๊ฐ€ ์‹ค์ œ ์‹คํ–‰์ด ๋˜์–ด ๋ฒ„๋ฆฐ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ ๋ฌธ์ž ๋”ํ•˜๊ธฐ ์—ฐ์‚ฐ์ด ๋ฐœ์ƒํ•œ๋‹ค.

log.debug("data={}", data)

๋กœ๊ทธ ์ถœ๋ ฅ ๋ ˆ๋ฒจ์„ info๋กœ ์„ค์ •ํ•˜๋ฉด ์•„๋ฌด์ผ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.
๋”ฐ๋ผ์„œ ์•ž๊ณผ ๊ฐ™์€ ์˜๋ฏธ์—†๋Š” ์—ฐ์‚ฐ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.
 
๋กœ๊ทธ ์‚ฌ์šฉ์‹œ ์žฅ์ 
์“ฐ๋ ˆ๋“œ ์ •๋ณด, ํด๋ž˜์Šค ์ด๋ฆ„ ๊ฐ™์€ ๋ถ€๊ฐ€ ์ •๋ณด๋ฅผ ํ•จ๊ป˜ ๋ณผ ์ˆ˜ ์žˆ๊ณ , ์ถœ๋ ฅ ๋ชจ์–‘์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
๋กœ๊ทธ ๋ ˆ๋ฒจ์— ๋”ฐ๋ผ ๊ฐœ๋ฐœ ์„œ๋ฒ„์—์„œ๋Š” ๋ชจ๋“  ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜๊ณ , ์šด์˜์„œ๋ฒ„์—์„œ๋Š” ์ถœ๋ ฅํ•˜์ง€ ์•Š๋Š” ๋“ฑ ๋กœ๊ทธ๋ฅผ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค.
์‹œ์Šคํ…œ ์•„์›ƒ ์ฝ˜์†”์—๋งŒ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํŒŒ์ผ์ด๋‚˜ ๋„คํŠธ์›Œํฌ ๋“ฑ, ๋กœ๊ทธ๋ฅผ ๋ณ„๋„์˜ ์œ„์น˜์— ๋‚จ๊ธธ ์ˆ˜ ์žˆ๋‹ค.
ํŠนํžˆ ํŒŒ์ผ๋กœ ๋‚จ๊ธธ ๋•Œ๋Š” ์ผ๋ณ„, ํŠน์ • ์šฉ๋Ÿ‰์— ๋”ฐ๋ผ ๋กœ๊ทธ๋ฅผ ๋ถ„ํ• ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.
์„ฑ๋Šฅ๋„ ์ผ๋ฐ˜ System.out๋ณด๋‹ค ์ข‹๋‹ค. (๋‚ด๋ถ€ ๋ฒ„ํผ๋ง, ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ๋“ฑ๋“ฑ)
๊ทธ๋ž˜์„œ ์‹ค๋ฌด์—์„œ๋Š” ๊ผญ ๋กœ๊ทธ๋ฅผ
์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

3. ์š”์ฒญ ๋งตํ•‘

์š”์ฒญ ์™”์„ ๋•Œ ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ์ด ๋˜๋Š”์ง€

(๋‹จ์ˆœ URL ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ ์š”์†Œ ์กฐํ•ฉ)

 

MappingController.class

ใ„ดhello - springmvc - basic - requestmapping 

@RestController
public class MappingController {
  
  private Logger log = LoggerFactory.getLogger(getClass());
  
  /**
  * ๊ธฐ๋ณธ ์š”์ฒญ
  * ๋‘˜๋‹ค ํ—ˆ์šฉ /hello-basic, /hello-basic/
  * HTTP ๋ฉ”์„œ๋“œ ๋ชจ๋‘ ํ—ˆ์šฉ GET, HEAD, POST, PUT, PATCH, DELETE */
  
  @RequestMapping("/hello-basic")
  public String helloBasic() {
    log.info("helloBasic");
    return "ok";
  }
[์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ์ด์ „]
๋‹ค์Œ ๋‘๊ฐ€์ง€ ์š”์ฒญ์€ ๋‹ค๋ฅธ URL์ด์ง€๋งŒ, ์Šคํ”„๋ง์€ ๋‹ค์Œ URL ์š”์ฒญ๋“ค์„ ๊ฐ™์€ ์š”์ฒญ์œผ๋กœ ๋งคํ•‘ํ•œ๋‹ค.
๋งคํ•‘: /hello-basic
URL ์š”์ฒญ: /hello-basic , /hello-basic/


[
์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ์ดํ›„]
์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ๋ถ€ํ„ฐ๋Š” /hello-basic , /hello-basic/ ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ URL ์š”์ฒญ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
๊ธฐ์กด์—๋Š” ๋งˆ์ง€๋ง‰์— ์žˆ๋Š” / (slash)๋ฅผ ์ œ๊ฑฐํ–ˆ์ง€๋งŒ, ์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ๋ถ€ํ„ฐ๋Š” ๋งˆ์ง€๋ง‰์˜ / (slash)๋ฅผ ์œ ์ง€ํ•œ๋‹ค.

> ๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹ค๋ฅด๊ฒŒ ๋งคํ•‘ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
   ๋งคํ•‘: /hello-basic URL ์š”์ฒญ: /hello-basic
   ๋งคํ•‘: /hello-basic/ URL ์š”์ฒญ: /hello-basic/


HTTP ๋ฉ”์„œ๋“œ

@RequestMapping ์— method ์†์„ฑ์œผ๋กœ HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด HTTP ๋ฉ”์„œ๋“œ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ํ˜ธ์ถœ๋œ๋‹ค.
๋ชจ๋‘ ํ—ˆ์šฉ GET, HEAD, POST, PUT, PATCH, DELETE

 

HTTP ๋ฉ”์„œ๋“œ ๋งตํ•‘

/**
* method ํŠน์ • HTTP ๋ฉ”์„œ๋“œ ์š”์ฒญ๋งŒ ํ—ˆ์šฉ
* GET, HEAD, POST, PUT, PATCH, DELETE
*/

@RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
public String mappingGetV1() {
      log.info("mappingGetV1");
      return "ok";
}

 

HTTP ๋ฉ”์„œ๋“œ ๋งตํ•‘ ์ถ•์•ฝ

/**
* ํŽธ๋ฆฌํ•œ ์ถ•์•ฝ ์• ๋…ธํ…Œ์ด์…˜ (์ฝ”๋“œ๋ณด๊ธฐ) 
* @GetMapping
* @PostMapping
* @PutMapping
* @DeleteMapping
* @PatchMapping
*/
@GetMapping(value = "/mapping-get-v2")
public String mappingGetV2() {
      log.info("mapping-get-v2");
      return "ok";
}

 

PathVariable(๊ฒฝ๋กœ ๋ณ€์ˆ˜) ์‚ฌ์šฉ

(์ž์ฃผ์‚ฌ์šฉ)

/**
* PathVariable ์‚ฌ์šฉ
* ๋ณ€์ˆ˜๋ช…์ด ๊ฐ™์œผ๋ฉด ์ƒ๋žต ๊ฐ€๋Šฅ
* @PathVariable("userId") String userId -> @PathVariable userId
*/
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
      log.info("mappingPath userId={}", data);
      return "ok";
}

 

์ตœ๊ทผ HTTP API๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฆฌ์†Œ์Šค ๊ฒฝ๋กœ์— ์‹๋ณ„์ž ๋„ฃ๋Š” ์Šคํƒ€์ผ ์„ ํ˜ธ

/mapping/userA

/users/1

 

@RequestMapping์€ URL ๊ฒฝ๋กœ๋ฅผ ํ…œํ”Œ๋ฆฟํ™” ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ @PathVariable์„ ์‚ฌ์šฉํ•˜๋ฉด ๋งค์นญ๋˜๋Š” ๋ถ€๋ถ„์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Œ

@PathVariable์˜ ์ด๋ฆ„๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์ด ๊ฐ™์œผ๋ฉด ์ƒ๋žต ๊ฐ€๋Šฅ

 

PathVariable ๋‹ค์ค‘ ๋งตํ•‘

(์ž์ฃผ์‚ฌ์šฉ)

/**
* PathVariable ์‚ฌ์šฉ ๋‹ค์ค‘
*/
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable String userId, @PathVariable Long orderId) {
      log.info("mappingPath userId={}, orderId={}", userId, orderId);
      return "ok";
}

 

ํŠน์ • ํŒŒ๋ผ๋ฏธํ„ฐ ์กฐ๊ฑด ๋งตํ•‘

(์ž์ฃผ ์‚ฌ์šฉ X)

 /**
 * ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ถ”๊ฐ€ ๋งคํ•‘
   * params="mode",
   * params="!mode"
   * params="mode=debug"
   * params="mode!=debug" (! = )
   * params = {"mode=debug","data=good"}
   */
  @GetMapping(value = "/mapping-param", params = "mode=debug")
  public String mappingParam() {
      log.info("mappingParam");
      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";
  }

 

๋ฏธ๋””์–ด ํƒ€์ž… ์กฐ๊ฑด ๋งตํ•‘ - HTTP ์š”์ฒญ Content-Type, consume

/**
* 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";
  }

 

Content-Type์— ๋”ฐ๋ผ ํ˜ธ์ถœํ•˜๋Š”๊ฒŒ ๋‹ฌ๋ผ์งˆ ๊ฒฝ์šฐ

consume : ์„œ๋ฒ„(์ปจํŠธ๋กค๋Ÿฌ)์ž…์žฅ์—์„œ ์†Œ๋น„

produce : ์„œ๋ฒ„์ž…์žฅ์—์„œ ์ƒ์‚ฐํ•ด๋‚ด๋Š” ํƒ€์ž… (HTTP์š”์ฒญ์ด Accept์™€ ๋งž์•„์•ผํ•จ)

 

๋ฏธ๋””์–ด ํƒ€์ž… ์กฐ๊ฑด ๋งตํ•‘ - HTTP ์š”์ฒญ Content-Type, produce

/**
* Accept ํ—ค๋” ๊ธฐ๋ฐ˜ Media Type * produces = "text/html"
* produces = "!text/html"
    * produces = "text/*"
   * produces = "*\/*"
   */
  @PostMapping(value = "/mapping-produce", produces = "text/html")
  public String mappingProduces() {
      log.info("mappingProduces");
      return "ok";
  }

4. ์š”์ฒญ ๋งตํ•‘ - API ์˜ˆ์‹œ

ํšŒ์› ๊ด€๋ฆฌ API ์˜ˆ์‹œ

ํšŒ์› ๋ชฉ๋ก ์กฐํšŒ : GET                '/users'

ํšŒ์› ๋“ฑ๋ก :         POST              '/users'

ํšŒ์› ์กฐํšŒ :         GET                 '/users/{userId}'

ํšŒ์› ์ˆ˜์ • :         PATCH            '/users/{userId}'

ํšŒ์› ์‚ญ์ œ :         DELETE           '/users/{userId}'

 

MappingClassController

@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
    /**
     * GET /mapping/users
     */
    @GetMapping
    public String users() {
        return "get users";
    }
    /**
     * POST /mapping/users
     */
    @PostMapping
    public String addUser() {
        return "post user";
    }
    /**
     * GET /mapping/users/{userId}
     */
    @GetMapping("/{userId}")
    public String findUser(@PathVariable String userId) {
        return "get userId=" + userId;
    }
    /**
     * PATCH /mapping/users/{userId}
     */
    @PatchMapping("/{userId}")
    public String updateUser(@PathVariable String userId) {
        return "update userId=" + userId;
    }
    
    @DeleteMapping("/{userId}")
      public String deleteUser(@PathVariable String userId) {
          return "delete userId=" + userId;
      }
}

5. HTTP ์š”์ฒญ - ๊ธฐ๋ณธ, ํ—ค๋” ์กฐํšŒ

@Annotation ๊ธฐ๋ฐ˜ ์Šคํ”„๋ง ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋‹ค์–‘ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ง€์›

HTTP ํ—ค๋”์ •๋ณด ์กฐํšŒ

 

RequestHeaderController

@Slf4j
@RestController
public class RequestHeaderController {
    @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("header host={}", host);
        log.info("myCookie={}", cookie);
        return "ok";
    }
}
HttpServletResponse

HttpMethod
: HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์กฐํšŒํ•œ๋‹ค. org.springframework.http.HttpMethod

Locale
: Locale ์ •๋ณด๋ฅผ ์กฐํšŒํ•œ๋‹ค.

@RequestHeader MultiValueMap<String, String> headerMap

    ๋ชจ๋“  HTTP ํ—ค๋”๋ฅผ MultiValueMap ํ˜•์‹์œผ๋กœ ์กฐํšŒํ•œ๋‹ค

@RequestHeader("host") String host
    ํŠน์ • HTTP ํ—ค๋”๋ฅผ ์กฐํšŒํ•œ๋‹ค.
    ์†์„ฑ

        ํ•„์ˆ˜ ๊ฐ’ ์—ฌ๋ถ€: required
        ๊ธฐ๋ณธ ๊ฐ’ ์†์„ฑ: defaultValue

@CookieValue(value = "myCookie", required = false) String cookie

    ํŠน์ • ์ฟ ํ‚ค๋ฅผ ์กฐํšŒํ•œ๋‹ค.
    
์†์„ฑ

        ํ•„์ˆ˜ ๊ฐ’ ์—ฌ๋ถ€: required
        
๊ธฐ๋ณธ ๊ฐ’: defaultValue

MultiValueMap
    MAP๊ณผ ์œ ์‚ฌํ•œ๋ฐ, ํ•˜๋‚˜์˜ ํ‚ค์— ์—ฌ๋Ÿฌ ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
    HTTP header, HTTP
์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๊ฐ™์ด ํ•˜๋‚˜์˜ ํ‚ค์— ์—ฌ๋Ÿฌ ๊ฐ’์„ ๋ฐ›์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

        keyA=value1&keyA=value2

6. HTTP ์š”์ฒญ - ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ, HTML Form

ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ HTTP ์š”์ฒญ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ• ๋”ฑ ์„ธ๊ฐ€์ง€ ๋ฟ(GET / POST / HTTP message body)

 

์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ(request parameter) ์กฐํšŒ

 

RequestParamController

@Slf4j
@Controller
public class RequestParamController {

/**
* ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ์—†์œผ๋ฉด์„œ ์ด๋ ‡๊ฒŒ ์‘๋‹ต์— ๊ฐ’์„ ์ง์ ‘ ์ง‘์–ด๋„ฃ์œผ๋ฉด, view ์กฐํšŒX
*/

  @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");
  }
}

7. HTTP ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ  - @RequestParam

requestParamV2

/**
* @RequestParam ์‚ฌ์šฉ
* - ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์œผ๋กœ ๋ฐ”์ธ๋”ฉ
* @ResponseBody ์ถ”๊ฐ€
* - View ์กฐํšŒ๋ฅผ ๋ฌด์‹œํ•˜๊ณ , HTTP message body์— ์ง์ ‘ ํ•ด๋‹น ๋‚ด์šฉ ์ž…๋ ฅ */

@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(
  @RequestParam("username") String memberName, @RequestParam("age") int memberAge) {
  log.info("username={}, age={}", memberName, memberAge);
  return "ok";
}
@RequestParam : ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์œผ๋กœ ๋ฐ”์ธ๋”ฉ
@ResponseBody : View ์กฐํšŒ๋ฅผ ๋ฌด์‹œํ•˜๊ณ , HTTP message body์— ์ง์ ‘ ํ•ด๋‹น ๋‚ด์šฉ ์ž…๋ ฅ

@RequestParam์˜ name(value) ์†์„ฑ์ด ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉ @RequestParam("username") String memberName
= request.getParameter("username")

 

๊ฐœ์„ 

/**
* @RequestParam ์‚ฌ์šฉ
* HTTP ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์ด ๋ณ€์ˆ˜ ์ด๋ฆ„๊ณผ ๊ฐ™์œผ๋ฉด @RequestParam(name="xx") ์ƒ๋žต ๊ฐ€๋Šฅ */
  @ResponseBody
  @RequestMapping("/request-param-v3")
  public String requestParamV3(
          @RequestParam String username,
          @RequestParam int age) {
      log.info("username={}, age={}", username, age);
      return "ok";
}

 

๊ฐœ์„ 

/**
* @RequestParam ์‚ฌ์šฉ
* String, int ๋“ฑ์˜ ๋‹จ์ˆœ ํƒ€์ž…์ด๋ฉด @RequestParam ๋„ ์ƒ๋žต ๊ฐ€๋Šฅ */
  @ResponseBody
  @RequestMapping("/request-param-v4")
  public String requestParamV4(String username, int age) {
           log.info("username={}, age={}", username, age);
        return "ok";
    }

์ฐธ๊ณ )

@RequestParam์ด ์—†๋Š”๊ฑด ์˜ค๋ฐ”..

 

 

 

ํŒŒ๋ผ๋ฏธํ„ฐ ํ•„์ˆ˜ ์—ฌ๋ถ€

/**
     * @RequestParam.required
* /request-param-required -> username์ด ์—†์œผ๋ฏ€๋กœ ์˜ˆ์™ธ *
* ์ฃผ์˜!
* /request-param-required?username= -> ๋นˆ๋ฌธ์ž๋กœ ํ†ต๊ณผ *
* ์ฃผ์˜!
* /request-param-required
* int age -> null์„ int์— ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅ, ๋”ฐ๋ผ์„œ Integer ๋ณ€๊ฒฝํ•ด์•ผ ํ•จ(๋˜๋Š” ๋‹ค์Œ์— ๋‚˜์˜ค๋Š”
defaultValue ์‚ฌ์šฉ) */

@ResponseBody
    @RequestMapping("/request-param-required")
    public String requestParamRequired(
            @RequestParam(required = true) String username,
            @RequestParam(required = false) Integer age) {
             log.info("username={}, age={}", username, age);
                    return "ok";
  }

- required true / false 

- true์ธ๋ฐ ์—†์œผ๋ฉด 400 ์˜ˆ์™ธ

 

์ฃผ์˜)

ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ์žˆ๊ณ  ๊ฐ’์ด ์—†๋Š” ๊ฒฝ์šฐ ํ†ต๊ณผ๋จ!!!

 

์ฃผ์˜)

int์— null ๋„ฃ์„ ์ˆ˜ ์—†์Œ -> Integerํ˜•์œผ๋กœ ์„ ์–ธ

 

๊ธฐ๋ณธ๊ฐ’ ์ ์šฉ ๊ฐ€๋Šฅ

/**
   * @RequestParam
* - defaultValue ์‚ฌ์šฉ *
* ์ฐธ๊ณ : defaultValue๋Š” ๋นˆ ๋ฌธ์ž์˜ ๊ฒฝ์šฐ์—๋„ ์ ์šฉ * /request-param-default?username=
*/
  @ResponseBody
  @RequestMapping("/request-param-default")
  public String requestParamDefault(
          @RequestParam(required = true, defaultValue = "guest") String username,
          @RequestParam(required = false, defaultValue = "-1") int age) {
           log.info("username={}, age={}", username, age);
                  return "ok";
  }

- ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— required ์˜๋ฏธ ์—†๊ณ  Integer์‚ฌ์šฉํ•  ํ•„์š” ์—†๋‹ค

 

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ Map์œผ๋กœ ์กฐํšŒํ•˜๊ธฐ - requestParamMap

/**
   * @RequestParam Map, MultiValueMap
   * Map(key=value)
   * MultiValueMap(key=[value1, value2, ...]) ex) (key=userIds, value=[id1, id2])
   */
  @ResponseBody
  @RequestMapping("/request-param-map")
  public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
      log.info("username={}, age={}", paramMap.get("username"),
  paramMap.get("age"));
      return "ok";
  }
 
@RequestParam Map : Map(key=value)
@RequestParam MultiValueMap
     MultiValueMap(key=[value1, value2, ...] ex) (key=userIds, value=[id1, id2])

* ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐ’์ด 1๊ฐœ๊ฐ€ ํ™•์‹คํ•˜๋‹ค๋ฉด Map ์„ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ, ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด MultiValueMap ์„ ์‚ฌ์šฉ


8. HTTP ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ  - @ModelAttribute

์‹ค์ œ ๊ฐœ๋ฐœํ•˜๋ฉด ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ›์•„ ํ•„์š”ํ•œ ๊ฐ์ฒด ๋งŒ๋“ค๊ณ  ๊ทธ ๊ฐ์ฒด์— ๊ฐ’ ๋„ฃ์–ด์ฃผ๋Š”๋ฐ,

์Šคํ”„๋ง์€ ์ด ๊ณผ์ •์„ ์ž๋™ํ™”ํ•ด์คŒ

 

์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉ ๋ฐ›์„ ๊ฐ์ฒด

  @Data
  public class HelloData {
      private String username;
      private int age;
  }

๋กฌ๋ณต @Data

@Getter , @Setter , @ToString , @EqualsAndHashCode , @RequiredArgsConstructor ๋ฅผ ์ž๋™์œผ๋กœ ์ ์šฉ


ModelAttribute ์ ์šฉ 

/**
* @ModelAttribute ์‚ฌ์šฉ
* ์ฐธ๊ณ : model.addAttribute(helloData) ์ฝ”๋“œ๋„ ํ•จ๊ป˜ ์ž๋™ ์ ์šฉ๋จ, ๋’ค์— model์„ ์„ค๋ช…ํ•  ๋•Œ ์ž์„ธํžˆ ์„ค๋ช…
*/
  @ResponseBody
  @RequestMapping("/model-attribute-v1")
  public String modelAttributeV1(@ModelAttribute HelloData helloData) {
      log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
      return "ok";
  }

- ๋ฐ”์ธ๋”ฉ is magic

 

๊ฐœ์„  (์ƒ๋žต)

/*
* @ModelAttribute ์ƒ๋žต ๊ฐ€๋Šฅ
* String, int ๊ฐ™์€ ๋‹จ์ˆœ ํƒ€์ž… = @RequestParam
* argument resolver ๋กœ ์ง€์ •ํ•ด๋‘” ํƒ€์ž… ์™ธ = @ModelAttribute */
    @ResponseBody
    @RequestMapping("/model-attribute-v2")
    public String modelAttributeV2(HelloData helloData) {
        log.info("username={}, age={}", helloData.getUsername(),
    helloData.getAge());
        return "ok";
    }

- String, int, Integer ๊ฐ™์€ ๋‹จ์ˆœ ํƒ€์ž… -> @RequestParam

- argument resolver๋กœ ์ง€์ •ํ•ด๋‘” ํƒ€์ž… ์™ธ @ModelAttribute


9. HTTP ์š”์ฒญ ๋ฉ”์‹œ์ง€ - ๋‹จ์ˆœ ํ…์ŠคํŠธ

HTTP message body์— ๋ฐ์ดํ„ฐ ์ง์ ‘ ๋‹ด์•„์„œ ์š”์ฒญํ•˜๋Š” ๊ฒฝ์šฐ

- HTTP API์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ (JSON)

- POST, PUT,  PATCH

 

์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋‹ค๋ฅด๊ฒŒ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ํ†ตํ•ด ์ง์ ‘ ๋ฐ์ดํ„ฐ๊ฐ€ ๋„˜์–ด์˜ค๋Š” ๊ฒฝ์šฐ๋Š” @RequestParam, @ModelAttribute ์‚ฌ์šฉ๋ถˆ๊ฐ€

 

HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์˜ ๋ฐ์ดํ„ฐ๋ฅผ InputStream์„ ํ†ตํ•ด ์ง์ ‘ ์ฝ๊ธฐ

 

RequestBodyStringController

@Slf4j
@Controller
public class RequestBodyStringController {
  
  @PostMapping("/request-body-string-v1")
  public void requestBodyString(HttpServletRequest request,
  HttpServletResponse response) throws IOException {
    ServletInputStream inputStream = request.getInputStream();
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
    log.info("messageBody={}", messageBody);
    response.getWriter().write("ok");
  } 
}

 

๊ฐœ์„ 

/**
* InputStream(Reader): HTTP ์š”์ฒญ ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์˜ ๋‚ด์šฉ์„ ์ง์ ‘ ์กฐํšŒ 
* OutputStream(Writer): HTTP ์‘๋‹ต ๋ฉ”์‹œ์ง€์˜ ๋ฐ”๋””์— ์ง์ ‘ ๊ฒฐ๊ณผ ์ถœ๋ ฅ 
*/
  @PostMapping("/request-body-string-v2")
  public void requestBodyStringV2(InputStream inputStream, Writer responseWriter)
  throws IOException {
   String messageBody = StreamUtils.copyToString(inputStream,StandardCharsets.UTF_8);
   log.info("messageBody={}", messageBody);
   responseWriter.write("ok");
}

์Šคํ”„๋ง MVC๋Š” ๋‹ค์Œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง€์›

InputStream(Reader): HTTP ์š”์ฒญ ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์˜ ๋‚ด์šฉ์„ ์ง์ ‘ ์กฐํšŒ 
OutputStream(Writer): HTTP ์‘๋‹ต ๋ฉ”์‹œ์ง€์˜ ๋ฐ”๋””์— ์ง์ ‘ ๊ฒฐ๊ณผ ์ถœ๋ ฅ

 

๋” ๊ฐœ์„ 

/**
* HttpEntity: HTTP header, body ์ •๋ณด๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์กฐํšŒ
* - ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด๋ฅผ ์ง์ ‘ ์กฐํšŒ(@RequestParam X, @ModelAttribute X)
* - HttpMessageConverter ์‚ฌ์šฉ -> StringHttpMessageConverter ์ ์šฉ *
*
* ์‘๋‹ต์—์„œ๋„ HttpEntity ์‚ฌ์šฉ ๊ฐ€๋Šฅ
* - ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด ์ง์ ‘ ๋ฐ˜ํ™˜(view ์กฐํšŒX)
* - HttpMessageConverter ์‚ฌ์šฉ -> StringHttpMessageConverter ์ ์šฉ
*/
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {
      String messageBody = httpEntity.getBody();
      log.info("messageBody={}", messageBody);
      return new HttpEntity<>("ok");
  }

์Šคํ”„๋ง MVC๋Š” ๋‹ค์Œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ง€์›

HttpEntity: HTTP header, body ์ •๋ณด๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์กฐํšŒ
- ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด๋ฅผ ์ง์ ‘ ์กฐํšŒ
- ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ๊ณผ ๊ด€๊ณ„ ์—†์Œ

@RequestParam X, @ModelAttribute X HttpEntity๋Š” ์‘๋‹ต์—๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

- ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด ์ง์ ‘ ๋ฐ˜ํ™˜ ํ—ค๋” ์ •๋ณด ํฌํ•จ ๊ฐ€๋Šฅ
- view ์กฐํšŒX
HttpEntity ๋ฅผ ์ƒ์†๋ฐ›์€ ๋‹ค์Œ ๊ฐ์ฒด๋“ค๋„ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณต

RequestEntity

- HttpMethod, url ์ •๋ณด๊ฐ€ ์ถ”๊ฐ€, ์š”์ฒญ์—์„œ ์‚ฌ์šฉ
ResponseEntity

- HTTP ์ƒํƒœ ์ฝ”๋“œ ์„ค์ • ๊ฐ€๋Šฅ, ์‘๋‹ต์—์„œ ์‚ฌ์šฉ
- return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED)

 

๋” ๊ฐœ์„ 

/**
     * @RequestBody
* - ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด๋ฅผ ์ง์ ‘ ์กฐํšŒ(@RequestParam X, @ModelAttribute X)
* - HttpMessageConverter ์‚ฌ์šฉ -> StringHttpMessageConverter ์ ์šฉ *
* @ResponseBody
* - ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด ์ง์ ‘ ๋ฐ˜ํ™˜(view ์กฐํšŒX)
* - HttpMessageConverter ์‚ฌ์šฉ -> StringHttpMessageConverter ์ ์šฉ */
    @ResponseBody
    @PostMapping("/request-body-string-v4")
    public String requestBodyStringV4(@RequestBody String messageBody) {
        log.info("messageBody={}", messageBody);
        return "ok";
    }

@Requestbody

@RequestBody ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
์ฐธ๊ณ ๋กœ ํ—ค๋” ์ •๋ณด๊ฐ€
ํ•„์š”ํ•˜๋‹ค๋ฉด HttpEntity ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ @RequestHeader ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
์ด๋ ‡๊ฒŒ ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ์ง์ ‘ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์€ ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” @RequestParam , @ModelAttribute ์™€๋Š” ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋‹ค.

@Responsebody

@ResponseBody ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ง์ ‘ ๋‹ด์•„์„œ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
๋ฌผ๋ก  ์ด ๊ฒฝ์šฐ์—๋„ view๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค

 

์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ vs HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””

์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ: @RequestParam , @ModelAttribute
HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ์ง์ ‘ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ: @RequestBody

10. HTTP ์š”์ฒญ ๋ฉ”์‹œ์ง€ - JSON

HTTP API์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” JSON ๋ฐ์ดํ„ฐ ํ˜•์‹ ์กฐํšŒ

 

RequestBodyJsonController

@Slf4j
  @Controller
  public class RequestBodyJsonController {
      private ObjectMapper objectMapper = new 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);
          log.info("messageBody={}", messageBody);
          HelloData data = objectMapper.readValue(messageBody, HelloData.class);
          log.info("username={}, age={}", data.getUsername(), data.getAge());
          response.getWriter().write("ok");
      }
}
HttpServletRequest๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ง์ ‘ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์™€์„œ, ๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
๋ฌธ์ž๋กœ ๋œ JSON ๋ฐ์ดํ„ฐ๋ฅผ Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ objectMapper ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ž๋ฐ” ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

 

๊ฐœ์„ 

 /**
* @RequestBody
* HttpMessageConverter ์‚ฌ์šฉ -> StringHttpMessageConverter ์ ์šฉ *
* @ResponseBody
* - ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— @ResponseBody ์ ์šฉ
* - ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด ์ง์ ‘ ๋ฐ˜ํ™˜(view ์กฐํšŒX)
* - HttpMessageConverter ์‚ฌ์šฉ -> StringHttpMessageConverter ์ ์šฉ */
  @ResponseBody
  @PostMapping("/request-body-json-v2")
  public String requestBodyJsonV2(@RequestBody String messageBody) throws
  IOException {
      HelloData data = objectMapper.readValue(messageBody, HelloData.class);
      log.info("username={}, age={}", data.getUsername(), data.getAge());
      return "ok";
}

 

๋” ๊ฐœ์„ 

/**
* @RequestBody ์ƒ๋žต ๋ถˆ๊ฐ€๋Šฅ(@ModelAttribute ๊ฐ€ ์ ์šฉ๋˜์–ด ๋ฒ„๋ฆผ)
* HttpMessageConverter ์‚ฌ์šฉ -> MappingJackson2HttpMessageConverter (content-
  type: application/json)
   *
*/
  @ResponseBody
  @PostMapping("/request-body-json-v3")
  public String requestBodyJsonV3(@RequestBody HelloData data) {
  log.info("username={}, age={}", data.getUsername(), data.getAge());
  return "ok";
}

 

@RequestBody

@RequestBody HelloData data
@RequestBody
์— ์ง์ ‘ ๋งŒ๋“  ๊ฐ์ฒด๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

HttpEntity , @RequestBody ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์˜ ๋‚ด์šฉ์„ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๋ฌธ์ž๋‚˜ ๊ฐ์ฒด ๋“ฑ์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ค€๋‹ค.
HTTP
๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋Š” ๋ฌธ์ž ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ JSON๋„ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š”๋ฐ, ์šฐ๋ฆฌ๊ฐ€ ๋ฐฉ๊ธˆ V2์—์„œ ํ–ˆ๋˜ ์ž‘์—…์„ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋’ค์— HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ์—์„œ ๋‹ค๋ฃฌ๋‹ค.

์ฃผ์˜) HTTP ์š”์ฒญ์‹œ์— content-type์ด application/json์ธ์ง€ ๊ผญ! ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค

 

  1. @RequestBody ์š”์ฒญ : JSON ์š”์ฒญ HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ ๊ฐ์ฒด
  2. @ResponseBody ์‘๋‹ต : ๊ฐ์ฒด HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ JSON ์‘๋‹ต

11. HTTP ์‘๋‹ต - ์ •์  ๋ฆฌ์†Œ์Šค, ๋ทฐ ํ…œํ”Œ๋ฆฟ

์Šคํ”„๋ง ์„œ๋ฒ„์—์„œ ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ• 3๊ฐ€์ง€

1) ์ •์  ๋ฆฌ์†Œ์Šค 

- ex) ์›น ๋ธŒ๋ผ์šฐ์ €์— ์ •์ ์ธ HTML, CSS, JS ์ œ๊ณตํ•  ๋• ์ •์  ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ

2) ๋ทฐ ํ…œํ”Œ๋ฆฟ ์‚ฌ์šฉ

- ex) ์›น ๋ธŒ๋ผ์šฐ์ €์— ๋™์ ์ธ HTML์„ ์ œ๊ณตํ•  ๋• ๋ทฐ ํ…œํ”Œ๋ฆฟ ์‚ฌ์šฉ

3) HTTP ๋ฉ”์‹œ์ง€ ์‚ฌ์šฉ

- ex) HTTP API๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ HTML์ด ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ด์•ผํ•˜๋ฏ€๋กœ,

         HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— JSON ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์–ด ๋ณด๋ƒ„

 

 

1) ์ •์  ๋ฆฌ์†Œ์Šค

 

[๊ฒฝ๋กœ]

src/main/resources

ใ„ด ๋ฆฌ์†Œ์Šค ๋ณด๊ด€ํ•˜๋Š” ๊ณณ, ํด๋ž˜์ŠคํŒจ์Šค์˜ ์‹œ์ž‘๊ฒฝ๋กœ

ใ„ด ๋‹ค์Œ ๋””๋ ‰ํ† ๋ฆฌ์— ๋ฆฌ์†Œ์Šค๋ฅผ ๋„ฃ์–ด๋‘๋ฉด ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ์ •์  ๋ฆฌ์†Œ์Šค๋กœ ์„œ๋น„์Šค ์ œ๊ณต

 

src/main/resources/static

src/main/resources/static/basic/hello-form.html

ใ„ด ๋‹ค์Œ ๊ฒฝ๋กœ์— ํŒŒ์ผ์ด ๋“ค์–ด์žˆ์œผ๋ฉด

 

http://localhost:8080/basic/hello-form.html

ใ„ด์›น๋ธŒ๋ผ์šฐ์ €์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํ–‰ํ•˜๋ฉด ๋จ

 

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>

 

์ปจํŠธ๋กค๋Ÿฌ

 @Controller
  public class ResponseViewController {
      @RequestMapping("/response-view-v1")
      public ModelAndView responseViewV1() {
         ModelAndView mav = new ModelAndView("response/hello")
                  .addObject("data", "hello!");
         return mav; 
      }
      
      // view์˜ ๋…ผ๋ฆฌ์ ์ด๋ฆ„
      @RequestMapping("/response-view-v2")
      public String responseViewV2(Model model) {
          model.addAttribute("data", "hello!!");
          return "response/hello";
      }
}

12. HTTP ์‘๋‹ต - HTTP API, ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ง์ ‘ ์ž…๋ ฅ

HTML์ด ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ -> ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— JSON ๋‹ด์•„ ๋ณด๋ƒ„

 

ResponseBodyController

@Slf4j
@Controller
//@RestController
public class ResponseBodyController {
    @GetMapping("/response-body-string-v1")
    public void responseBodyV1(HttpServletResponse response) throws IOException
{
        response.getWriter().write("ok");
    }
/**
* HttpEntity, ResponseEntity(Http Status ์ถ”๊ฐ€)
* @return
*/
    @GetMapping("/response-body-string-v2")
    public ResponseEntity<String> responseBodyV2() {
        return new ResponseEntity<>("ok", HttpStatus.OK);
    }
    @ResponseBody
    @GetMapping("/response-body-string-v3")
    public String responseBodyV3() {
        return "ok";
    }
    @GetMapping("/response-body-json-v1")
    public ResponseEntity<HelloData> responseBodyJsonV1() {
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        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("userA");
          helloData.setAge(20);
          return helloData;
      }
}
responseBodyV1
์„œ๋ธ”๋ฆฟ์„ ์ง์ ‘ ๋‹ค๋ฃฐ ๋•Œ ์ฒ˜๋Ÿผ
HttpServletResponse ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด์„œ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ง์ ‘ ok ์‘๋‹ต ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

responseBodyV2
ResponseEntity ์—”ํ‹ฐํ‹ฐ๋Š” HttpEntity ๋ฅผ ์ƒ์† ๋ฐ›์•˜๋Š”๋ฐ, HttpEntity๋Š” HTTP ๋ฉ”์‹œ์ง€์˜ ํ—ค๋”, ๋ฐ”๋”” ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ResponseEntity ๋Š” ์—ฌ๊ธฐ์— ๋”ํ•ด์„œ HTTP ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
HttpStatus.CREATED ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด 201 ์‘๋‹ต์ด ๋‚˜๊ฐ€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

responseBodyV3
@ResponseBody ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด view๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ†ตํ•ด์„œ HTTP ๋ฉ”์‹œ์ง€๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค. ResponseEntity ๋„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

responseBodyJsonV1
ResponseEntity ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ†ตํ•ด์„œ JSON ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜๋˜์–ด์„œ ๋ฐ˜ํ™˜๋œ๋‹ค.

responseBodyJsonV2

ResponseEntity ๋Š” HTTP ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, @ResponseBody ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ๊ฒƒ์„ ์„ค์ •ํ•˜๊ธฐ ๊นŒ๋‹ค๋กญ๋‹ค.
@ResponseStatus(HttpStatus.OK) ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์‘๋‹ต ์ฝ”๋“œ๋„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌผ๋ก  ์• ๋…ธํ…Œ์ด์…˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜๋Š” ์—†๋‹ค. ํ”„๋กœ๊ทธ๋žจ ์กฐ๊ฑด์— ๋”ฐ๋ผ์„œ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด ResponseEntity ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@RestController

@Controller ๋Œ€์‹ ์— @RestController ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด, ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์— ๋ชจ๋‘ @ResponseBody ๊ฐ€ ์ ์šฉ๋˜๋Š” ํšจ๊ณผ๊ฐ€ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ทฐ ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅํ•œ๋‹ค. ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ Rest API(HTTP API)๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์ด๋‹ค.

์ฐธ๊ณ ๋กœ
@ResponseBody ๋Š” ํด๋ž˜์Šค ๋ ˆ๋ฒจ์— ๋‘๋ฉด ์ „์ฒด ๋ฉ”์„œ๋“œ์— ์ ์šฉ๋˜๋Š”๋ฐ, @RestController ์—๋…ธํ…Œ์ด์…˜ ์•ˆ์— @ResponseBody ๊ฐ€ ์ ์šฉ๋˜์–ด ์žˆ๋‹ค.

13. HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ

HTTP API (JSON) ๋ฐ์ดํ„ฐ๋ฅผ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ง์ ‘ ์ฝ๊ฑฐ๋‚˜ ์“ฐ๋Š” ๊ฒฝ์šฐ HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์‚ฌ์šฉ

@ResponseBody 

- HTTP body์— ๋ฌธ์ž ๋‚ด์šฉ ์ง์ ‘ ๋ฐ˜ํ™˜

- viewResolver๋Œ€์‹  HttpMessageConverter ๋™์ž‘

- ๊ธฐ๋ณธ ๋ฌธ์ž ์ฒ˜๋ฆฌ : StringHttpMessageConverter

- ๊ธฐ๋ณธ ๊ฐ์ฒด ์ฒ˜๋ฆฌ : MappingJackson2HttpMessageConverter

 

์ฐธ๊ณ ) ์‘๋‹ต์˜ ๊ฒฝ์šฐ ํด๋ผ์ด์–ธํŠธ์˜ HTTP Accept ํ—ค๋”์™€ ์„œ๋ฒ„์˜ ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ˜ํ™˜ ํƒ€์ž… ์ •๋ณด ์กฐํ•ฉํ•ด์„œ HttpMessageConverter ์„ ํƒ๋จ

 

์Šคํ”„๋ง MVC๋Š” ๋‹ค์Œ์˜ ๊ฒฝ์šฐ HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ ์ ์šฉ

HTTP ์š”์ฒญ: @RequestBody , HttpEntity(RequestEntity) 
HTTP
์‘๋‹ต: @ResponseBody , HttpEntity(ResponseEntity) 

 

HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค

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;
}

HTTP ๋ฉ”์„ธ์ง€ ์ปจ๋ฒ„ํ„ฐ๋Š” ์š”์ฒญ ์‘๋‹ต ๋ชจ๋‘ ์‚ฌ์šฉ๋จ

- canRead(), canWrite() : ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ํ•ด๋‹น ํด๋ž˜์Šค, ๋ฏธ๋””์–ดํƒ€์ž… ์ง€์›ํ•˜๋Š”์ง€ ์ฒดํฌ

- read(), write() : ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ†ตํ•ด์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ๊ธฐ๋Šฅ

 

์Šคํ”„๋ง๋ถ€ํŠธ ๊ธฐ๋ณธ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ -> ๋Œ€์ƒ ํด๋ž˜์Šค ํƒ€์ž…๊ณผ ๋ฏธ๋””์–ด ํƒ€์ž… ์ฒดํฌํ•ด์„œ ์‚ฌ์šฉ์—ฌ๋ถ€ ๊ฒฐ์ •

 

๋ช‡๊ฐ€์ง€ ์ฃผ์š”ํ•œ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ

ByteArrayHttpMessageConverter : byte[] ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
>ํด๋ž˜์Šค ํƒ€์ž…: byte[] , ๋ฏธ๋””์–ดํƒ€์ž…: */* ,
>์š”์ฒญ ์˜ˆ) @RequestBody byte[] data
>์‘๋‹ต ์˜ˆ) @ResponseBody return byte[] ์“ฐ๊ธฐ ๋ฏธ๋””์–ดํƒ€์ž… application/octet-stream


StringHttpMessageConverter
: String ๋ฌธ์ž๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค. ํด๋ž˜์Šค ํƒ€์ž…: String , ๋ฏธ๋””์–ดํƒ€์ž…: */*
>์š”์ฒญ ์˜ˆ) @RequestBody String data
>์‘๋‹ต ์˜ˆ) @ResponseBody return "ok" ์“ฐ๊ธฐ ๋ฏธ๋””์–ดํƒ€์ž… text/plain


MappingJackson2HttpMessageConverter
: application/json
>ํด๋ž˜์Šค ํƒ€์ž…: ๊ฐ์ฒด ๋˜๋Š” HashMap , ๋ฏธ๋””์–ดํƒ€์ž… application/json ๊ด€๋ จ
>์š”์ฒญ ์˜ˆ
) @RequestBody HelloData data
>์‘๋‹ต ์˜ˆ) @ResponseBody return helloData ์“ฐ๊ธฐ ๋ฏธ๋””์–ดํƒ€์ž… application/json ๊ด€๋ จ

 

StringHttpMessageConverter

content-type: application/json

@RequestMapping
void hello(@RequestBody String data) {}

MappingJackson2HttpMessageConverter

content-type: application/json

@RequestMapping
void hello(@RequestBody HelloData data) {}

์•ˆ๋˜๋Š” ์ผ€์ด์Šค

content-type: text/html

@RequestMapping
void hello(@RequestBody HelloData data) {}
HTTP ์š”์ฒญ ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ
HTTP ์š”์ฒญ์ด ์˜ค๊ณ , ์ปจํŠธ๋กค๋Ÿฌ์—์„œ @RequestBody , HttpEntity ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด canRead() ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

  >๋Œ€์ƒ ํด๋ž˜์Šค ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”๊ฐ€.
         ์˜ˆ) @RequestBody ์˜ ๋Œ€์ƒ ํด๋ž˜์Šค ( byte[] , String , HelloData )

  >HTTP ์š”์ฒญ์˜ Content-Type ๋ฏธ๋””์–ด ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”๊ฐ€.
         ์˜ˆ) text/plain , application/json , */*

canRead() ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋ฉด read() ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ณ , ๋ฐ˜ํ™˜ํ•œ๋‹ค.
HTTP ์‘๋‹ต ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
์ปจํŠธ๋กค๋Ÿฌ์—์„œ @ResponseBody , HttpEntity ๋กœ ๊ฐ’์ด ๋ฐ˜ํ™˜๋œ๋‹ค.
๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด canWrite() ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

  >๋Œ€์ƒ ํด๋ž˜์Šค ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”๊ฐ€.
         ์˜ˆ) return์˜ ๋Œ€์ƒ ํด๋ž˜์Šค ( byte[] , String , HelloData )

  >HTTP ์š”์ฒญ์˜ Accept ๋ฏธ๋””์–ด ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”๊ฐ€.(๋” ์ •ํ™•ํžˆ๋Š” @RequestMapping ์˜ produces ) 
         
์˜ˆ) text/plain , application/json , */*

canWrite() ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋ฉด write() ๋ฅผ ํ˜ธ์ถœํ•ด์„œ HTTP ์‘๋‹ต ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

14. ์š”์ฒญ ๋งตํ•‘ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Žํ„ฐ ๊ตฌ์กฐ

HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋Š” ์Šคํ”„๋ง MVC ์–ด๋””์„œ ์‚ฌ์šฉ?

@RequestMapping์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ์ธ RequestMappingHandlerAdapter(์š”์ฒญ ๋งตํ•‘ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ)

 

RequestMappingHandlerAdapter ๋™์ž‘๋ฐฉ์‹

ArgumentResolver

์ƒ๊ฐํ•ด๋ณด๋ฉด, ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์˜ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋งค์šฐ ๋‹ค์–‘ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
HttpServletRequest , Model ์€ ๋ฌผ๋ก ์ด๊ณ , @RequestParam , @ModelAttribute ๊ฐ™์€ ์• ๋…ธํ…Œ์ด์…˜ ๊ทธ๋ฆฌ๊ณ  @RequestBody , HttpEntity ๊ฐ™์€ HTTP ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„๊นŒ์ง€ ๋งค์šฐ ํฐ ์œ ์—ฐํ•จ์„ ๋ณด์—ฌ์ฃผ์—ˆ๋‹ค.
์ด๋ ‡๊ฒŒ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ ๊ฐ€ ๋ฐ”๋กœ ArgumentResolver ๋•๋ถ„์ด๋‹ค.

์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” RequestMappingHandlerAdapter ๋Š” ๋ฐ”๋กœ ์ด 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() ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ ์ƒ์„ฑ๋œ ๊ฐ์ฒด๊ฐ€ ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ์‹œ ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ์ด๋‹ค.

 

 

ReturnValueHandler

->์ปจํŠธ๋กค๋Ÿฌ์—์„œ String์œผ๋กœ ๋ทฐ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•ด๋„, ๋™์ž‘ํ•˜๋Š” ์ด์œ 

 

HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ ์œ„์น˜

์š”์ฒญ์˜ ๊ฒฝ์šฐ
@RequestBody ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ArgumentResolver ๊ฐ€ ์žˆ๊ณ ,
HttpEntity ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ArgumentResolver ๊ฐ€ ์žˆ๋‹ค.
์ด ArgumentResolver ๋“ค์ด HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค

์‘๋‹ต์˜ ๊ฒฝ์šฐ
@ResponseBody ์™€ HttpEntity ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ReturnValueHandler ๊ฐ€ ์žˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์—ฌ๊ธฐ์—์„œ HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ ๋‹ค.

์Šคํ”„๋ง MVC๋Š” @RequestBody @ResponseBody ๊ฐ€ ์žˆ์œผ๋ฉด RequestResponseBodyMethodProcessor (ArgumentResolver)
HttpEntity ๊ฐ€ ์žˆ์œผ๋ฉด HttpEntityMethodProcessor (ArgumentResolver)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
ํ™•์žฅ
์Šคํ”„๋ง์€ ๋‹ค์Œ์„ ๋ชจ๋‘ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ œ๊ณตํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ํ•„์š”ํ•˜๋ฉด ์–ธ์ œ๋“ ์ง€ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.
HandlerMethodArgumentResolver
HandlerMethodReturnValueHandler
HttpMessageConverter

ํ™•์žฅํ•  ์ผ ๋งŽ์ง€ ์•Š์Œ

 

ํ™•์žฅ์ด ํ•„์š”ํ•  ๋•Œ WebMvcConfigurer ๊ฒ€์ƒ‰

@Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addArgumentResolvers(List<HandlerMethodArgumentResolver>
    resolvers) {
//...
} @Override
  public void extendMessageConverters(List<HttpMessageConverter<?>>
  converters) {
//...
} };
}
  • ๋„ค์ด๋ฒ„ ๋ธ”๋Ÿฌ๊ทธ ๊ณต์œ ํ•˜๊ธฐ
  • ๋„ค์ด๋ฒ„ ๋ฐด๋“œ์— ๊ณต์œ ํ•˜๊ธฐ
  • ํŽ˜์ด์Šค๋ถ ๊ณต์œ ํ•˜๊ธฐ
  • ์นด์นด์˜ค์Šคํ† ๋ฆฌ ๊ณต์œ ํ•˜๊ธฐ