[스프링 MVC 1편] 6. 스프링 MVC - 기본 기능

조은지·2023년 8월 6일
0

HTTP 요청 - 기본, 헤더 조회


 @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); //HttpServlet Request
        log.info("response={}", response);
        log.info("httpMethod={}", httpMethod);
        log.info("locale={}", locale); //localeResolver
        log.info("headerMap={}", headerMap);
        log.info("header host={}", host);
        log.info("myCookie={}", cookie);

        //HttpSession , InputStream, OutputStream , RequestPart, RedirectAttribute
        //BindingResult
        return "ok";
    }


HTTP 요청 파라미터


쿼리 파라미터, HTML Form , @RequestParam

쿼리파라미터와 htmlForm은 request.getParameter()을 사용하면 다음 두가지 요청 파라미터를 조회할 수 있다.


1. HttpServletRequest 사용

    //서블릿에서 요청 파라미터를 직접 꺼내는 방식
    @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"); 
    }

2. @RequestParam 사용

    @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 사용
     * Map을 사용하면 전체 쿼리파라미터를 가져올 수 있다. 
     */
   @ResponseBody
    @RequestMapping("/request-param-test")
    public String requestParamTest(@RequestParam Map<String,Object> paramMap) {
        log.info("username={},",paramMap);
        return "ok";
    }
  • 추가 ) @ResponseBody

    Controller 어노테이션에 String반환을 하게 되면 ViewResolver가 호출이 된다.

    해결방법

    1. RestController로 바꾼다.
    2. 메소드레벨에 @ResponseBody를 추가한다.

    html폼으로 해도 정상적으로 동작한다.


@ModelAttribute

@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
 log.info("username={}, age={}", helloData.getUsername(),
helloData.getAge());
 return "ok";
}
  • 바인딩 할 객체에 @Data 애노테이션이 있어야 한다.

  • @ModelAttribute 과정

    HelloData 객체를 생성한 후 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력(바인딩) 한다

  • 데이터 타입이 다르게 들어가는 경우 바인딩 오류가 발생 -> 검증 부분에서 처리 필요



HTTP 요청 메시지

요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우는 @RequestParam , @ModelAttribute 사용이 불가하다.


단순 텍스트와 JSON


1. HttpServletRequest

  • request.getInputStream() 으로 직접 String을 읽어오거나 objectMapper을 사용하여 객체로 바인딩해준다.

2. InputStream

  • Controller의 파라미터로 InputStream과 Writer을 직접 받아올 수 있다

3. HttpEntity

  • 스프링에서 제공하는 Http 헤더, 바디 정보를 편리하게 조회할 수 있는 객체
  • 요청 파라미터를 조회하는 기능과는 관계 없다. (@RequestParam , @ModelAttribute X)
  • HttpEntity를 상속받은 객체
    • RequestEntity, ResponseEntity - http메소드, 응답코드 등을 추가할 수 있다.

4. @RequestBody

  • 스프링 MVC 내부에서 HTTP 메시지 바디를 읽어 문자열 혹은 객체로 변환해서 전달해준다.
    • 이 때, HTTP 메세지 컨버터 기능을 사용한다.


HTTP 응답


응답 데이터를 만드는 방식 3가지

  1. 정적 리소스
  2. 뷰 템플릿 (동적 html)
  3. HTTP 메시지 사용 (HTTP API 를 사용

HTTP API, 메시지 바디에 직접 입력


1. HttpServletResponse

  • response.getWriter().write("ok") HTTP 메시지 바디에 직접 응답 메시지를 전달한다.

2. ResponseEntity<>

  • HttpEntity를 상속받은 ResponseEntity 객체를 반환할 수 있다.
  • HTTP 응답 코드 설정이 가능하다.

3. @ResponseBody

  • view를 사용하지 않고 HTTP 메시지 컨버터를 통해 직접 전달이 가능하다.
  • @ResponseStatus 애노테이션을 통해 응답코드도 설정할 수 있다.


HTTP 메시지 컨버터


@ResponseBody의 사용 원리

  • HTTP Body에 문자내용을 직접 반환

  • viewResolver 대신에 HttpMessageConverter가 동작


스프링 MVC가 HTTP 메시지 컨버터를 적용하는 경우

  • @RequestBody, @ResponseBody
  • HttpEntity(RequestEntity) HttpEntity(ResponseEntity)

http 요청 데이터 읽기: 대상 클래스 타입을 지원하는지 확인, http 요청의 Content-Type 확인

http 응답 데이터 생성: 대상 클래스 타입을 지원하는지 확인, http 요청의 accept 미디어 타입을 확인


Controller 에서의 동작

content-type:application/json

@RequestMapping

public void hello(@RequestBody String data){}

메시지 컨버터의 동작

  1. 바이트어레이컨버터의 canRead()가 호출 ->Pass
  2. String컨버터의 canRead()가 호출->OK

이 때, 클래스 타입과 미디어타입을 같이 확인한다.

미디어타입 */* 이기 때문에 어떤 미디어타입이든 가능하다.

=>StringHttpMessageConverter가 동작한다.

우선순위상 콘텐츠 타입이 json이여도 String컨버터가 먼저 사용이 된다.


안되는 경우

content-type: text/html

@RequestMapping

public void Hello(@RequestBody HelloData data){}

1.우선순위 상 ByteArray컨버터의 canRead()호출->Pass

  1. String컨버터의 canRead()호출->Pass

3.MappingJackson2컨버터의 canRead() 호출

Class 타입이 객체이기 때문에 만족,

그러나 미디어 타입은 text/html이기 때문에 만족하지 않는다.



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

HTTP 메시지 컨버터는 스프링 MVC 어디쯤에서 사용되는 것일까?/

요청 매핑 핸들러 어댑터의 동작

RequestMappingHandlerAdapter가 호출이 되었다고 할 때,

먼저, 컨트롤러 클래스의 메소드를 보면 파라미터에 서블렛리퀘스트, 서블렛리스폰스 등의 정보를 던져준다.


ArgumentResolver.

애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있다. 이렇게 파라미터를 유연하게 처리할 수 있는 이유가 바로 ArgumentResolver덕분이다.

  • supportsParameter() - 핸들러(Controller)가 받아야 하는 파라미터 정보를 지원하는지 판단
  • 지원하는 경우, resolveArgument()를 통해 객체를 만들어서 반환한다.
    • 이 때, 메시지 컨버터를 사용해서 필요한 객체를 생성!

ReturnValueHandler

  • ArgumentResolver와 비슷하며, 응답 값을 변환하고 처리한다.
    • 마찬가지로, 여기에서 메시지 컨버터를 호출해서 응답 결과 생성!

0개의 댓글