[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 ์์ฑ ํ์ ๊ฐ ์ฌ๋ถ: 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์ธ์ง ๊ผญ! ํ์ธํด์ผ ํ๋ค
- @RequestBody ์์ฒญ : JSON ์์ฒญ HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ ๊ฐ์ฒด
- @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) {
//...
} };
}
'๐จโ๐ป Web Development > Spirng - MVC ํจํด l' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] 7.์คํ๋ง MVC - ์น ํ์ด์ง ๋ง๋ค๊ธฐ (0) | 2023.03.18 |
---|---|
[Spring] 5.์คํ๋ง MVC - ๊ตฌ์กฐ ์ดํด (0) | 2023.03.14 |
[Spring] 4.MVC ํ๋ ์์ํฌ ๋ง๋ค๊ธฐ (0) | 2023.03.13 |
[Spring] 3.์๋ธ๋ฆฟ, JSP, MVC ํจํด (0) | 2023.03.13 |
[Spring] 2.์๋ธ๋ฆฟ (0) | 2023.03.12 |
์ต๊ทผ๋๊ธ