월루를 꿈꾸는 대학생
[MVC-1] 스프링 MVC - 기본 기능 본문
Welcome 페이지
- Jar를 사용하면 /resources/static/index.html 위치를 기본 웰컴페이지로 인식함
로깅 간단히 알아보기
- 운영 시스템이는 println으로 정보를 출력하는 것이 아니라 별도 로깅라이브러리를 써서 로그를 남긴다
로깅 라이브러리
@RestController
public class LogTestController {
// 내 클래스를 지정
private final Logger log = LoggerFactory.getLogger(getClass());
@RequestMapping("/log-test")
public String logTest(){
String name = "Spring";
//보통 개발 할 때는 프린트로 로그를 남기면 수만 개 출력이 생기니까 안 됨
System.out.println("name = "+name);
// 로그 레벨을 정해서 볼수 있음
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";
// 그냥 @Controller를 썼다면 리턴값으로 논리 경로를 반환한다고 생각을 할텐데
// restController 는 리턴값이 뷰리졸버로 가는게 아니라 스트링 그 값 그대로 화면에 출력시킴 즉 반환됨
// RestAPI만들 때 중요
}
}
@RestController
- 그냥 @Controller는 반환값이 String인 경우 view 이름으로 인식하고 뷰 랜더링이 되는데 @RestController는 반환값이 HTTP Body에 바로 입력되어짐 -> rest api 개발용
- 시간 / 로그 레벨 / pid / 쓰레드명 / 클래스명 / 로그 메서지
- trace -> debug -> info -> warn -> error
- 멀티스레드에서 http요청오면 스레드풀에서 찾아서 처리함
application.properties
코드를 건들이지 않고 설정으로 로그 레벨 설정 가능
# 전체 로그 레벨 설정(기본 info) logging.level.root=info # hello.springmvc 패키지와 그 하위 로그 레벨 설정 logging.level.hello.springmvc=debug
@Slf4j
보다 간편하게 로그 사용 가능
@Slf4j @RestController public class LogTestController { Factory.getLogger(getClass()); @RequestMapping("/log-test") public String logTest(){ log.trace("trace log={}", name);
로그를 출력할 때는 + 연산에 주의해야함
log.debug("data={}", data)
로그 사용시 장점
- 단순 출력보다 더 많은 정보를 남길 수 있다 (쓰레드 정보, 클래스 이름 등등)
- 로그 레벨을 용도에 맞게 조절
- 실무에서는 꼭 로그를 사용하자 성능이 훨씬 좋음
요청 매핑
- 요청이 올 때 어떤 컨트롤러가 요청이 되어야 하는 가?
- url이외에 어떤 것이 있을까
@RestController
public class MappingController {
private Logger log = LoggerFactory.getLogger(getClass());
@RequestMapping({"/hello-basic","/hello-go"})
public String helloBasic() {
log.info("helloBasic");
return "ok";
}
@ReqeustMapping
- 해당 url이 요청되면 매핑된 메서드 호출
- 매핑은 {} 배열로 여러개 매핑 가능
- url요청시 끝에 '/'의 유무는 차이가 없음
@RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
@GetMapping(value = "/mapping-get-v2")
- 무조건 get방식의 맞는 url요청만 응답
PathVariable 경로 변수 사용
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable("userId") String userId, @PathVariable Long
orderId) {
log.info("mappingPath userId={}, orderId={}", userId, orderId);
return "ok";
}
- 최근에 자주 사용하는 방식
- @PathVariable 애노테이션 이용해서 요청에 있는 키-값을 가져와서 사용 가능
- @PathVariable 이름이랑 파라미터 이름 같으면 괄호 생략가능
특정 파라미터 조건 매핑
@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
log.info("mappingParam");
return "ok";
}
- url에 ?mode=debug 가 붙어있는 경우에 동작함
- 요즘 잘 안 쓰임
특정 헤더 조건 매핑
@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
log.info("mappingHeader");
return "ok";
}
- 해더 값에 mode=debug 값이 있는 경우에 동작
미디어 타입 조건 매핑 -HTTP 요청 Content-Type, consume
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
log.info("mappingConsumes");
return "ok";
}
- 미디어 타입이 json 인 경우에 동작
- 서버 입장에서 요청을 소비하니까 consumes 이라고 함 = 요청헤더의 컨텐트 타입
- 타입이 안 맞으면 415에러
미디어 타입 조건 매핑 -HTTP 요청 Accept , produce
@PostMapping(value = "/mapping-produce", produces = "text/html")
public String mappingProduces() {
log.info("mappingProduces");
return "ok";
}
- 요청의 Accept헤더 기반으로 미디어타입 매핑
- 타입이 안 맞으면 406에러
차이점
Content-type
- http 메세지에 정의된 데이터 형식
- 데이터를 싣어 보낼 때 해당 데이터가 어떤 타입인지 정의해서 보낸다 그래야 서버에서 별도 처리없이 아 json이구나 인식하고 데이터를 처리하는 것
Accept
- 클라이언트가 서버한테 요청할 때 쓰는 거
- 요청 메세지에 text/html이라고 쓴 거면 클라이언트는 해당 text/html 타입 아니면 화면에 출력하지 않겠다 그러니 꼭 text/html 타입으로 넘겨줘라고 서버에 요청을 하는것 지키든 말든 서버 마음이긴 함
참고
https://dololak.tistory.com/630
요청 매핑 - API 예시
- 회원 목록 조회: GET /users
- 회원 등록: POST /users
- 회원 조회: GET /users/{userId}
- 회원 수정: PATCH /users/{userId}
- 회원 삭제: DELETE /users/{userId}
- url은 같아도 방식으로 구분이 가능
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
@GetMapping("/{userId}")
public String findUser(@PathVariable String userId) {
return "get userId=" + userId;
}
@PatchMapping("/{userId}")
public String updateUser(@PathVariable String userId) {
return "update userId=" + userId;
}
}
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);
//메서드 조회 get , post
log.info("httpMethod={}", httpMethod);
// 위치 정보 : jp , kr
log.info("locale={}", locale);
//모든http 헤더를 맵형태로 만들어서 조회
log.info("headerMap={}", headerMap);
// 특정해더 조회
log.info("header host={}", host);
// 특정 쿠키 조회
log.info("myCookie={}", cookie);
return "ok";
}
}
- 애노테이션 기반 컨트롤러는 다양한 파라미터 받기 가능 정형화 된 거 아니니까
request=org.apache.catalina.connector.RequestFacade@5dc99ba7
response=org.apache.catalina.connector.ResponseFacade@425f4a2e
httpMethod=GET
locale=ko_KR
headerMap={host=[localhost:8080], connection=[keep-alive], cache-control=[max-age=0], sec-ch-ua=["Whale";v="3", "Not-A.Brand";v="8", "
header host=localhost:8080
myCookie=null
MultiValueMap
- map과 유사 / 하나의 키에 여러 값을 배열 행태로 받음
- 하나의 키에 여러 값 받을 때 사용 ex ) http header , 쿼리파라미터 등
- keyA=value1&keyA=value2
리퀘스트 파라미터 목록
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html
HTTP 요청 파라미터 - 쿼리파라미터 , html form
서블릿이 http요청 조회했던 방법을 스프링에서 어떻게 바뀌었는지 확인
http요청 메서지를 서버로 전달하는 방법 3가지
- GET - 쿼리 파라미터
- ?username=hello&age=20
- url에 직접 데이터를 넣어서 전달하는 방식
- Post - html form
- content-type: application/x-www-form-urlencoded
- 메세지 바디에 쿼리 파라미터 형식으로 전달 username=hello&age=20
- HTTP message body
- API에서 주로 사용 JSON
보통 이 3가지로 귀결되고 우리는 1,2 번을 알아보자
1,2 번은 방식은 다르지만 데이터를 보내는 형식은 같기에 같은 메서드로 요청에 있는 값을 꺼내서 확인할 수 있다
HttpServletRequest 의 request.getParameter()
이를 파라미터 조회 라고 칭함
@Slf4j
@Controller
public class ReqeustParamController {
@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");
}
}
void타입으로 반환인데 이런 경우 응답에 직접 갑을 넣으면 view조회 하지않고 해당 값을 그대로 클라이언트에게 반환시켜준다
response.getWriter().write("ok");
HTTP 요청 파라미터 - @RequestParam
- 스프링이 제공하는 애노테이션을 사용해서 편리하게 요청 데이터를 가져오자!
level1
@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 애노테이션 사용시 RestController랑 같은 의미 그냥 ok리턴하면 해당하는 물리적 위치 찾으려고 뷰리졸버가 찾는데 그걸 무시(view조회 무시)하고 문자열 그대로 반환시킴
- @RequestParam 속성을 파라미터 이름 처럼 사용 해당 괄호는 파라미터 이름을 쓰면 알아서 요청에서 가지고 와서 씀
level2
@ResponseBody
@RequestMapping("/request-param-v3")
public String requestParamV3(
@RequestParam String username,
@RequestParam int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
- 변수명과 파라미터 이름이 같으면 () 괄호 생략가능
level3
@ResponseBody
@RequestMapping("/request-param-v4")
public String requestParamV4(String username, int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
- 단순 타입인 경우 (int, String, Integer)는 @RequestParam마저 생략이 가능
- 다만 너무 없으면 좀 그러니 level2를 쓰도록 하자
- @RequestParam생략시 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";
}
- 디폴트는 true / true로 설정한 값이 요청에 없으면 400에러가 뜸
- 기본값이 설정되어 있지 않는 경우 빈문자열도 그대로 통과 시킴
- int로 설정한 파라미터에 요청 null(빈문자열)입력시 500에러 발생 null을 받을 수 있는 Integer같은 타입으로 변환이 필요 혹은 기본값 사용시 처리 가능
기본값 적용
@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";
}
- defaultValue로 기본값 설정 가능
- 빈문자열 혹은 아무것도 입력하지 않으면 기본값으로 요청을 해석 후 처리
- defaultValue가 설정되면 짜피 다 들어가니까 required가 없어도 오케
파라미터를 map으로 조회
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
log.info("paramap={}",paramMap);
log.info("username={}, age={}", paramMap.get("username"),
paramMap.get("age"));
return "ok";
}
- map을 파라미터로 설정하면 해당 쿼리 파라미터 모든 값이 map형태로 반환됨
paramap={username=hello, age=20}
파라미터 값이 여러개인 경우 MultiValueMap을 사용해도 되지만 보통 파라미터 값은 1개인 경우가 많아서 Map으로도 충분
HTTP요청 파라미터 - @ModelAttribute
요청 파라미터 받아서 객체 만들어서 쓰는 경우가 많음
이걸 자동화 해준 것이 @ModelAttribute
@Data
public class HelloData {
private String username;
private int age;
}
- @Data
- getter , setter, toString 등등 필요한 거 대부분 자동 적용해줌
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(),
helloData.getAge());
return "ok";
}
- 진짜... 그냥 알아서 다 만들어주네;; 놀랍다 놀라워
@ModelAttribute 실행 순서
- HelloData 객체 생성
- 요청 파라미터 이름으로 HelloData객체 프로퍼티 찾음
- 해당 프로퍼티의 setter 호출해서 파라미터 값 입력 (바인딩) 함
ex) username이면 setUsername() 메서드 찾아서 값 입력
프로퍼티
객체에 getUsername , setUsername 등의 메서드가 있으면 이 객체는 username이라는 프로퍼티를 가지고 있다고 인식함
바인딩 오류
- 숫자 넣을 곳에 문자 넣으면 BindException발생
@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(),
helloData.getAge());
return "ok";
}
- 이것도 RequestParam 처럼 생량 가능
HTTP요청 메세지 - 단순 텍스트
- 주로 json
- 요청 파라미터와 다르게 http message body에 직접 데이터가 오는 경우 위에 썼던 @RequestParam , @ModelAttribute 사용 못함 ;;;
- 쿼리데이터랑 html form의 방법 외에는 그냥 지금 밑에 방법 쓰면 됨 그 외는 데이터를 직접 가지고 와야함 !
- 스트림으로 데이터 읽고 바꾸고 했던 거를 스프링의 http 메세지 컨버터가 자동으로 처리해줌
level 1
@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");
}
- 서블릿 처럼 처리
- 서블릿 request객체의 getInsputStream 메서드를 호출해서 스트림을 반환하고 해당 스트림에 있는 바이트 데이터들을 utf-8로 인코딩해서 문자열로 반환
- 해당 문자열을 보고 내부에서 처리하는 것
level2
@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을 바로 받음
- 위에서 request에 있는 거 쓰는 게 아니라 InputStream을 바로 파라미터에 받아서 해당 스트림 안에 http 메세지 바이트 코드가 있을테니까 해당 코드를 또 인코딩하고 내부에서 씀
스프링 mvc가 지원하는 파라미터
- InputStream(Reader): HTTP 요청 메시지 바디의 내용을 직접 조회
- OutputStream(Writer): HTTP 응답 메시지의 바디에 직접 결과 출력
level 3
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {
// http메세지 컨버터가 해당 코드 알아서 실행시켜주나봄 String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); String messageBody = httpEntity.getBody(); // 메세지에 있는 바디를 끄냄
log.info("messageBody={}", messageBody);
return new HttpEntity<>("ok");
}
- HttpEntity
- header , body 정보를 간편하게 조회
- 요청 파라미터 @ReqeustParam @ModelAttribute 와 노관계
- 응답에서도 사용 가능하며 헤더 정보 및 메세지 바디 정보를 반환 가능
- 리턴할 때 view 정보를 반환하지 않음
HttpEntity를 상속한 다음 객체들도 같은 기능 제공
- RequestEntity
- HttpMethod, url 정보가 추가, 요청에서 사용
- ResponseEntity
- HTTP 상태 코드 설정 가능, 응답에서 사용
- return new ResponseEntity<\String>("Hello World", responseHeaders,
HttpStatus.CREATED)
스프링 내부에서 http 메세지 바디 읽어서 문자 혹은 객체 변환해주는 http 메세지 컨버터
현업에서 쓰는 거
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) {
log.info("messageBody={}", messageBody);
return "ok";
}
@RequestBody
- 편리하게 http 메세지 바디 정보 조회 가능
- 헤더 정보가 필요한 경우 위에서 봤던 HttpEntity , @ReqeustHeader쓰자
요청 파라미터와 http 메세지 바디
- 요청 파라미터 조회 : @ReqeustParam , @ModelAttribute
- http 메세지 조회 : @RequestBody
@ResponseBody
- 뷰 쓰는 게 아니라 바로 http 메세지 바디에 데이터 담아서 전달
HTTP 요청 메세지 JSON
level1
@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");
}
- 서블릿 request사용해서 http메세지 바디 데이터를 스트림으로 받아서 컨버팅후 읽음
- json의 데이터를 objectMapper를 통해 HelloData객체로 변환
level2
@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";
}
- @ReqeustBody를 통해서 http메세지 취득후 ObjectMapper로 객체 변호나
문자 변환후 다시 json 변환 과정 불편..
level3
@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData data) {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return "ok";
}
@RequestBody 객체 파라미터
@RequestBody, @HttpEntity 사용 시 http메세지 컨버터가 http 바디 내용을 원하는 문자나 객체로 변환시켜서 반환해줌 -> json도 객체로 변환해준다 !
@RequestBody는 생략 불가 // 생략하면 1,2, 요청파라미터에서 했던 기본값 이외의 값 HelloData가 들어있으니 @ModelAttribute가 적용되어 에러가 뜬다
level 3 -2
@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";
}
level4
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return data;
}
@ResponseBody
- 문자 뿐만 아니라 객체도 http 바디에 넣어서 반환 가능
- HttpEntity 사용해도 괜찮
@RequestBody 요청
- JSON 요청 HTTP 메시지 컨버터 객체
@ResponseBody 응답 - 객체 HTTP 메시지 컨버터 JSON 응답
HTTP 응답 - 정적 리소스 , 뷰 템플릿
- 정적 리소스
- 예) 웹 브라우저에 정적인 HTML, css, js를 제공할 때는, 정적 리소스를 사용한다.
- 뷰 템플릿 사용
- 예) 웹 브라우저에 동적인 HTML을 제공할 때는 뷰 템플릿을 사용한다.
- HTTP 메시지 사용
- HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 하므로, HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 실어 보낸다
정적 리소스
아래 디렉토리에 있는 정적 리소스 제공
/static , /public , /resources , /META-INF/resourcessrc/main/resources 는 정적 리소스 보관 장소이며 클래스 패스의 시작 경로임
정적 리소스는 파일 변경없이 그대로 서비스 하는 것
뷰템플릿
- 동적인 html 제공
- 뷰템플릿을 거쳐서 html 생성되고 뷰가 응답 만들어서 전달
- 기본 뷰 템플릿 경로
src/main/resources/templates
@Controller
public class ResponseViewController {
@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";
}
//비추
@RequestMapping("/response/hello")
public void responseViewV3(Model model) {
model.addAttribute("data", "hello!!");
}
}
https://www.inflearn.com/questions/961287
String 반환하는 경우
- @ResponseBody 없으면 리턴값으로 뷰리졸버 찾아서 뷰 찾고 랜더링
- @ResponseBody 있으면 리턴값 그대로 바디에 넣어서 반환
Void 반환
- @Controller 사용하고 HttpServletResponse ,OutputStreawm 같은 http 메세지 처리하는 파라미터가 없는 경우 요청URL 참고해서 논리뷰 이름으로 사용
- 다만 비추임
HTTP 응답 - HTTP API , 메세지 바디에 직접 입력
- 데이터를 메세지 바디에 JSON같은 형식으로 데이터를 실어 보냄
level1
@GetMapping("/response-body-string-v1")
public void responseBodyV1(HttpServletResponse response) throws IOException
{
response.getWriter().write("ok");
}
- 서블릿처럼 http객체를 사용해서 직접 응답을 넣어줌
level2
@GetMapping("/response-body-string-v2")
public ResponseEntity<String> responseBodyV2() {
return new ResponseEntity<>("ok", HttpStatus.OK);
}
- 반환값이 ResponseEntity<\String> <- httpEntity 상속 메세지 헤더 및 바디 정보 가짐
- http 응답 코드 설정 가능
level3
@ResponseBody
@GetMapping("/response-body-string-v3")
public String responseBodyV3() {
return "ok";
}
- @ResponseBod로 문자열 그대로 바디에 실어서 반환
json level1
@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);
}
- ResponseEntity<\HelloData> 객체를 반환
- 상태코드도 넣어줄 수 있고 JSON그대로 넣어서 반환해줌
json level2
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json-v2")
public HelloData responseBodyJsonV2() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return helloData;
}
- HelloData 객체 반환
- 응답 코드는 애노테이션을 사용해서 설정가능 = 정적
@RestController
- 클래스에 해당 애노테이션 붙이면 하위 메서드에 @ResponseBody붙은 것과 같은 효과를 줌
HTTP 메세지 컨버터
뷰템플릿으로 html 만들어서 응답하는 것이 아니라 API처럼 json데이터를 바디에 직접 읽거나 쓰는 경우 HTTP 메세지 컨버터를 사용
@ResponseBody처리 순서
1. http 바디에 문자내용 직접 반환
2. viewReslover 대신 HttpMessageConverter가 동작
3. 반환에 따라 StringHttpMessagConverter / MappiongJackson2HttpMessageConverter 가 동작함
4. byte , string 객체 등 여러 httpmessageconverter가 기본으로 등록되어 제공
응답의 경우 accept헤더와 컨트롤러 타입 두개 다 비교하고 컨버터를 선택
아래의 상황에 http메세지컨버터가 동작
- HTTP 요청: @RequestBody , HttpEntity(RequestEntity) ,
- HTTP 응답: @ResponseBody , HttpEntity(ResponseEntity)
canRead() , canWrite() : 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크
read() , write() : 메시지 컨버터를 통해서 메시지를 읽고 쓰는 기능
순서
읽기 : canRead() -> read()
쓰기 : canWrite -> 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 요청 데이터 읽기
- 요청이 오면 컨트롤러에서 @RequestBody ,HttpEntity 파라미터를 사용
- 메세지 컨버터가 메세지 읽을 웃 있는지 canRead()로 확인
- 대상 클래스 타입을 지원하는지 bytes[], String , HelloData
- 요청 content-type미디어 타입 지원하는지 text/plain , application/json , /
- canRead()에서 조건 만족하면 read()를 호출해서 객체 생성 후 반환
http 응답 데이터 생성
- 컨트롤러에서 @ResponseBody , HttpEntity 로 값이 반환
- 메세지 컨버터가 메세지 쓸 수 있는지 carnWrite()로 확인
- 대상 클래스 타입 지원하는가 return : byte[] , String , HelloData
- Accept미디어 타입 지원하는가 ext/plain , application/json , /
- canWrite() 조건을 만족하면 write() 호출해서 바디에 데이터 생성해줌
https://www.inflearn.com/questions/751430
요청 매핑 핸들러 어뎁터 구조
메세지 컨버터는 어디쯤에서 사용되는가??
핸들러 어댑터에서 관련
- 애노테이션 기반 컨트롤러 (@ReqeustMapping)처리하는 핸들러 어댑터인 RequestMappingHandlerAdapter 요청 매핑 핸들러 어댑터 이 있음
RequestMappiongHandlerAdapter 동작방식
리졸버 = 일단 뭐든 처리해주는 친구
ArgumentResolver
- 매우 다양한 파라미터를 사용하는 애노테이션 기반 컨트롤러에게 파라미터를 유연하게 처리하게 처리해주는 리졸버
- 핸들러 어댑터는 컨트롤러를 호출하기 전에 해당 컨트롤러가 필요로 하는 파라미터들을 준비할 수 있는 ArgumentResolver에게 요청을 해서 파라미터 값을 생성하고 핸들러어댑터는 리졸버가 다 준비되면 그제서야 옛다하고 컨트롤러에게 파라미터 넘겨주고 실행시킴
- 그러니까 파라미터 암거나 막 넣어도 임마가 알아서 넣어줬으니까 신경 안쓰고 유연하게 만들 수 있었던 것
- 해당 리졸버는 인터페이스로 약 30개 넘는 스프링이 제공해주는 구현된 리졸버를 통해서 다양한 컨트롤러의 파라미터를 지원해줄 수 있음
ReturnValueHandler
- 응답값을 변환하고 처리
- 컨트롤러에서 String ,void 등등 반환해도 동작해주는 게 욤마 덕분
HTTP메세지 컨버터
메세지 컨버터를 사용하는 경우는 http 메세지 바디값을 직접 건드릴 때
@RequestBody , @ResponseBody를 사용할 때 메세지 컨버터 호출
요청
- @RequestBody처리하는 ArgumentResolver가 있고 HttpEntity처리하는 리졸버가 있음 이 리졸버들이 http메세지 컨버터 사용할 때 필요한 객체를 생성해서 컨트롤러에 넣음
응답
- @ResponseBody , HttpEntity 처리하는 ReutrnValueHandler가 있음 여기서 메세지 컨트롤러 호출해서 응답 결과를 만든다
https://www.inflearn.com/questions/944109
https://www.inflearn.com/questions/858726
https://www.inflearn.com/questions/825997
https://www.inflearn.com/questions/947991
https://www.inflearn.com/questions/947991
'Programing > Spring Boot' 카테고리의 다른 글
[MVC-2] 타임리프 (0) | 2023.08.08 |
---|---|
[MVC-1] 스프링 MVC -웹 페이지 (0) | 2023.08.08 |
[MVC-1] 스프링 MVC - 구조 이해 (0) | 2023.08.01 |
[MVC-1] MVC 프레임 워크 만들기 (0) | 2023.08.01 |
[MVC-1] 서블릿 , JSP , MVC패턴 (0) | 2023.07.25 |