[스프링 - MVC] HTTP 요청 바디 데이터 조회

이전 게시글들 (요청파라미터 , @ModelAttribute) 에서 “GET, 쿼리 파라미터”와 “POST, HTML Form” 으로 전달된 파라미터를 처리하는 방법에 대해 설명하였다.

요청 메시지가 어떻게 데이터를 전달하는지 다시 확인해보자면 아래와 같다.

  • GET - 쿼리 파라미터
  • POST - HTML Form
  • HTTP message body에 데이터를 직접 담아서 전달하는 방식

이 중 첫 번째와 두 번째 방식을 처리하는 방법에 대해서 이전 글에서 다뤘으니, 이제 세 번째 방식 ‘요청 메시지의 바디부에 데이터가 담긴 방식’ 을 어떻게 처리할 것인지 알아보자.




개요

HTTP message body에 데이터를 직접 담아서 요청

  • HTTP API에서 주로 사용한다. (JSON, XML, TEXT 등)
  • 데이터 형식은 주로 JSON을 사용한다.

이 방식의 경우, 요청 파라미터와는 다르게 @RequestParam , @ModelAttribute 를 사용할 수 없다.

물론, HTML Form 형식을 통해 바디부에 입력되는 경우 제외




요청 메시지 바디 데이터 처리

기초적인 방식

직접 InputStream을 가져와서 바디 데이터 조회하기

@Slf4j
@Controller
public class RequestBodyStringController {

	@PostMapping("/request-body-string-v1")
	public void requestBodyString(HttpServletRequest request, HttpServletResponse response) throws IOException {
		
		//InputStream을 통해 요청msg의 바디부 데이터를 받는다. (바이트코드로 받음)
		ServletInputStream inputStream = request.getInputStream();

		//바이트코드로 구성된 바디부 데이터를 UTF-8 형식으로 인코딩하여 문자열로 변환한다.
		String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

		log.info("messageBody={}", messageBody);
		response.getWriter().write("ok");
	}

}
  • request.getInputStream()
    • 요청 메시지의 바디부분에 저장되어있는 데이터를 바이트코드로 받는다.
  • StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8)
    • InputStream으로 받은 데이터를 인코딩하여 문자열로 변환한다.


스프링에게 InputStream , Writer 객체를 전달받아 조회하기

@Slf4j
@Controller
public class RequestBodyStringController {

	/**
	 * 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 inputStream
    • 스프링으로부터 ‘요청 메시지의 바디 데이터가 들어있는 InputStream 객체’ 를 전달받는다.
  • 매개변수 Writer responseWriter
    • 스프링으로부터 ‘응답 메시지의 바디에 데이터를 작성할 수 있는 Writer 객체’ 를 전달받는다.
  • InputStream (Reader)
    • HTTP 요청 메시지 바디의 내용을 직접 조회하는 기능을 지원한다.
  • OutputStream (Writer)
    • HTTP 응답 메시지의 바디에 직접 결과 출력하는 기능을 지원한다.


스프링에게 HttpEntity<> 객체를 전달받아 조회하기

@Slf4j
@Controller
public class RequestBodyStringController {

	/**
	 * 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) {
		
		//요청 메시지의 바디부분 데이터 받기
		String messageBody = httpEntity.getBody();

		log.info("messageBody={}", messageBody);

		//HttpEntity<> 객체로 응답도 할 수 있다.
		return new HttpEntity<>("ok");
	}

}
  • HttpEntity
    • HTTP header, body 정보를 편리하게 조회할 수 있도록 지원하는 객체이다.
    • 매개변수 HttpEntity<String> httpEntity 는 스프링으로부터 받는다.
    • 요청 파라미터를 조회하는 기능과 관계는 없다.
      • @RequestParam , @ModelAttribute 와 관계 없음.
    • 응답에도 사용할 수 있다.
      • 응답으로 사용시, 메시지 바디 정보를 직접 반환하게 된다.
      • 헤더 정보 포함 가능
      • view 조회는 안된다.
  • HttpEntity 를 상속받은 객체들
    • RequestEntity
      • HttpMethod, url 정보가 추가됐다.
      • 요청에서 사용한다.
    • ResponseEntity
      • HTTP 상태 코드를 설정할 수 있다.

참고로, 스프링 MVC 내부에서 ‘HTTP 메시지 바디’를 읽어서 문자나 객체로 변환하여 전달해준다. 이때 사용되는 기능이 바로 “HTTP 메시지 컨버터” 이다. HTTP 메시지 컨버터는 나중에 자세히 다룬다.



애너테이션 방식

@RequestBody 를 통해 요청 메시지의 바디 데이터 조회하기

@Slf4j
@Controller
public class RequestBodyStringController {

	/**
	 * @RequestBody
	 * - 메시지 바디 정보를 직접 조회(@RequestParam X, @ModelAttribute X)
	 * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
	 *
	 * @ResponseBody
	 * - 메시지 바디 정보 직접 반환(view 조회X)
	 * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
	 */
	@ResponseBody
	@PostMapping("/request-body-string-v4")
	public String requestBodyStringV4(@RequestBody String messageBody) {
		log.info("messageBody={}", messageBody);
		return "ok";
	}

}
  • @RequestBody
    • HTTP 메시지 바디 정보를 편리하게 조회할 수 있다.
    • 헤더 정보가 필요하면 위에서 설명한 HttpEntity 를 사용하거나, @RequestHeader 를 사용하면 된다.
    • 요청 파라미터 조회 기능인 @RequestParam@ModelAttribute 와 관계가 없다.
  • @ResponseBody 역시 존재한다.
    • HTTP 응답 메시지에 직접 정보를 담아서 전달할 수 있다.



요청 파라미터 vs HTTP 메시지 바디

요청 파라미터 조회 기능

  • @RequestParam
  • @ModelAttribute


HTTP 메시지 바디 정보 조회 기능

  • @RequestBody




요청 메시지 바디의 JSON 데이터 처리

이번에는 요청 메시지의 바디가 JSON 형식일 경우에 대해 알아보자.



기초적인 방식

직접 InputStream을 가져와서 바디 데이터를 자바 객체로 전환하기

/**
 * 요청메시지의 정보
 * 바디: {"username":"hello", "age":20} 
 * 헤더: content-type: application/json
 */
@Slf4j
@Controller
public class RequestBodyJsonController {

	//JSON을 처리하기 위한 객체
	private ObjectMapper objectMapper = new ObjectMapper();

	@PostMapping("/request-body-json-v1")
	public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
		
		//InputStream을 통해 요청 메시지 바디 정보를 가져옴.
		ServletInputStream inputStream = request.getInputStream();

		//인코딩하여 문자열로 변환
		String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

		log.info("messageBody={}", messageBody);

		//ObjectMapper 객체를 통해 JSON 데이터를 HelloData 객체에 바인딩
		HelloData data = objectMapper.readValue(messageBody, HelloData.class);

		log.info("username={}, age={}", data.getUsername(), data.getAge());
		response.getWriter().write("ok");
	}

}
  • request.getInputStream()
    • 위에서 설명했던 것과 동일하다.
  • ObjectMapper 객체
    • 문자로 된 JSON 데이터를 Jackson 라이브러리인 ObjectMapping 를 사용해서 자바 객체로 변환한다.



애너테이션 방식

@RequestBody 를 통해 JSON 데이터를 문자열로 받아 객체로 전환하기

/**
 * 요청메시지의 정보
 * 바디: {"username":"hello", "age":20} 
 * 헤더: content-type: application/json
 */
@Slf4j
@Controller
public class RequestBodyJsonController {

	private ObjectMapper objectMapper = new ObjectMapper();

	/**
	 * @RequestBody
	 * HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
	 *
	 * @ResponseBody
	 * - 모든 메서드에 @ResponseBody 적용
	 * - 메시지 바디 정보 직접 반환(view 조회X)
	 * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
	 */
	@ResponseBody
	@PostMapping("/request-body-json-v2")
	public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {

		//문자열로 받은 JSON 데이터를 HelloData 객체로 변환한다.
		HelloData data = objectMapper.readValue(messageBody, HelloData.class);

		log.info("username={}, age={}", data.getUsername(), data.getAge());
		return "ok";
	}

}
  • 위에서 설명한 @RequestBody 를 적용하여 문자열을 자바 객체로 변환했다.
  • 하지만, 여전히 불편하다…
  • 아래에 설명을 계속하겠다.


@RequestBody 를 통해 JSON 데이터를 자바 객체로 전환하기

/**
 * 요청메시지의 정보
 * 바디: {"username":"hello", "age":20} 
 * 헤더: content-type: application/json
 */
@Slf4j
@Controller
public class RequestBodyJsonController {

	/**
	 * @RequestBody 생략 불가능(@ModelAttribute 가 적용되어 버림)
	 * HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter (content- type: application/json)
	 *  */
	@ResponseBody
	@PostMapping("/request-body-json-v3")
	public String requestBodyJsonV3(@RequestBody HelloData data) {
		log.info("username={}, age={}", data.getUsername(), data.getAge());
		return "ok";
	}

}
  • 매개변수 @RequestBody HelloData data
    • HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 우리가 원하는 문자나 객체 등으로 변환해준다.
    • 위 코드 (requestBodyJsonV3 메서드) 의 경우, JSON 데이터를 자바 객체로 변환하여 매개변수로 전달해준 것이다.

    다시 이야기하지만, 이 마법같은 HTTP 메시지 컨버터는 나중에 다루겠다.

  • 주의사항
    • HTTP 요청 메시지의 헤더가 content-type: application/json 를 포함하는지 확인해야한다.
    • 그래야만, HTTP 메시지 컨버터가 정상적으로 JSON 데이터를 자바 객체로 변환해준다.

위 코드는 아래 코드와 동일하다.


HttpEntity 객체를 통해 JSON 데이터를 자바 객체로 전환하기

/**
 * 요청메시지의 정보
 * 바디: {"username":"hello", "age":20} 
 * 헤더: content-type: application/json
 */
@Slf4j
@Controller
public class RequestBodyJsonController {

	@ResponseBody
	@PostMapping("/request-body-json-v3")
	public String requestBodyJsonV3(@RequestBody HelloData data) {
		log.info("username={}, age={}", data.getUsername(), data.getAge());
		return "ok";
	}

	//위 코드 (requestBodyJsonV3 메서드)와 동일하게 동작한다.
	@ResponseBody
	@PostMapping("/request-body-json-v4")
	public String requestBodyJsonV4(HttpEntity<HelloData> data) {
		log.info("username={}, age={}", data.getUsername(), data.getAge());
		return "ok";
	}

}


JSON 형식으로 응답하기

/**
 * 요청메시지의 정보
 * 바디: {"username":"hello", "age":20} 
 * 헤더: content-type: application/json
 */
@Slf4j
@Controller
public class RequestBodyJsonController {

	/**
	 * @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());

		//자바 객체를 그대로 반환하여, JSON 형식으로 응답한다.
		//이러한 동작은 @ResponseBody를 통해 가능하다.
		return data;
	}

}
  • @ResponseBody
    • 응답 메시지 바디에 직접 데이터를 입력한다. (반환값을 응답 데이터로)
    • 자바 객체를 반환할 땐, JSON 형식으로 응답하게 된다.



정리

@RequestBody

  • JSON 요청 → HTTP 메시지 컨버터 → 객체
  • 매개변수의 데이터타입에 따라, 동작이 달라진다.
    • String 타입의 매개변수에 적용시
      • 요청 메시지의 바디 정보를 문자열로 넘겨준다.
    • 단순 타입 이외의 매개변수에 적용시
      • 요청 메시지의 바디 정보를 해당 객체로 변환하여 넘겨준다.
      • (당연히 JSON 타입으로 요청시, content-type: application/json 이어야 한다.)

@ResponseBody

  • 객체 → HTTP 메시지 컨버터 → JSON 응답
  • 반환값의 데이터타입에 따라, 동작이 달라진다.
    • 반환값이 String 타입일 때
      • 응답 메시지 바디에 문자열(text)로 담아 응답한다.
    • 반환값이 단순 타입 이외의 매개변수일 때
      • 응답 메시지 바디에 JSON 형식으로 담아 응답한다.




  • 본 게시글은 김영한님의 강의를 토대로 정리한 글입니다.
  • 더 자세한 내용을 알고 싶으신 분들이 계신다면, 해당 강의를 수강하시는 것을 추천드립니다.