6. 스프링 MVC 기본 기능
@RequestMapping(value = "/mapping/v1, method = RequestMethod.GET)
public String mappingV1(){
...
}
@GetMapping(value = "/mapping/v1") //Get, Post, Put, Delete, Patch
public String mappingV1(){
...
}
@GetMapping(value = "/mapping/v1/{userId}") //Get, Post, Put, Delete, Patch
public String mappingV1(@PathVariable("userId") String data){
...
}
@PathVariable
어노테이션을 이용해 값을 읽을 수 있다.("userId")
를 생략하면 변수명과 비교해 값을 저장한다. 이 경우는 data
.@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable String userId, @PathVariable Long
orderId) {
log.info("mappingPath userId={}, orderId={}", userId, orderId);
return "ok";
}
다양한 조건을 걸어 매핑을 할 수도 있다.
@GetMapping(value = "/mapping-param", params = "mode=debug")
파라미터 조건 매핑은 잘 사용하지 않는 편이다.
@GetMapping(value = "/mapping-param", headers = "mode=debug")
@PostMapping(value = "/mapping-consume", consumes = "application/json")
@PostMapping(value = "/mapping-produce", produces = "text/html")
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
)
@RequestHeader MultiValueMap<String, String> headerMap,
: 모든 header 정보를 MultiValueMap 형식으로 조회한다.key
에 여러개의 값을 받을 수 있다. value
가 List
@CookieValue(value = "myCookie", required = false) String cookie
: 특정 쿠키를 조회한다.required
defaultValue
public String requestParamV2(
@RequestParam("username") String memberName,
@RequestParam("age") int memberAge) {
...
}
기존 서블릿에서는 request.getXXX 후 타입변환 및 변수에 저장하는 방식이었는데,
Spring은 이 과정을 자동화해준다.
@RequestParam(name="xx")
생략 가능 -> 생략시 required = false
적용String
, int
, Integer
등의 단순 타입이면 @RequestParam
도 생략 가능너무 많이 생략하는 것은 과할 수 있다. 적당히 생략하여 코드를 명확하게.
public String requestParamDefault(
@RequestParam(required = true, defaultValue = "guest") String username,
@RequestParam(required = false, defaultValue = "-1") int age) {
...
}
required = false
이고 값이 들어오지 않으면 null
값이 저장된다.
-> 기본형(ex int
) 에는 null
값이 저장 불가능하므로, Integer
를 사용하거나 기본값을 지정해준다.
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
log.info("username={}, age={}", paramMap.get("username"),
paramMap.get("age"));
return "ok";
}
Map
자료구조를 이용해 Parameter를 받을 수 있다.
Map
, 아니면 MultiValueMap
을 사용일반적으로는 요청 파라미터를 받아서, 필요한 객체에 값을 넣어 사용한다.
하지만 스프링은 이 과정을 자동화해주는 @ModelAttribute
기능을 제공한다.
이것도 어댑터의 효능이지 않을까
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
...
}
이렇게 @ModelAttribute
를 클래스명 앞에 붙이면, 해당 프로퍼티의 setter
를 호출하여, 값을 알아서 주입해준다.
프로퍼티
객체에 getUsername() , setUsername() 메서드가 있으면, 이 객체는 username 이라는 프로퍼티를 가지고 있다.
username 프로퍼티의 값을 변경하면 setUsername() 이 호출되고, 조회하면 getUsername() 이호출된다.
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {
String messageBody = httpEntity.getBody();
log.info("messageBody={}", messageBody);
return new HttpEntity<>("ok");
}
Spring MVC는 HttpEntity
를 지원한다.
HttpEntity
: HTTP header, body 정보를 편리하게 조회HttpEntity
를 상속받은 RequestEntity
, ResponseEntity
기능도 제공된다.public String requestBodyStringV4(@RequestBody String messageBody) {
...
}
결론 .
요청 파라미터, html요청 조회 ->@RequestParam
,@ModelAttribute
Http Body 메시지 직접 조회 ->@RequestBody
기존 서블릿에서 했던 대로라면, messagebody
를 읽어온 후, objectMapper
를 통해 자바 객체로 변환했다.
하지만 Spring은 이것도 자동으로 해준다!
public String requestBodyJsonV3(@RequestBody HelloData data) {
...
}
public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {
HelloData data = httpEntity.getBody();
}
정적 리소스
경로 : src/main/resources/static
src/main/resources/static/basic/hello-form.html
->
http://localhost:8080/basic/hello-form.html
해당 경로로 요청시, 정적 리소스를 제공한다.
뷰 템플릿
경로 :src/main.resources/templates
기본적으로 Controller에서 뷰의 논리 이름을 반환하면, Model에 담긴 데이터를 이용하여 템플릿엔진이 뷰를 렌더링 하고 반환한다.
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model) {
model.addAttribute("data", "hello!!");
return "response/hello";
}
controller에서 반환타입을 void로 해도, 요청 url을 참고하여 뷰를 찾지만 권장하지 않는다.
application properties
에서 prefix
, suffix
를 설정할 수 있다. 기본적으로는 다음과 같이 설정돼있다.
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
@ResponseBody
어노테이션이 붙은 경우, 직접 Http 메시지 바디에 데이터를 담아서 전송한다.
1. HttpEntity - ResponseEntity
응답코드를 동적으로 설정할 수 있다.
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;
}
이 경우에도 @ResponseStatus
를 이용해 Status를 설정할 수 있지만, 고정적이라는 한계가 있다.
Controller
대신RestController
어노테이션이 붙은 경우는 ResponseBody
가 기본적으로 적용된다.
스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용한다.
@RequestBody
, HttpEntity(RequestEntity)
@ResponseBody
,HttpEntity(ResponseEntity)
메시지 컨버터는 대상 클래스 타입과 Http 미디어 타입을 체크해서 둘 다 만족하는 경우 사용여부를 결정한다.
1. ByteArrayHttpMessageConverter : byte[]
데이터를 처리한다.
클래스 타입: byte[]
, 미디어타입 : */*
,
2. StringHttpMessageConverter : String
문자로 데이터를 처리한다.
클래스 타입: String
, 미디어타입 : */*
3. MappingJackson2HttpMessageConverter : application/json
클래스 타입: 객체 또는 HashMap
, 미디어타입 : application/json
관련
Spring을 이용하면, Controller
에서 다양한 매개변수를 편리하게 받아 사용할 수 있는데, 이것을 가능하게 하는 것이 Argument Resolver
다.
그리고 이 Argument Resolver
에서 HttpMessageConverter
를 호출하여 사용한다.
일단은 어노테이션 기반 Handler Adapter가 Argument Resolver를 사용한다고 이해했는데, 뭔가 내가 이해하고 있던 Adapter의 역할을 Arguement Resolver가 한다고 해서 조금 혼란스럽지만, 결국 둘이 같이 잘 해결하는 거니까 일단은 넘어간다..
스프링이 필요한 대부분의 기능을 제공하기 때문에 실제 기능을 확장할 일이 많지는 않다. 기능 확장은
WebMvcConfigurer 를 상속 받아서 스프링 빈으로 등록하면 된다. 실제 자주 사용하지는 않으니 실제 기능확장이 필요할 때 WebMvcConfigurer 를 검색해보자.