요청 데이터 전달하는 3가지 방법
- GET - URL에 쿼리 파라미터로 전달
- POST - HTML Form에 쿼리 파라미터 형식으로 전달
- HTTP message body에 데이터를 직접 담아서 요청
HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form
@Slf4j
@Controller
public class RequestParamController {
@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);
#반환 타입이 없으면서 이렇게 응답에 값을 직접 집어넣으면, view 조회X
response.getWriter().write("ok");
}
}
- 단순히 HttpServletRequest가 제공하는 방식으로 요청 파라미터를 조회했다.
- 요청 파라미터: GET방식의 쿼리 스트링, HTML Form의 POST 방식
HTTP 요청 파라미터 - @RequestParam
#localhost:8080/request-param?username=khm&age=23
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(@RequestParam("username") String memberName, @RequestParam("age") int memberAge) {
log.info("username={}, age={}", memberName, memberAge);
return "ok";
}
@ResponseBody
@RequestMapping("/request-param-v3")
public String requestParamV3(@RequestParam String username, @RequestParam int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
@ResponseBody
@RequestMapping("/request-param-v4")
public String requestParamV4(String username, int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
}
- requestParamV2
- @RequestParam : 파라미터 이름으로 바인딩
- @ResponseBody : View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력
- requestParamV3
- HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx") 생략 가능
- requestParamV4
- String , int , Integer 등의 단순 타입이면 @RequestParam 도 생략 가능
- @RequestParam 애노테이션을 생략하면 스프링 MVC는 내부에서 required=false를 적용한다.
파라미터 필수 여부
@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";
}
@RequestParam의 required
- 파라미터 필수 여부
- 기본값이 파라미터 필수( true )이다. => 값이 있어야 한다.
- @RequestParam(required = false) => 값이 없어도 된다.
- '/request-param-required'로 요청 시 => username 이 없으므로 400 예외가 발생한다.
- '/request-param-required? username='로 요청 시 => 파라미터 이름만 있고 값이 없는 경우 => 빈문자("")로 통과
주의! - 기본형(primitive)에 null 입력
@RequestParam(required = false) int age 일 때, '/request-param-required?username=' 로 요청 시
null을 int에 입력하는 것은 불가능하므로 500 예외가 발생한다.
따라서 Integer로 변경하거나 defaultValue를 사용해야 한다. @RequestParam(required = false, defaultValue = "-1") int age / @RequestParam(required = false) Integer age
defaultValue를 사용하여 기본값을 설정하면 required는 아무 의미가 없어진다. (값이 있든 없든 무조건 입력되니까)
defaultValue는 빈 문자의 경우에도 설정한 기본 값이 적용된다.
파라미터를 Map으로 조회하기 - requestParamMap
@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";
}
파라미터를 Map, MultiValueMap으로 조회할 수 있다.
- @RequestParam Map : Map(key=value)
- 하나의 key에 1개의 value
- @RequestParam MultiValueMap : MultiValueMap(key=[value1, value2, ...] )
- 하나의 key에 다수의 value
- (key=userIds, value=[id1, id2])
HTTP 요청 파라미터 - @ModelAttribute
HelloData (요청 파라미터를 바인딩 받을 객체)
import lombok.Data;
@Data
public class HelloData {
private String username;
private int age;
}
@Data
@Getter , @Setter , @ToString , @EqualsAndHashCode , @RequiredArgsConstructor를 자동으로 적용해준다.
@ModelAttribute 적용 - modelAttributeV1
#localhost:8080/request-param?username=khm&age=23
//model.addAttribute(helloData) 코드도 함께 자동 적용됨
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(),helloData.getAge());
return "ok";
}
- 스프링MVC는 @ModelAttribute 가 있으면 다음을 실행한다.
- HelloData 객체를 생성한다.
- 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력(바인딩) 한다.
- 예) 파라미터 이름이 username 이면 setUsername() 메서드를 찾아서 호출하면서 값을 입력한다.
- 마법처럼 HelloData 객체가 생성되고, 요청 파라미터의 값도 모두 들어간다.
프로퍼티
객체에 getUsername() , setUsername() 메서드가 있으면, 이 객체는 username이라는 프로퍼티를 가지고 있다. username 프로퍼티의 값을 변경하면 setUsername() 이 호출되고, 조회하면 getUsername() 이 호출된다.
바인딩 오류
age=abc 처럼 숫자가 들어가야 할 곳에 문자를 넣으면 BindException 이 발생한다.
@ModelAttribute 생략 - modelAttributeV2
@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
- @ModelAttribute는 생략할 수 있다. 그런데 @RequestParam 도 생략할 수 있으니 혼란이 발생할 수 있다.
스프링은 해당 생략시 다음과 같은 규칙을 적용한다. 요청 파라미터를 받는 인자가
- String , int , Integer 같은 단순 타입이면 @RequestParam
- 나머지는 @ModelAttribute (argument resolver로 지정해둔 타입 외)
@ModelAttribute - Model 추가
@ModelAttribute는 중요한 한가지 기능이 더 있는데, 바로 모델(Model)에 @ModelAttribute 로 지정한 객체를 자동으로 넣어준다.
모델에 데이터를 담을 때는 이름이 필요하다. 이름은 @ModelAttribute 에 지정한 name(value) 속성을 사용한다.
@ModelAttribute의 이름을 생략하면 모델에 저장될 때 클래스명을 사용한다. 이때 클래스의 첫글자만 소문자로 변경해서 등록한다.
Ex)
@ModelAttribute("hello") Item item => model.addAttribute("hello", item); 자동 실행
@ModelAttribute Item item1 => model.addAttribute("item", item1); 자동 실행
HTTP 요청 메시지 - 단순 텍스트
HTTP message body에 데이터를 직접 담아서 요청
- HTTP API에서 주로 사용, JSON, XML, TEXT
- 데이터 형식은 주로 JSON 사용
- POST, PUT, PATCH
요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 데이터가 넘어오는 경우는 @RequestParam, @ModelAttribute를 사용할 수 없다. (물론 HTML Form 형식으로 전달되는 경우는 요청 파라미터로 인정된다.)
requestBodyStringV4
@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
HTTP 요청 메시지 - JSON
requestBodyJsonV3
@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData data) {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return "ok";
}
- @RequestBody를 사용하면 HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 우리가 원하는 문자나 객체 등으로 변환해준다.
@RequestBody는 생략 불가능
위 코드의 경우 HelloData에 @RequestBody를 생략하면 스프링의 규칙에 의해 @ModelAttribute 가 적용되어버린다. HelloData data @ModelAttribute HelloData data 따라서 생략하면 HTTP 메시지 바디가 아니라 요청 파라미터를 처리하게 된다.
주의
HTTP 요청시에 content-type이 application/json인지 꼭! 확인해야 한다. 그래야 JSON을 처리할 수 있는 HTTP 메시지 컨버터가 실행된다.
requestBodyJsonV5
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return data;
}
- 응답의 경우에도 @ResponseBody 를 사용하면 해당 객체를 HTTP 메시지 바디에 직접 넣어줄 수 있다. Json 형식 그대로 화면에 출력.
Json 요청, Json 응답 일 경우
@RequestBody 요청
JSON 요청 => HTTP 메시지 컨버터 => 객체
@ResponseBody 응답
객체 => HTTP 메시지 컨버터 => JSON 응답
'스프링' 카테고리의 다른 글
[스프링 MVC 1편] PRG Post/Redirect/Get, RedirectAttributes (0) | 2022.06.30 |
---|---|
[스프링 MVC 1편] 스프링 MVC - 기본 기능 (3) (0) | 2022.06.29 |
[스프링 MVC 1편] 스프링 MVC - 기본 기능 (1) (0) | 2022.06.23 |
[스프링 핵심 원리] 빈 스코프 (2) (0) | 2022.06.22 |
[스프링 핵심 원리] 빈 스코프 (1) (0) | 2022.06.22 |