본문 바로가기
Spring/스프링 MVC

[스프링 MVC 1편] 6 - (4) 응답 & 메세지 컨버터 & 핸들러 어댑터

by Poorm 푸름 2023. 11. 29.

 *  스프링 입문 = window, 스프링 MVC 1편 = Mac 으로 진행합니다

 *  진도 : 섹션6 - (11)~(14)

 *          : 자바 클래스명,         : 코드,         : 단축키

 

 

1. HTTP 응답 

 

스프링(서버)에서 응답 데이터를 만드는 방법은 크게 3가지이다

 

정적 리소스 정적인 HTML, css, js를 제공할 때는 정적 리소스를 사용

경로: /static/public/resources/META-INF/resources 
뷰 템플릿  동적인 HTML을 제공할 때는 뷰 템플릿을 사용
HTTP 메시지 사용 HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 실어 보내기

 

 

예시 1) 뷰 템플릿
[ src - main - java - hello.springmvc - basic - response - ResponseViewController ]

@Controller
public class ResponseViewController {
    @RequestMapping("/response-view-v1")
    public ModelAndView responseViewV1(){
        ModelAndView mav = new ModelAndView("response/hello")
                .addObject("data", "hello!");
        return mav;
    }
}

 

- @Controller

 

- 요청 정보 매핑 @RequestMapping

 

  • 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출 )

- 모델 & 뷰 영역 객체 생성  ~ new ModelandView("// 논리 이름 //")


- 데이터 추가 add.Object()

 

@Controller
public class ResponseViewController {
    @RequestMapping("/response-view-v2")
    public String responseViewV2(Model model){
        model.addAttribute("data", "hello!");
        return "response/hello";
    }
}

 

- @Controller

 

  • @ResponseBody 가 없으면 response/hello 로 뷰 리졸버가 실행되어서 뷰를 찾아 렌더링 한다
  • @ResponseBody 가 있으면 HTTP 메시지 바디에 직접 response/hello 라는 문자가 입력

 

- 요청 정보 매핑 @RequestMapping

 

  • 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출 )

- String으로 반환  public String ~


- Model 객체 생성 및 저장 


- Model에 데이터 추가 .addAttribute()

 

  • addAttribute(String name, Object value)
    value 객체를 name 이름으로 추가

** @Controller를 사용하면서 String으로 반환하면 [반환값 = 뷰의 논리적이름]이 된다 **

 

 

예시 2) HTTP API, 메세지 사용
[ src - main - java - hello.springmvc - basic - response - ResponseBodyController ]

 

@Slf4j
@Controller
public class ResponseBodyController {
    @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";
    }

    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    @GetMapping("/response-body-json-v1")
    public HelloData responseBodyJsonV1(){
        HelloData helloData = new HelloData();
        helloData.setUsername("UserA");
        helloData.setAge(20);
        return helloData;
    }
}

 

- responseBodyV1

  • HttpServletResponse 객체를 통해서 HTTP 메시지 바디에 직접 ok 응답 메시지를 전달 (서블릿과 유사)

 

- responseBodyV2

 

  • ResponseEntity<String> ~  ( = HttpEntity 를 상속 받고, HttpEntity는 HTTP 메시지의 헤더, 바디 정보를 갖고 있다)

  • ResponseEntity는 HTTP 응답 코드를 설정 가능

- responseBodyV3

 

  • @ResponseBodypublic String을 사용해 반환 값 view가 아닌 문자로 받기
    (@RestController 사용하면 @ResponseBody 삭제 가능)

  • ResponseEntity 도 동일한 방식으로 동작

- responseBodyJsonV1

 

  • ResponseEntity<HelloData> 반환 (JSON 형식)

  • @ResponseBody 사용하면 ResponseEntity 생략 가능하고 반환값은 helloData로 변경
    (@RestController 사용하면 @ResponseBody 삭제 가능)

  • 반환 형식이 바뀌어서 응답코드 임의로 설정 불가하므로 @ResponseStatus 애노테이션을 사용

 

 

2. HTTP 메세지 컨버터

 

뷰템플릿으로 HTML을 생성해서 응답하는 것이 아니라 HTTP API 처럼 JSON 데이터를 HTTP 메세지 바디에서 직접 읽고 쓰는 경우에 HTTP 메세지 컨버터를 사용한다

 

 

 

< @ ResponseBody 과정 >

 

localhost:8080 실행

           ↓

내장 톰켓 서버가 입력 받음 

           ↓

스프링에서 Controller 찾기

           ↓

 ResponeseBody

           ↓

 MVC의 View 대신에 Http Messager Converter 사용

           ↓

 기본 문자처리 String  /  기본 객체처리 JSON     

           ↓

 웹에서 출력

 

 

- HTTP 메시지 컨버터를 적용하는 경우

 

  • HTTP 요청 : @RequestBody    /    HttpEntity(RequestEntity)

  • HTTP 응답 : @ResponseBody    /    HttpEntity(ResponseEntity

  • canRead() , canWrite() : 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크

  • read() , write() : 실제로 메시지를 읽고 쓰는 기능

 

    < 컨버터 종류 참고 >

  • 클래스 타입: byte[] , 미디어타입: */* ,
    요청 예) @RequestBody byte[] data
    응답 예) @ResponseBody return byte[] 쓰기 미디어타입 application/octet-stream

  • 클래스 타입: String , 미디어타입: */*
    요청 예) @RequestBody String data
    응답 예) @ResponseBody return "ok" 쓰기 미디어타입 text/plain

  • 클래스 타입: 객체 또는 HashMap , 미디어타입 application/json 관련
    요청 예) @RequestBody HelloData data
    응답 예) @ResponseBody return helloData 쓰기 미디어타입 application/json 관련

 

  < HTTP 요청 데이터  읽기 과정>

 

  1. HTTP 요청 데이터 읽기

 

  2. 컨트롤러에서 @RequestBody , HttpEntity 파라미터를 사용

 

  3. 메시지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 canRead() 를 호출

 

  4. 대상 클래스 타입을 지원하는가       /       HTTP 요청의 Accept 미디어 타입을 지원하는가

      ( byte[] , String , HelloData )     /      ( text/plain application/json */* )


   5. canRead() 조건을 만족하면 read() 를 호출해서 객체 생성 및 반환

 

  < HTTP 응답 데이터  생성 과정>

  1. 컨트롤러에서 @RequestBody , HttpEntity로 값 반환

 

  3. 메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 canWrite() 를 호출

 

  4. 대상 클래스 타입을 지원하는가       /       HTTP 요청의 Accept 미디어 타입을 지원하는가

   

      ( byte[] , String , HelloData )     /      ( text/plain application/json */* )

 

   5. canWrite() 조건을 만족하면 write() 를 호출해서 HTTP 응답 메세지 바디에 데이터 생성

 

 

3. 요청 매핑 핸들러 어댑터 구조

 

< RequestMappingHandlerAdapter 동작 방식 >

 

Dispatcher Servlet

             

RequestMapping 핸들러 어댑터 호출

              

파라미터를 유연하게 처리할 수 있는 ArgumentResolver 호출

              

컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성 / 이 때 HTTP 메세지 컨버터 동작

( HttpServletRequest, Model, @RequestParam, @ModelAttribute, @RequestBody , HttpEntity )

              

파리미터의 값 준비되면 컨트롤러를 호출하면서 값을 넘겨주기

              

ReturnValueHandler / 이 때 HTTP 메세지 컨버터 동작

( ModelAndView @ResponseBody HttpEntity String )

 

 

** ReturnValueHandler **

     ArgumentResolver 와 비슷한데 이것은 응답 값을 변환하고 처리한다

     컨트롤러에서 String으로 뷰 이름을 반환해도 동작하는 이유가 바로 ReturnValueHandler 덕분이다

 

 

[ 정리 ]

 

스프링은 다음을 모두 인터페이스로 제공한다

  • HandlerMethodArgumentResolver

  • HandlerMethodReturnValueHandler

  • HttpMessageConverter