Spring/SpringMVC

HTTP 응답 - HTTP API, 메시지 바디에 직접 입력 & HTTP 메시지 컨버터

느리지만 꾸준하게 2022. 5. 1. 03:51

HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 한다.

 

HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 실어 보낸다.

 

 

  • HTML이나 뷰 템플릿 사용해도 HTTP 응답 메시지 바디에 HTML 데이터가 담겨서 전달됨.
  • 정적 리소스나 뷰 템플릿 거치지 않고, 직접 HTTP 응답 메시지를 전달하는 경우를 말함
// ResponseBodyController Class

package hello.springmvc.basic.response;


import hello.springmvc.basic.HelloData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
//@RestController
//@Controller 대신에 @RestController 애노테이션을 사용하면, 해당 컨트롤러에 모두
//@ResponseBody 가 적용되는 효과가 있다. 따라서 뷰 템플릿을 사용하는 것이 아니라, HTTP 메시지 바디에
//        직접 데이터를 입력한다. 이름 그대로 Rest API(HTTP API)를 만들 때 사용하는 컨트롤러이다.
//        참고로 @ResponseBody 는 클래스 레벨에 두면 전체에 메서드에 적용되는데, @RestController
//에노테이션 안에 @ResponseBody 가 적용되어 있다.
@RestController
public class ResponseBodyController {


    // responseBodyV1
    // 서블릿을 직접 다룰 때 처럼
    // HttpServletResponse 객체를 통해서 HTTP 메시지 바디에 직접 ok 응답 메시지를 전달한다.
    // response.getWriter().write("ok")
    @GetMapping("response-body-string-v1")
    public void responseBodyV1(HttpServletResponse response) throws IOException {
        response.getWriter().write("ok");
    }


    // responseBodyV2
    // ResponseEntity 엔티티는 HttpEntity 를 상속 받았는데, HttpEntity는 HTTP 메시지의 헤더, 바디
    // 정보를 가지고 있다. ResponseEntity 는 여기에 더해서 HTTP 응답 코드를 설정할 수 있다.
    // HttpStatus.CREATED 로 변경하면 201 응답이 나가는 것을 확인할 수 있다
    @GetMapping("response-body-string-v2")
    public ResponseEntity<String> responseBodyV2() throws IOException {
        return new ResponseEntity<>("ok", HttpStatus.OK);
    }


    // responseBodyV3
    // @ResponseBody 를 사용하면 view를 사용하지 않고, HTTP 메시지 컨버터를 통해서 HTTP 메시지를 직접
    // 입력할 수 있다. ResponseEntity 도 동일한 방식으로 동작한다.
    @ResponseBody
    @GetMapping("response-body-string-v3")
    public String responseBodyV3() {
        return "ok";
    }


    // responseBodyJsonV1
    // ResponseEntity 를 반환한다. HTTP 메시지 컨버터를 통해서 JSON 형식으로 변환되어서 반환된다.
    @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);
    }



    // responseBodyJsonV2
    // ResponseEntity 는 HTTP 응답 코드를 설정할 수 있는데, @ResponseBody 를 사용하면 이런 것을
    //설정하기 까다롭다.
    // @ResponseStatus(HttpStatus.OK) 애노테이션을 사용하면 응답 코드도 설정할 수 있다.
    // 물론 애노테이션이기 때문에 응답 코드를 동적으로 변경할 수는 없다. 프로그램 조건에 따라서 동적으로
    // 변경하려면 ResponseEntity 를 사용하면 된다.

    // ResponseBody가 있으면 (helloData, HttpStatus.OK) 지정을 못하니까 @ResponseStatus(HttpStatus.OK) annotation을 쓴다.
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    @GetMapping("/response-body-json-v2")
    public HelloData responseBodyJsonV2() {
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        helloData.setAge(20);
        return helloData;
    }
}

 

 

 

 

 

최상단에 @ResponseBody를 붙이거나

@Controller이나 @ResponseBody를 주석처리하고 @RestController를 붙여서 대체할 수 있다.

@Slf4j
//@Controller
@ResponseBody
//@RestController
public class ResponseBodyController {

 

@Slf4j
//@Controller
//@ResponseBody
@RestController
public class ResponseBodyController {

 

 

 

 

 

HTTP 메시지 컨버터

뷰 템플릿으로 HTML을 생성해 응답하는 게 아니고, HTTP API처럼 JSON 데이터를 HTTP 메시지 바디에 직접 읽거나 쓰는 경우에 HTTP 메시지 컨버터를 사용하면 편리하다.

 

 

 

@ResponseBody를 사용

  • HTTP의 BODY에 문자 내용을 직접 반환
  • viewResolver 대신에 HttpMessageConverter이 동작

 

  • 기본 문자처리: StringHttpMessageConverter
  • 기본 객체처리: MappingJackson2HttpMessageConverter
  • byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있음

=> 응답의 경우 클라이언트의 HTTP Accept 해더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해서
HttpMessageConverter 가 선택됨.

 

스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용한다

  • HTTP 요청: @RequestBody , HttpEntity(RequestEntity)
  • HTTP 응답: @ResponseBody , HttpEntity(ResponseEntity)

 

 

 

 

 

 

 

HTTP 메시지 컨버터는 HTTP 요청, HTTP 응답 둘 다 사용된다.

  • canRead() , canWrite() : 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크
  • read() , write() : 메시지 컨버터를 통해서 메시지를 읽고 쓰는 기능

 

 

 

스프링 부트 기본 메시지 컨버터

0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter

 

스프링 부트는 다양한 메시지 컨버터를 제공하는데, 대상 클래스 타입과 미디어 타입 둘을 체크해서
사용여부를 결정한다. 만약 만족하지 않으면 다음 메시지 컨버터로 우선순위가 넘어간다.

 

 

 

 

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(@RequetsBody String data) {}

 

 

MappingJackson2HttpMessageConverter

content-type: application/json
@RequestMapping
void hello(@RequetsBody HelloData data) {}

 

 

?

content-type: text/html
@RequestMapping
void hello(@RequetsBody HelloData data) {}

 

 

 

 

 

HTTP 요청 데이터 읽기

HTTP 요청이 오고, 컨트롤러에서 @RequestBody , HttpEntity 파라미터를 사용한다.

 


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

 

 

대상 클래스 타입을 지원하는가.

  • 예) @RequestBody 의 대상 클래스 ( byte[] , String , HelloData )

 

 

 

HTTP 요청의 Content-Type 미디어 타입을 지원하는가.

  • 예) text/plain , application/json , */*


canRead() 조건을 만족하면 read() 를 호출해서 객체 생성하고, 반환한다.

 

 

 

 

 

HTTP 응답 데이터 생성

컨트롤러에서 @ResponseBody , HttpEntity 로 값이 반환된다.

 

 

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

 

 

대상 클래스 타입을 지원하는가

  • 예) return의 대상 클래스 ( byte[] , String , HelloData )

 

 

 

HTTP 요청의 Accept 미디어 타입을 지원하는가.(더 정확히는 @RequestMapping 의 produces )

  • 예) text/plain , application/json , */*

 

 

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<출처 김영한: 스프링 MVC 1편 - 벡앤드 웹 개발 핵심 기술>

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -

www.inflearn.com