[spring] HTTP 요청

공수정·2022년 5월 29일
0

spring

목록 보기
29/32
post-custom-banner

종류

클라이언트에서 서버로 요청 데이터를 전달할 때 가장 많이 사용하는 3가지 방식에 대해서 설명하고자한다.

1. GET - 쿼리 파라미터

/url?username=hello&age=20와 같이 HTTP 메시지 body없이 쿼리 파라미터에 데이터를 보내는 방식이다.

2. POST - HTML Form

html에 form 태그에 작성된 내용을 전송 받는 방법이다.

3. HTTP message body

HTTP API에서 주로 사용하는 방식이다.


1. GET - 쿼리 파라미터 & 2. POST - HTML Form

이 두가지 방식을 합친 이유는 클라이언트에서 보내는 방식이 다르게 보일지 몰라도 HTTP 메시지에는 같은 형식으로 저장되기 때문에 구분 없이 사용되기때문에 같이 설명하려고한다.

컨트롤러 작성

/url?username=hello&age=20을 처리해야한다는 상황을 가정하고 컨트롤러를 작성해보자.

1) HttpServletRequest

Servlet을 사용할 때 처럼 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"));
}

2) @RequestParam

@RequestParam라는 어노테이션을 이용하는 방식으로, ()안에 쿼리 파라미터의 키값을 넣어주면 뒤에있는 데이터 형식으로 변환해서 변수에 넣어준다.
1번의 방법보다 훨씬 코드가 깔끔해지는 것을 볼 수 있다.

값을 받을 변수명과 쿼리 파라미터의 키 값이 같다면 ()안에 쿼리 파라미터의 키값을 넣는 것을 생략할 수 있다.
@RequestParam("username") String memberName
-> @RequestParam String memberName
또 이때 변수의 형식이 단순 데이터(int, String, Integer 등 ) 이라면 @RequestParam까지 생략할 수 있다.
@RequestParam("username") String memberName
-> String memberName
(이렇게 되면 spring 내부에서 required=false를 적용한다.)
하지만 어노테이션까지 생략하는 것은 명시적이지 않아 권하지 않는다.

@RequestParam 추가 속성

  1. required : 파라미터 필수 여부 (기본 값 true)
  2. defaultValue : 기본 값 세팅 (기본 값 없음)
@RequestMapping("/request-param-v2")
public void requestParamV2(
	@RequestParam("username") String memberName,
	@RequestParam("age") int memberAge) {
	log.info("username={}, age={}", memberName, memberAge);
}

3) @ModelAttribute

만약 값을 객체로 받고싶다면 @ModelAttribute를 사용해보자.
이 어노테이션을 사용한다면 객체도 자동으로 생성되고, 생성된 객체에 값도 자동으로 넣어준다.

이 어노테이션은 객체의 setter()를 통해서 값을 넣기 때문에 꼭 setter()가 정의되어있어야한다.

이 어노테이션도 생략할 수는 있지만 권장하지 않는다.

@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
	log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
	return "ok";
}

2. HTTP message body

HTTP 메시지 body를 통해 데이터가 넘어오는 경우에는 위의 방식을 사용할 수 없다.

컨트롤러 작성

{"username":"hello", "age":20}
content-type: application/json

이러한 형식의 데이터를 받아서 처리한다고 가정하고 컨트롤러를 작성해보자

1) HttpServletResponse

HttpServletRequest를 사용해서 직접 HTTP 메시지 바디에서 데이터를 InputStream을 이용해 직접 읽을 수 있다.

  • string
@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);
	response.getWriter().write("ok");
}
  • json 등의 객체타입
    문자로된 JSON 데이터를 변환하는 과정이 필요
private ObjectMapper objectMapper = new ObjectMapper();

@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");
}

2) InputStream

InputStream은 위의 HttpServletResponse에서 InputStream을 생성하던 것을 spring이 대신 해줘서 InputStream으로 바로 받을 수 있게 해주는 것이다. 나머지는 동일하다.

@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");
}

3) HttpEntity

HttpEntity란 HTTP 메시지를 편하게 조회할 수 있게 해준다.
HttpEntity를 상속받은 RequestEntityResponseEntity도 같은 기능을 제공한다.

@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {
	String messageBody = httpEntity.getBody();
	log.info("messageBody={}", messageBody);
	return new HttpEntity<>("ok");
}

4) @RequestBody

@RequestBody어노테이션은 HTTP 메시지 바디 정보를 편리하게 조회할 수 있게 해준다.

  • string
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) {
	log.info("messageBody={}", messageBody);
	return "ok";
}
  • json 등의 객체타입
    만약 @RequestBody 데이터 타입이 객체라면 @ModelAttribute와 같이 객체가 생성되며 자동으로 값이 들어간다.

    @RequestParam나 @ModelAttribute`와 같이 생략은 불가능하다

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

물론 HttpEntity에서도 동일하게 동작된다.

@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";
}

HTTP 요청시에 content-typeapplication/json인지 꼭! 확인해야 한다. 그래야 JSON을 처리할 수 있는 HTTP 메시지 컨버터가 실행된다.

출처
1. 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

profile
계속해서 공부하는 개발자입니다 :)
post-custom-banner

0개의 댓글