HTTP 요청
헤더 조회
- MultiValueMap은 하나의 키에 여러 값을 받을 수 있기 때문에 HTTP 헤더, HTTP 쿼리 파라미터와 같이 하나의 키에 여러 값을 받을 때 사용한다.
@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";
}
}
요청 파라미터 조회 (쿼리 파라미터, HTML Form)
- GET 쿼리 파라미터 전송 방식과 POST HTML Form 방식은 형식이 같으므로 구분없이 조회할 수 있다.
@RequestParam
- 스프링이 제공하는 @RequestParam을 사용하면 파라미터 이름으로 바인딩해주기 때문에 요청 파라미터를 편리하게 사용할 수 있다.
- required 속성으로 파라미터 필수 여부를 지정할 수 있으며, 파라미터 이름만 있고 값이 없는 경우는 빈 문자로 통과한다.
- 만약 required 속성이 false로 지정된 경우 값이 없으면 null로 넘어오는데 기본형에는 null을 입력할 수 없으므로, defaultValue를 사용해서 기본 값을 적용하거나 참조형 변술르 사용해야 한다.
- 참고로 defaultValue는 빈 문자의 경우에도 설정한 기본 값이 적용된다.
/**
* @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 사용 - 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.required
* /request-param -> username이 없으므로 예외
*
* 주의!
* /request-param?username= -> 빈문자로 통과
*
* 주의!
* /request-param
* 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";
}
/**
* @RequestParam
* - defaultValue 사용
*
* 참고: defaultValue는 빈 문자의 경우에도 적용
* /request-param?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";
}
/**
* @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";
}
@ModelAttribute
- 요청 파라미터를 받아서 필요한 객체를 만들고, 그 객체에 값을 넣는 과정을 자동화해준다.
- 스프링 MVC는 @ModelAttribute이 있으면 객체를 생성하고, 요청 파라미터의 이름으로 객체의 프로퍼티를 찾은 다음, 해당 프로퍼티의 setter를 호출해서 파라미터 값을 바인딩한다.
/**
* @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";
}
/**
* @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";
}
요청 메시지 조회
단순 텍스트
- 요청 파라미터와 다르게 HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우에는 @RequestParam 이나 @ModelAttribute를 사용할 수 없다.
- 대신 HTTP 메시지 바디의 데이터는 InputStream을 사용해서 직접 읽는다.
InputStream, Writer
- InputStream: HTTP 요청 메시지 바디의 내용을 직접 조회
- 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");
}
RequestEntity, ResponseEntity
- HttpEntity는 Http 헤더, 바디 정보를 편리하게 조회할 수 있으며, 헤더 정보를 포함해서 메시지 바디 정보를 뷰를 사용하지 않고 직접 반환할 수 있는 기능을 제공한다.
- RequestEntity와 ResponseEntity는 HttpEntity를 상속받아 추가 기능을 제공한다.
- RequestEntity: 요청에서 HttpMethod, url 정보를 조회할 수 있다.
- ResponseEntity: 응답에 HTTP 상태 코드를 설정할 수 있다.
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(RequestEntity<String> httpEntity) throws IOException {
String messageBody = httpEntity.getBody();
log.info("messageBody={}", messageBody);
return new ResponseEntity<String>("ok", HttpStatus.CREATED);
}
@RequestBody, @ResponseBody
- @RequestBody: HTTP 메시지 바디 정보를 편리하게 조회할 수 있으며, 헤더 정보가 필요하면 HttpEntity나 @RequestHeader를 사용해야 한다.
- @ResponseBody: 응답 결과를 HTTP 메시지 바디에 직접 전달한다. (뷰를 사용하지 않는다.)
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) throws IOException {
log.info("messageBody={}", messageBody);
return "ok";
}
JSON
HttpServletRequest, @RequestBody
- 직접 HTTP 메시지 바디에서 데이터를 읽어와 문자로 변환하고, 문자로 된 JSON 데이터를 Jackson 라이브러리인 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");
}
@ResponseBody
@PostMapping("/request-body-json-v2")
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {
log.info("messageBody={}", messageBody);
HelloData data = objectMapper.readValue(messageBody, HelloData.class);
log.info("username={}, age={}", data.getUsername(), data.getAge());
return "ok";
}
@RequestBody, HttpEntity
- HTTP 메시지 컨버터가 메시지 바디의 내용을 우리가 원하는 문자나 객체 등으로 변환해준다.
- 문자뿐만 아니라 JSON도 객체로 변환 가능
@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData data) throws IOException {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return "ok";
}
@ResponseBody
@PostMapping("/request-body-json-v4")
public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {
HelloData data = httpEntity.getBody();
log.info("username={}, age={}", data.getUsername(), data.getAge());
return "ok";
}
/**
* @RequestBody 생략 불가능(@ModelAttribute 가 적용되어 버림)
* HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter (contenttype: application/json)
*
* @ResponseBody 적용
* - 메시지 바디 정보 직접 반환(view 조회X)
* - HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter 적용
(Accept: application/json)
*/
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return data;
}
※ @RequestBody는 생략 불가능
- 스프링은 @RequestParam이나 @ModelAttribute 생략 시 String, int, Integer 같은 단순 타입은 @RequestParam으로, 나머지 타입은 @ModelAttribute로 처리한다.
- 따라서 객체를 저장할 때 @RequestBody를 생략하면 @ModelAttribute가 적용되어 HTTP 메시지 바디가 아니라 요청 파라미터를 처리하게 된다.
HTTP 응답
정적 리소스, 뷰 템플릿
- 정적 리소스란 웹 브라우저에 정적인 HTML, CSS, JS를 제공할 때 사용하며, 해당 파일을 변경없이 그대로 서비스한다.
- src/main/resources/static 디렉터리에 리소스를 넣어두면 스프링 부트가 정적 리소스로 서비스를 제공한다.
- 뷰 템플릿은 일반적으로 웹 브라우저에 동적인 HTML을 제공할 때 사용하며, 뷰 템플릿을 거쳐서 HTML이 생성되고 뷰가 응답을 만들어서 전달한다.
- 기본 뷰 템플릿 경로는 src/main/resources/templates
String 반환
- @ResponseBody가 없으면 뷰 리졸버가 실행되어 뷰를 찾고 렌더링한다.
- @ResponseBody가 있으면 뷰 리졸버를 실행하지 않고, HTTP 메시지 바디에 직접 입력한다.
@RequestMapping("/response-view-v1")
public ModelAndView responseViewV1() {
ModelAndView mav = new ModelAndView("response/hello")
.addObject("data", "hello!");
return mav;
}
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model) {
model.addAttribute("data", "hello!!");
return "response/hello";
}
HTTP API, 메시지 바디에 직접 입력
- HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 하므로, HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 전송한다.
String 반환 방법
- HttpServletResponse를 통해서 HTTP 메시지 바디에 직접 응답 메시지를 전달
- ResponseEntity 사용
- @ResponseBody를 사용하면 뷰를 사용하지 않고, HTTP 메시지 컨버터를 통해서 HTTP 메시지를 직접 입력
@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() {
return new ResponseEntity<>("ok", HttpStatus.OK);
}
@ResponseBody
@GetMapping("/response-body-string-v3")
public String responseBodyV3() {
return "ok";
}
JSON 반환 방법
- ResponseEntity를 반환하면 HTTP 메시지 컨버터를 통해서 JSON 형식으로 변환되어 반환
- @ResponseBody (응답 코드 설정은 불가능)
- @RestController를 사용하면 해당 컨트롤러에 모두 @ResponseBody가 적용 (REST API를 만들 때 사용)
@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;
}
'Spring > Spring MVC' 카테고리의 다른 글
뷰 컨트롤러 (0) | 2022.03.27 |
---|---|
서블릿, JSP, MVC 패턴 (0) | 2022.03.03 |
@Valid 유효성 검사 (0) | 2022.01.26 |
스프링 MVC 구조 (0) | 2022.01.23 |