- [Spring] 스프링 MVC 1편 6) 스프링 MVC 기본 기능(2)2023년 11월 08일 09시 43분 37초에 업로드 된 글입니다.작성자: 민발자728x90
Session 6 스프링 MVC 기본 기능
8. HTTP 요청 파라미터 - @ModelAttribute
1) @ModelAttribute
요청 파라미터를 받아서 필요한 객체를 만들고 객체에 값을 넣어주어야 한다
스프링 mvc는 이 과정을 자동화해주는 @ModelAttribute 기능을 제공한다.
2) HelloData
@Data public class HelloData { private String username; private int age; }
롬복 @Data 사용하면 @Getter, @Setter, @ToStirng, @EqualsAndHashCode, @RequiredArgsConstructor를 자동으로 적용
3) modelAttributeV1
public String modelAttributeV1(@ModelAttribute HelloData helloData) { ... }
HelloData 객체가 생성되고 요청 파라미터 값도 모두 들어가 있다.
4) @ModelAttribute 동작
HelloData 객체 생성
요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다.
해당 프로퍼티의 setter를 호출해서 파라미터 값을 입력(바인딩)한다.
5) 프로퍼티
객체에 getUsername(), setUsername() 메서드가 있으면 이 객체는 username이라는 프로퍼티를 가지고 있다.
username 프로퍼티 값을 변경하면 setUsername()이 호출되고, 조회하면 getUsername()이 호출된다.
6) 바인딩 오류
숫자가 들어가야 할 곳에 문자를 넣으면 BindException 발생
7) @ModelAttribute 생략 - modelAttributeV2
public String modelAttributeV2(HelloData helloData) { ... }
@RequestParam도 생략가능하니 혼란 발생할 수 있다.
String, int, Integer 같은 단순 타입 ▶ @RequestParam
argument resolver로 지정해 둔 타입 외 나머지 ▶ @ModelAttribute
9. HTTP 요청 메시지 - 단순 텍스트
1) HTTP 메시지 바디에 데이터를 직접 담아서 요청
주로 json 사용
요청 파라미터와 다르게 @RequestParam, @ModelAttribute 사용 불가(HTML Form 형식으로 전달되는 경우 요청 파라미터로 인정)
▶ InputStream을 사용해 직접 읽을 수 있다.
2) Input, Output 스트림, Reader
/** * 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"); }
InputStream(Reader) : HTTP 요청 메시지 바디의 내용을 직접 조회
OutputStream(Writer) : HTTP 응답 메시지 바디에 직접 결과 출력
3) HttpEntity
/** * 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) throws IOException { String messageBody = httpEntity.getBody(); log.info("messageBody={}", messageBody); return new HttpEntity<>("ok"); }
HttpEntity : HTTP header, body 정보를 편리하게 조회, 메시지 바디 정보를 직접 조회, 요청 파라미터를 조회하는 기능과 관계없음
HttpEntity는 응답에서도 사용가능, 메시지 바디 정보 직접 반환, 헤더 정보 포함 가능, view 조회 X
- HttpEntity를 상속받은 다음 객체들도 같은 기능 제공
RequestEntity : HttpMethod, url 정보 추가, 요청에서 사용
ResponseEntity : HTTP 상태코드 설정 가능, 응답에서 사용
4) @RequestBody
/** * @RequestBody * - 메시지 바디 정보를 직접 조회(@RequestParam X, @ModelAttribute X) * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용 * @ResponseBody * - 메시지 바디 정보 직접 반환(view 조회X) * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용 */ public HttpEntity<String> requestBodyStringV4(@RequestBody String messageBody){ ... }
실무에서 많이 사용
@RequestBody를 사용하면 HTTP 메시지 바디 정보를 편리하게 조회할 수 있다.
헤더 정보가 필요하다면 HttpEntity를 사용하거나 @RequestHeader를 사용하면 된다.
요청 파라미터 조회와 전혀 관계없다!!
5) 요청 파라미터 vs HTTP 메시지 바디
요청 파라미터를 조회하는 기능 : @RequestParam, @ModelAttribute
HTTP 미시지 바디를 직접 조회하는 기능 : @RequestBody
6) @ResponseBody
응답 결과를 HTTP 메시지 바디에 직접 담아 전달, view 사용하지 않음
10. HTTP 요청 메시지 - json
1) @RequestBody 문자 변환
@ResponseBody @PostMapping("/request-body-json-v2") public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException { log.info("messageBody={}", messageBody); HelloData helloData = objectMapper.readValue(messageBody, HelloData.class); log.info("username={}, age={}", helloData.getUsername(), helloData.getAge()); return "ok"; }
@RequestBody를 사용해서 HTTP 메시지에 데이터를 꺼내고 messageBody에 저장
문자로 된 json 데이터인 messageBody를 ObjectMapper를 통해 자바 객체로 변환
2) @RequestBody 객체 변환
/** * @RequestBody 생략 불가능(@ModelAttribute 가 적용되어 버림) * HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter (content-type: application/json) */ @ResponseBody @PostMapping("/request-body-json-v3") public String requestBodyJsonV3(@RequestBody HelloData helloData) { log.info("username={}, age={}", helloData.getUsername(), helloData.getAge()); return "ok"; }
@RequestBody에 직접 만든 객체를 지정할 수 있다.
HttpEntity, @RequestBody를 사용하면 HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 문자나 객체 등으로 변환해 줌, json도 객체로 변환해 줌
이때 @RequestBody는 생략 불가능 ▶ @ModelAttribute가 적용됨
HTTP 요청 시에 content-type: application/json인지 꼭 확인! 그래야 json을 처리할 수 있는 HTTP 메시지 컨버터가 실행
3) HttpEntity 사용
@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"; }
4) json 응답
/** * @RequestBody 생략 불가능(@ModelAttribute 가 적용되어 버림) * HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter (content-type: 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; }
@ResponseBody 응답의 경우 해당 객체를 HTTP 메시지 바디에 직접 넣어줄 수 있다.
- 동작
@RequestBody 요청 json 요청 → HTTP 메시지 컨버터 → 객체
@ResponseBody 응답 객체 → HTTP 메시지 컨버터 → json 응답
11. 응답 - 정적 리소스, 뷰 템플릿
1) 스프링에서 응답 데이터를 만드는 방법 3가지
- 정적 리소스
웹 브라우저에 정적인 HTML, CSS, js을 제공할 때는 정적 리소스 사용
- 뷰 템플릿 사용
웹 브라우저에 동적인 HTML을 제공할 때는 뷰 템플릿 사용
- HTTP 메시지 사용
HTTP API를 제공하는 경우에는 HTML이 아닌 데이터를 전달, HTTP 메시지 바디에 json 형식으로 데이터를 실어 전송
2) 정적 리소스
스프링 부트는 /static, /public, /resources, /META-INF/resources 디렉터리에 있는 정적 리소스 제공
파일을 변경 없이 그대로 그대로 서비스
3) 뷰 템플릿
뷰 템플릿을 거쳐 HTML이 생성되고 뷰가 응답을 만들어 전달
일반적으로 HTML을 동적으로 생성하는 용도로 사용
스프링 부트는 기본 뷰 템플릿 경로를 제공 src/main/resources/templates
-Contorller + ModelAndView 반환
@RequestMapping("/response-view-v1") public ModelAndView responseViewV1() { ModelAndView mav = new ModelAndView("response/hello").addObject("data", "hello!!!!"); return mav; }
-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>
렌더링 될 때 data를 전달받은 값으로 바꿔서 렌더링
- String 반환
@RequestMapping("/response-view-v2") public String responseViewV2(Model model) { model.addAttribute("data", "hello!!!!"); return "response/hello"; }
반환된 문자열은 뷰의 논리이름
@ResponseBody 사용하면 뷰 템플릿이 렌더링 해주는 것이 아니라 메시지 바디에 그래로 문자열 전달
@ResponseBody가 없으면 뷰 리졸버 실행해 뷰를 찾고 렌더링
- void 반환
@RequestMapping("/response/hello") public void responseViewV3(Model model) { model.addAttribute("data", "hello!!!!"); }
컨트롤러를 사용하고 HttpServletResponse, OutputStream 같은 HTTP 메시지 바디를 처리하는 파라미터가 없으면 요청 온 컨트롤러의 경로를 논리 뷰 이름으로 사용 /response/hello → response/hello
명시성이 떨어지고 요청 경로와 뷰 이름이 동일한 경우도 많이 없어서 권장하지 않음
4) thymeleaf 설정
thymeleaf 라이브러리를 추가하면 스프링 부트가 자동으로 ThymeleafViewResolver와 필요한 스프링 빈들을 등록해주고 설정 정보도 자동으로 설정해 줌
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
12. HTTP 응답 - HTTP API, 메시지 바디에 직접 입력
1) responseBodyV1
HttpServletResponse 객체를 통해서 HTTP 메시지 바디에 직접 응답 메시지 전달
response.getWriter().write("ok")
2) responseBodyV2
ResponseEntity는 HTTP 메시지 헤더, 바디, HTTP 응답 코드 설정 가능
뷰를 사용하지 않고 HTTP 컨버터 통해 메시지를 직접 입력
return new ResponseEntity<>("ok", HttpStatus.OK);
3) responseBodyV3
@ResponseBody 애노테이션을 사용하면 뷰를 사용하지 않고 HTTP 컨버터 통해 메시지를 직접 입력 가능
return "ok";
4) responseBodyJsonV1
ResponseEntity를 반환 HTTP 메시지 컨버터를 통해서 JSON 형식으로 변환되어 반환
return new ResponseEntity<>(helloData, HttpStatus.OK);
5) responseBodyJsonV2
@ResponseBody 애노테이션을 사용해서 HTTP 컨버터 통해 JSON 형식으로 변환되어 반환
응답코드는 애노테이션 사용해 설정 가능 @ResponseStatus(HttpStatus.OK)
6) @RestController
@ResponseBody 클래스 레벨에 두면 모든 컨트롤러에 적용
뷰 템플릿을 사용하는 것이 아니라, HTTP 메시지 바디에 직접 데이터를 입력 Rest API 만들 때 사용하는 컨트롤러
@ResponseBody + @Contorller = @RestController
13. HTTP 메시지 컨버터
1) @ResponseBody 사용 원리
클라이언트 요청
컨트롤러 호출 @ResponseBody를 사용
HttpMessageConverter가 동작
기본 문자 → StringHttpMessageConverter
기본 객체 → MappingJackson2HttpMessageConverter
HTTP 바디에 문자 내용을 직접 반환
- 참고 : 응답의 경우 클라이언트의 HTTp Accept 헤더와 서버의 컨트롤러 반환 타입 정보를 조합해 HttpMessageConverter가 선택
2) 스프링 MVC HttpMessageConverter 적용
HTTP 요청 : @RequestBody, HttpEntity(RequestEntity)
HTTP 응답 : @ResponseBody, HttpEntity(ResponsetEntity)
3) 주요한 메시지 컨버터
대상 클래스 타입과 미디어 타입을 체크해서 사용 여부를 결정
4) HTTP 요청 데이터 읽기
HTTP 요청이 오고 컨트롤러에서 @RequestBody, HttpEntity 파라미터를 사용
메시지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 canRead() 호출
대상 클래스 타입 지원하는지, Content-Type 미디어 타입을 지원하는지 조건을 만족하면 read()를 호출해서 객체를 생성하고 반환
5) HTTP 응답 데이터 생성
컨트롤러에서 @ResponseBody, HttpEntity로 값이 반환된다.
메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 canWriter() 호출
대상 클래스 타입 지원하는지, Content-Type 미디어 타입을 지원하는지 조건을 만족하면 write() 호출해서 HTTP 응답 메시지 바디에 데이터 생성
14. 요청 매핑 핸들러 어댑터 구조
1) 스프링 mvc 구조
RequestMappingHandlerAdapter 핸들러 어댑터와 HTTP 메시지 컨버터 연관
2) RequestMappingHandlerAdapter 동작 방식
RequestMapping 핸들러 어댑터 호출
ArgumentResolver가 컨트롤러의 파라미터, 애노테이션 정보를 기반으로 전달 데이터를 생성해 컨트롤러로 넘겨줌
컨트롤러 호출
ReturnValueHandler가 컨트롤러의 반환 값을 변환
3) ArgumentResolver
애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있는데 이것을 유연하게 처리해주는 것이 ArgumentResolver
ArgumentResolver가 컨트롤러의 파라미터, 애노테이션 정보를 기반으로 전달 데이터를 생성하고 파라미터 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨줌
ArgumentResolver supportParameter()를 호출해서 해당 파라미터를 지원하는지 체크, 지원하면 resolveArgument() 호출해서 실제 객체를 생성, 생성된 객체가 컨트롤러 호출 시 넘어가는 것
4) ReturnValueHandler
응답 값을 변환하고 처리함컨트롤러에서 String, ModelAndView로 반환할 때 ReturnValueHandler가 응답 값을 변환해 줌
5) HTTP 메시지 컨버터 위치
HTTP 메시지 컨버터를 사용하는 @RequestBody도 컨트롤러가 필요로 하는 파라미터 값에 사용된다.
@ResponseBody의 경우에도 컨트롤러 반환 값을 이용
요청의 경우 @RequestBody, HttpEntity를 처리하는 ArgumentResolver가 있음 ArgumentResolver들이 HTTP 메시지 컨버터를 사용해서 필요한 객체를 생성하는 것
응답의 경우 @ResponseBody, HttpEntity를 처리하는 ReturnValueHandler가 있음, 여기에서 HTTP 메시지 컨버터를 호출해서 응답 결과를 만듦
728x90'정리 > Spring' 카테고리의 다른 글
[Spring] 스프링 MVC 1편 7) 스프링 MVC 웹 페이지 만들기(2) (2) 2023.11.10 [Spring] 스프링 MVC 1편 7) 스프링 MVC 웹 페이지 만들기(1) (0) 2023.11.09 [Spring] 스프링 MVC 1편 6) 스프링 MVC 기본 기능(1) (0) 2023.11.07 [Spring] 스프링 MVC 1편 5) 스프링 MVC 구조이해 (0) 2023.11.06 [Spring] 스프링 MVC 1편 4) MVC 프레임워크 만들기 (0) 2023.11.03 다음글이 없습니다.이전글이 없습니다.댓글