Spring

클라이언트에서 서버로 HTTP 요청 메시지 보내는 방법

꾸준함. 2021. 6. 2. 23:45

주로 사용하는 방법 3가지

  • 쿼리 파라미터 - GET 방식
  • HTML Form - POST 방식
  • HTTP Message Body - POST 방식

 

* 데이터 형식은 주로 JSON 사용 (레거시 코드는 간혹 XML을 사용하지만 최근에는 대부분 JSON)

 

1. 쿼리 파라미터 - GET 방식

  • 메시지 바디 없이 URL에 쿼리 파라미터를 추가해서 데이터를 전달하는 방식 (URL에 그대로 노출되므로 주로 검색, 필터, 페이징 등에 사용)
  • URL에 ?을 시작으로 보내고 &를 통해 추가 파라미터를 구분
  • ex) http://localhost:8080/example?id=jaimemin&blog=tistory
  • 쿼리 파라미터로 전달된 내용 -> id = jaimemin, blog = tistory

 

1.1 Servlet에서 조회하는 방법

 

@WebServlet(name = "ExampleServlet", urlPatterns = "/example")
public class ExampleServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 전체 파라미터 조회
request
.getParameterNames()
.asIterator()
.forEachRemaining(paramName ->
System.out.println(paramName + "=" + request.getParameter(paramName))
);
// 단일 파라미터 조회
String id = request.getParameter("id"); // jaimemin
String blog = request.getParameter("blog"); // tistory
// 파라미터를 이용하는 로직 중략
}
}
view raw .java hosted with ❤ by GitHub

 

 

* 쿼리 파라미터를 통해 넘어온 파라미터명은 같은데 값이 여러 개일 경우에는 어떻게 처리를 할까?

  • 예를 들자면, http://localhost:8080/example?id=jaimemin&blog=tistory&id=gudetama
  • 상단 코드에서처럼 request.getParameter("id")를 통해 값을 받을 경우에는 첫 번째로 넘어온 jaimemin
  • 둘 다 받고 싶은 경우에는 String [] ids = request.getParameterValues("id")와 같이 사용

 

1.2 Spring MVC Controller 방식


@Slf4j
@Controller
public class ExampleRequestParamController {
@RequestMapping("/request-param-version1")
public void requestParamVersion1(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");
}
@ResponseBody
@RequestMapping("/request-param-version2")
public String requestParamVersion2(@RequestParam("username") String memberName
, @RequestParam("age") int memberAge) {
log.info("username = {}, age = {}", memberName, memberAge);
return "ok";
}
@ResponseBody
@RequestMapping("/request-param-version3")
public String requestParamVersion3(@RequestParam String username
, @RequestParam int age) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
@ResponseBody
@RequestMapping("/request-param-version4")
public String requestParamVersion4(String username, int age) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
@ResponseBody
@RequestMapping("/request-param-required")
public String requestParamRequired(@RequestParam(required = true) String username
, @RequestParam(required = false) int age) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
@ResponseBody
@RequestMapping("/request-param-default")
public String requestParamDefault(@RequestParam(defaultValue = "guest") String username
, @RequestParam(required = false, defaultValue = "-1") int age) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
log.info("username = {}, age = {}", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
@ResponseBody
@RequestMapping("/model-attribute-version1")
public String modelAttributeVersion1(@ModelAttribute Example example) {
log.info("example = {}", example);
return "ok";
}
@ResponseBody
@RequestMapping("/model-attribute-version2")
public String modelAttributeVersion2(Example example) {
log.info("example = {}", example);
return "ok";
}
}
view raw .java hosted with ❤ by GitHub

 

* @RequestParam: 파라미터 이름으로 바인딩

* @ResponseBody: HTTP message body에 직접 해당 내용 입력 (@Controller 내 해당 어노테이션이 없을 경우 ok라는 뷰를 조회)

* HTTP 파리미터명과 변수명이 같을 경우 @RequestParam 내 name 필드 생략 가능

* String, Integer와 같이 단순 타입일 경우 @RequestParam 어노테이션 생략 가능 (개인적으로는 명시하는 편이 좋을 듯)

* @RequestParam 내 required 필드는 디폴트가 true 즉, 쿼리 파라미터로 필수적으로 넘어와야 함 (required = false일 경우 필수가 아니어도 되고 defaultValue라는 키워드를 통해 넘어오지 않았을 때 디폴트 값으로 세팅 가능)

* 쿼리 파라미터를 Map 혹은 MultiValueMap으로 조회 가능하고 쿼리 파라미터의 key와 value가 1:1로 매칭 한다는 보장이 없을 경우 MultiValueMap을 사용하는 것을 권장

* @ModelAttribute: 쿼리 파라미터로 넘어온 값을 객체에 바인딩해주는 어노테이션 (실행 순서는 객체를 생성한 후 쿼리 파라미터의 이름으로 객체의 property를 찾아 값을 바인딩)

* 앞서 언급한 것처럼 String, Integer와 같은 단순 타입은 어노테이션 생략 시 @RequestParam 어노테이션이 자동으로 붙고 ArgumentResolver를 지정해둔 타입을 제외한 나머지 타입은 어노테이션 생략 시 @ModelAttribute 어노테이션이 자동으로 붙음

 

2. HTML Form - POST 방식

  • HTML의 Form을 사용해서 데이터를 전송하는 방법
  • HTTP Body 내 Form에 작성된 데이터를 포함해서 전송하기 때문에 content-type 설정 필요
  • content-type은 application/x-www-form-urlencoded
  • 메시지 바디 내 쿼리 파라미터 형식으로 데이터 전달

 

2.1 example.html

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HTML Form Example</title>
</head>
<body>
<form action="/example" method="post">
id: <input type="text" name="id"/>
blog: <input type="text" name="blog"/>
<button type="submit">데이터 전송</button>
</form>
</body>
</html>
view raw .html hosted with ❤ by GitHub

 

 

2.2 ExampleServlet.java

 

@WebServlet(name = "ExampleServlet", urlPatterns = "/example")
public class ExampleServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 전체 파라미터 조회
request
.getParameterNames()
.asIterator()
.forEachRemaining(paramName ->
System.out.println(paramName + "=" + request.getParameter(paramName))
);
// 단일 파라미터 조회
String id = request.getParameter("id"); // jaimemin
String blog = request.getParameter("blog"); // tistory
// 파라미터를 이용하는 로직 중략
}
}
view raw .java hosted with ❤ by GitHub

 

 

* 메시지 바디 내 쿼리 파라미터 형식으로 보내기 때문에 쿼리 파라미터에서 사용한 코드를 동일하게 사용하면 됩니다. * * 서버 입장에서는 둘 다 동일

* 즉, 정리하자면 request.getParameter() 메서드는 GET 방식과 POST 방식 둘 다 지원 (즉, 쿼리 파라미터 방식 내 Spring MVC 예제 코드(1.2)를 동일하게 사용 가능)

 

3. HTTP Message Body - POST 방식

  • HTTP message body에 데이터를 직접 담아서 요청하는 방식
  • HTTP Api에 주로 사용
  • 대표적인 형식은 JSON (이외 XML, Text도 가능)
  • POST, PUT, PATCH 방식 지원
  • HTTP 메시지 바디 내 데이터를 InputStream을 통해 조회 가능

 

3.1 ExampleBodyTextServlet.java (Text 방식)

  • content-type: text/plain
  • message body 내 text 형식으로 데이터 전달

 

@WebServlet(name = "exampleBodyStringText", urlPatterns = "/example-body-text")
public class ExampleBodyTextServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
// messageBody를 활용하여 로직 작성
}
}
view raw .java hosted with ❤ by GitHub

 

3.2 Spring MVC Controller 방식 (Text 방식)


@Slf4j
@Controller
public class ExampleRequestBodyStringController {
@PostMapping("/request-body-string-version1")
public void requestBodyStringVersion1(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");
}
@PostMapping("/request-body-string-version2")
public void requestBodyStringVersion2(InputStream inputStream, Writer responseWriter)
throws IOException {
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody = {}", messageBody);
responseWriter.write("ok");
}
@PostMapping("/request-body-string-version3")
public HttpEntity<String> requestBodyStringVersion3(HttpEntity<String> httpEntity)
throws IOException {
String messageBody = httpEntity.getBody();
log.info("messageBody = {}", messageBody);
return new HttpEntity<>("ok");
}
@ResponseBody
@PostMapping("/request-body-string-version4")
public String requestBodyStringVersion4(@RequestBody String messageBody) {
log.info("messageBody = {}", messageBody);
return "ok";
}
}
view raw .java hosted with ❤ by GitHub

 

* InputStream(Reader): HTTP 요청 내 바디의 내용을 직접 조회

* OutputStream(Writer): HTTP 응답 메시지의 바디에 직접 결과 출력

* HttpEntity: HTTP header, body 정보를 편리하게 조회 (쿼리 파라미터 조회하는 기능과 무관)

* HttpEntity는 응답 메시지의 헤더와 바디에 직접 결과 출력도 가능 (View 조회는 불가)

* RequestEntity: HttpEntity를 상속받고 HTTP 메서드, URL 정보 추가 및 요청에 사용

* ResponseEntity: HttpEntity를 상속받고, HTTP 상태 코드 설정 가능 및 응답에 사용

* RequestBody: HTTP 메시지 바디 정보를 편리하게 조회 가능

 

3.3 ExampleBodyJsonServlet.java (Json 방식)

  • content-type: application/json
  • message body 내 json 형식으로 데이터 전달
  • JSON을 파싱해서 자바 객체로 변환하기 위해서는 라이브러리가 필요한데 Spring Boot 내에는 Jackson 라이브러리가 포함되어있음 (ObjectMapper)

 

@WebServlet(name = "exampleBodyJsonServlet", urlPatterns = "/example-body-json")
public class ExampleBodyJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
ExampleCriteria exampleCriteria = objectMapper.readValue(messageBody, ExampleCriteria.class);
// exampleCriteria를 활용한 로직
}
}
@Getter
@Setter
public class ExampleCriteria {
private String id;
private String blog;
}
view raw .java hosted with ❤ by GitHub

 

3.4 Spring MVC Controller 방식 (JSON 방식)

 

@Slf4j
@Controller
public class ExampleRequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@PostMapping("/request-body-json-version1")
public void requestBodyJsonVersion1(HttpServletRequest request, HttpServletResponse response)
throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody = {}", messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge());
response.getWriter().write("ok");
}
@ResponseBody
@PostMapping("/request-body-json-version2")
public String requestBodyJsonVersion2(@RequestBody String messageBody)
throws IOException {
log.info("messageBody = {}", messageBody);
Example example = objectMapper.readValue(messageBody, Example.class);
log.info("username = {}, age = {}", example.getUsername(), example.getAge());
return "ok";
}
@ResponseBody
@PostMapping("/request-body-json-version3")
public String requestBodyJsonVersion3(@RequestBody Example example) {
log.info("username = {}, age = {}", example.getUsername(), example.getAge());
return "ok";
}
@ResponseBody
@PostMapping("/request-body-json-version4")
public String requestBodyJsonVersion4(HttpEntity<Example> data) {
Example example = data.getBody();
log.info("username = {}, age = {}", example.getUsername(), example.getAge());
return "ok";
}
@ResponseBody
@PostMapping("/request-body-json-version5")
public HelloData requestBodyJsonVersion5(@RequestBody Example example) {
log.info("username = {}, age = {}", example.getUsername(), example.getAge());
return helloData;
}
}
view raw .java hosted with ❤ by GitHub

 

* @RequestBody: HttpEntity, @RequestBody를 사용할 경우 HTTPMessageConverter가 HTTP 메시지 바디의 내용을 사용자의 편의대로 문자나 객체 등으로 변환해줌

HTTP 요청 시에 content-type이 application/json이어야 JSON 처리할 수 있는 HTTPMessageConverter 실행 가능

* @RequestParam, @ModelAttribute와 달리 @RequestBody는 생략 불가능 (생략할 경우 @ModelAttribute가 적용되어 버림)

* @RequestBody 요청 과정: JSON 요청 -> HTTPMessageConverter -> 객체

* @ResponseBody 응답 과정: 객체 -> HTTPMessageConverter -> JSON 응답

 

XML 방식은 생략

 

참고

인프런 스프링 MVC 1편 (김영한 강사님)

 

 

 

 

 

 

 

반응형

'Spring' 카테고리의 다른 글

HttpMessageConverter 간단 정리  (0) 2021.06.11
Spring MVC 구조 정리  (0) 2021.06.09
빈 스코프 (Bean Scope)  (0) 2021.05.26
스프링 빈 생명주기 (Spring Bean Life Cycle)  (0) 2021.05.25
@Autowired - 의존관계 주입  (2) 2021.05.18