Spring (9)

destro·2025년 5월 14일

2. Spring

목록 보기
9/17
post-thumbnail

HTTP 요청 데이터

  • Client에서 Server로 Data를 전달하는 방법 📚 Client에서 Server로 Data를 전달하는 방법은 Query Parameter, HTTP Form Data, HTTP Request Body 크게 세가지가 있다. **1. GET + Query Parameter(=Query String)** - URL의 쿼리 파라미터를 사용하여 데이터 전달하는 방법 ``` http://localhost:8080/request-params?**key1=value1&key2=value2** ``` - HttpServletRequest 사용 ```java @Slf4j @Controller public class RequestParamController { @GetMapping("/request-params") public void params( HttpServletRequest request, HttpServletResponse response ) throws IOException { String key1Value = request.getParameter("key1"); String key2Value = request.getParameter("key2"); log.info("key1Value={}, key2Value={}", key1Value, key2Value); response.getWriter().write("success"); } } ``` - `response.getWriter().write()` - HttpServletResponse를 사용해서 응답값을 직접 다룰 수 있다. - @Controller 지만 @ResponseBody를 함께 사용한 것과 같다. - Postman 요청 ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/c7a25323-99cd-4190-bd43-87507de4d3a1/image.png) - log 출력결과 ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/ef598162-b31f-47ef-99ef-6e6eb00c54b1/image.png) **2. POST + HTML Form(x-www-form-urlencoded)** - HTTP Request Body에 쿼리 파라미터 형태로 전달하는 방법 - **HTTP Request** ``` POST /form-data content-type: application/x-www-form-urlencoded **key1=value1&key2=value2** ``` - HttpServletRequest 사용 ```java @Slf4j @Controller public class RequestBodyController { @PostMapping("/form-data") public void requestBody( HttpServletRequest request, HttpServletResponse response ) throws IOException { String key1Value = request.getParameter("key1"); String key2Value = request.getParameter("key2"); log.info("key1Value={}, key2Value={}", key1Value, key2Value); response.getWriter().write("success"); } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/c19029fb-c671-4f80-b8e2-561308283584/image.png) - Log 출력 ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/cad8644a-d357-4194-9eb7-e2ad4f286423/Untitled.png) 💡 `HttpServletRequest.getParameter(”key”);`를 사용하면 Query Parameter, HTML Form Data 두가지 경우 모두 데이터 형식(key=value)이 같기 때문에 해당값에 접근할 수 있다. **3. HTTP Request Body** - 데이터(JSON, TEXT, XML 등)를 직접 HTTP Message Body에 담아서 전달한다. - 주로 @RestController에서 사용하며, 대부분 JSON 형식으로 데이터를 전달한다. - POST, PUT, PATCH Method에서 사용한다. - GET, DELETE Method는 Body에 데이터를 담는것을 권장하지 않는다. - HttpServletRequest 사용 ```java @Getter @Setter public class Board { private String title; private String content; } ``` ```java package com.example.springbasicannotation.controller; import com.example.springbasicannotation.entity.Board; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.PostMapping; import java.io.IOException; import java.nio.charset.StandardCharsets; @Slf4j @Controller public class RequestBodyController { // JSON을 객체로 변환해주는 Jackson 라이브러리 private ObjectMapper objectMapper = new ObjectMapper(); @PostMapping("/request-body") public void requestBody( HttpServletRequest request, HttpServletResponse response ) throws IOException { ServletInputStream inputStream = request.getInputStream(); String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); log.info("messageBody={}", messageBody); Board board = objectMapper.readValue(messageBody, Board.clss); log.info("board.getTitle()={}, board.getContent()={}", board.getTitle(), board.getContent()); response.getWriter().write("success"); } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/04593e7b-efae-4207-8e79-f5a14dd7613a/image.png) - 출력결과 ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/29cf038d-24f3-43c4-8f83-f1730cd4e9e1/image.png) ⭐ JSON을 Java 객체로 변환하려면 Jackson과 같은 라이브러리를 사용해야 한다. Spring Boot는 기본적으로 Jackson 라이브러리의 ObjectMapper를 제공하며, starter-web에 포함되어 있습니다.

Spring 요청 데이터 (1)

  • @RequestParam 📚 URL에서 파라미터 값과 이름을 함께 전달하는 방식으로 주로 HTTP 통신 Method 중 GET 방식의 통신을 할 때 많이 사용한다. @Requestparam을 사용하면 요청 파라미터 값에 아주 쉽고 간편하게 접근(Parameter Binding)할 수 있다. 💡 ?뒤에 오는 URL을 **Query String, Query Parameter, Request Param**이라 한다. - 예시코드 ```java @Slf4j @Controller public class RequestParamControllerV2 { @ResponseBody @GetMapping("/v1/request-param") public String requestParamV1 ( @RequestParam("name") String userName, @RequestParam("age") int userAge ) { // logic log.info("name={}", userName); log.info("age={}", userAge); return "success"; } } ``` 1. `@Controller + @ResponseBody` - View를 찾는 것이 아니라 ResponseBody에 응답을 작성한다(=@RestController) 2. `@RequestParam` - 파라미터 이름으로 바인딩한다. 3. `@RequestParam(”속성값”)` - 속성값이 파라미터 이름으로 매핑된다. - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/8818471f-6d34-44a6-8b0a-b082e7a999c8/image.png) - 출력결과 ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/17ceb812-32b1-4d71-b062-e4ecc4c4e9f9/image.png) - “속성값”과 변수명이 같으면 생략이 가능하다. ex) `@RequestParam("name") String name` ```java // GET http://localhost:8080/v2/request-param?name=sparta&age=100 @ResponseBody @GetMapping("/v2/request-param") public String requestParamV2 ( @RequestParam String name, @RequestParam int age ) { // logic log.info("name={}", name); log.info("age={}", age); return "success"; } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/6b301f53-3039-4165-94e3-f844c78cc47a/image.png) - 출력결과 ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/17ceb812-32b1-4d71-b062-e4ecc4c4e9f9/image.png) - **@RequestParam 사용법** 1. 어노테이션, 속성값 모두 생략 - @RequestParam은 생략이 가능하다. ```java // GET http://localhost:8080/v3/request-param?name=sparta&age=100 @ResponseBody @GetMapping("/v3/request-param") public String requestParamV3 ( String name, int age ) { // logic log.info("name={}", name); log.info("age={}", age); return "success"; } ``` - 생략하면 `@RequestParam(required=false)` 필수 여부 속성이 default로 설정된다. - 단, 요청 파라미터와 이름이 완전히 같아야 한다. - 단순 타입(int, String, Integer 등)이어야 한다. - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/dfcdf292-a1d1-4bee-8b74-3d03b9a49db0/image.png) - 출력결과 ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/25b2d0a4-6c2e-488c-95e1-66992f82025a/image.png) ⭐ 위의 방식은 권장하지 않습니다. 명시적으로 표시되어있지 않으면 팀의 협의가 있지 않는 경우 다른 개발자들에게 혼동을 주게 됩니다. 최소 `@RequestParam String name` 속성 값 생략 형태를 쓰면 됩니다. 2. required 속성 설정 - 파라미터의 필수 값을 설정한다. - API 스펙을 규정할 때 사용한다. ```java @ResponseBody @GetMapping("/v4/request-param") public String requestParam ( @RequestParam(required = true) String name, // 필수 @RequestParam(required = false) int age // 필수가 아님 ) { // logic log.info("name={}", name); log.info("age={}", age); return "success"; } ``` - @RequestParam을 사용하면 기본 Default값은 **True**이다. - True로 설정된 파라미터 값이 요청에 존재하지 않으면 400 BadRequest(클라이언트 측 에러) - Exception이 발생하지 않는 경우 **ex)** `http://localhost:8080/v4/request-param?name=sparta&age=100` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/7c045795-95df-405a-bec4-f3143767776f/image.png) - Exception이 발생하는 경우 **ex)** `http://localhost:8080/v4/request-param?age=100` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/13e202fd-990e-4aa1-89c3-7b44b75a89af/image.png) **ex)** `http://localhost:8080/v4/request-param` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/6b4b66c0-25f4-4f47-90fa-039d31179d5c/image.png) - `required = false` 설정이 되어있으면 해당 파라미터는 없어도 된다. - **주의!** `http://localhost:8080/v4/request-param?name=sparta` 요청한다면? ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/75749845-239f-4218-879a-0943e3930160/image.png) - 500 Error가 발생한다. - int Type에는 null을 넣을 수 없다. 0이라도 들어가야 한다. ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/d85e7e1b-3c4d-42e2-96eb-30482656a4d1/Untitled.png) - 따라서 보통 null을 허용하는 **Integer**로 사용하거나 **default 옵션**을 사용한다. ```java @ResponseBody @GetMapping("/v4/request-param") public String requestParam ( @RequestParam(required = true) String name, // 필수 @RequestParam(required = false) Integer age ) { // logic log.info("name={}", name); log.info("age={}", age); return "success"; } ``` - 파라미터 Key값만 있고 Value가 없는 경우 `http://localhost:8080/request-param?name=` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/f1877e91-8e6c-4c3c-a9ab-73cdf72cb3fc/image.png) - null과 빈 문자열 “”은 다르다! - 위 형태는 빈 문자열 “” 로 인식한다, True지만 통과가 되어버린다. **주의**해야 한다. ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/ad3a33fa-8c95-424b-8c31-cd14af66d860/image.png) 3. default 속성 적용 - 파라미터의 기본 값을 설정한다. ```java @ResponseBody @GetMapping("/v5/request-param") public String requestParam ( @RequestParam(required = true, defaultValue = "sparta") String name, @RequestParam(required = false, defaultValue = "1") int age ) { // logic log.info("name={}", name); log.info("age={}", age); return "success" } ``` - name Parameter 의 값이 없으면 기본적으로 “sparta”으로 설정한다 **ex)** `http://localhost:8080/v5/request-param?age=100` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/0022b3f9-c011-4d2f-813e-be283725c9ca/image.png) ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/3a64419d-6585-4476-8ea5-bcee51b5b87e/image.png) - age Parameter의 값이 없으면 기본적으로 1 으로 설정한다. **ex)** `http://localhost:8080/v5/request-param?name=wonuk` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/97ad3076-826c-44a7-b8db-918e63851104/image.png) ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/feffac3d-94ff-4e51-836f-75ec52fbb4c9/image.png) **ex)** `http://localhost:8080/v5/request-param` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/bee18213-75a5-403a-a1fe-af183318d10d/image.png) ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/c2712932-c628-4690-8678-eadb8b5134b3/image.png) - **주의!** defaultValue 속성을 설정하게 되면 “” 빈 문자열의 경우에도 기본 값이 설정된다. **ex)** `http://localhost:8080/v5/request-param?name&age` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/73cba866-4efd-4ccc-b32d-0ba7e2486d7f/image.png) ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/eecc459f-c0ca-4cac-9afd-cdee5c909fbf/image.png) 4. Map 사용 - Parameter를 Map형태로 조회가 가능하다. ```java @ResponseBody @GetMapping("/v6/request-param") public String requestParamV6( @RequestParam Map map ) { // logic log.info("name={}", map.get("name")); log.info("age={}", map.get("age")); return "success"; } ``` - Map 형태(`key=value`)로 조회가 가능하다 **ex)** `http://localhost:8080/v6/request-param?name=sparta&age=100` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/5b1d7a3d-3325-4e9f-b6d7-e5c1ee09da41/image.png) ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/3d25fe4b-7f1f-449f-a031-8a11fb07e082/image.png) - MultiValueMap 형태(`key=[value1, value2]`)로 조회가 가능하다. ```java @ResponseBody @GetMapping("/v6/request-param") public String requestParamV6( @RequestParam MultiValueMap map ) { // logic log.info("name={}", map.get("name")); log.info("age={}", map.get("age")); return "success"; } ``` **ex)** `http://localhost:8080/v6/request-param?name=sparta&name=wonuk&name=tutor&age=100` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/6fe6bf9d-51f3-48e2-8e0e-5249de7cc9c6/image.png) ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/9c700cb9-24f9-40b0-9bb4-e5fd4d8e6537/image.png) 💡 파라미터 Map의 Value가 1개인 경우에는 Map, 여러 개인 경우 MultiValueMap을 사용한다. 하지만 대부분의 파라미터 값은 한 개만 존재한다.
  • @ModelAttribute 📚 요청 파라미터를 받아 필요한 Object로 바인딩 해준다. 주로 HTML 폼에서 전송된 데이터를 바인딩하고 HTTP Method POST인 경우 사용된다. 1. **기존 코드** 💡 **@Data**는 `@Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor`를 자동으로 설정해주는 역할을 한다. 테스트 용도로만 사용하고 실무에서는 잘 사용하지 않는다. ex) `http://localhost:8080/v1/tutor` + x-www-form-urlencoded ```java @Data public class Tutor { private String name; private int age; } @Controller public class ModelAttributeController { @ResponseBody @PostMapping("/v1/tutor") public String requestParamV1( @RequestParam String name, @RequestParam int age ) { Tutor tutor = new Tutor(); tutor.setName(name); tutor.setAge(age); return "tutor name = " + name + " age = " + age; } } ``` - @RequestParam 의 Mapping을 사용하게 되면 위와 같은 객체를 생성하는 코드가 포함된다. - `@ModelAttribute` 는 해당 과정을 자동화 한다. - Postman ``` POST /v1/tutor content-type: application/x-www-form-urlencoded name=wonuk&age=100 ``` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/57076788-27b4-42c0-9add-b5ccd4d19ae5/image.png) 2. **@ModelAttribute 적용** ex) `http://localhost:8080/v2/tutor`+ x-www-form-urlencoded ```java @ResponseBody @PostMapping("/v2/tutor") public String modelAttributeV2( @ModelAttribute Tutor tutor ) { String name = tutor.getName(); int age = tutor.getAge(); return "tutor name = " + name + " age = " + age; } ``` - Postman ``` POST /v2/tutor content-type: application/x-www-form-urlencoded name=wonuk&age=100 ``` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/abd397c3-12a3-405c-86cd-a5e8807ffbdf/image.png) - @ModelAttirubte 동작 순서 1. 파라미터에 @ModelAttribute가 있으면 파라미터인 Tutor 객체를 생성한다. 2. 요청 파라미터 이름으로 객체 필드의 Setter를 호출해서 바인딩한다. 1. 파라미터 이름이 `name` 이면 `setName(value);` 메서드를 호출한다. 2. 파라미터 이름과 필드 이름이 반드시 같아야 한다! - @Data의 Setter가 없다면? ```java @Getter public class Tutor { private String name; private int age; } ``` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/8400ae49-20fc-43b8-8e4c-10405867591b/image.png) - 객체 필드에 값이 set되지 않는다. - **파라미터의 타입이 다른 경우** - 만약 요청 파라미터 `age` 에 `int`가 아닌 `String` 이 전달된다면? **ex)** `http://localhost:8080/v2/tutor`+ x-www-form-urlencoded ``` POST /v2/tutor content-type: application/x-www-form-urlencoded name=wonuk&age=nbcamp ``` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/f432e5f8-e445-4af3-bbc6-91155ee5d79f/image.png) - BindException 발생 - 이런 경우 때문에 **Validation(검증)**이 필요하다. 3. **@ModelAttirubte 생략** - @ModelAttribute와 지난 시간에 배운 @RequestParam은 모두 생략이 가능하다. ex) `http://localhost:8080/v3/tutor`+ x-www-form-urlencoded ``` POST /v3/tutor content-type: application/x-www-form-urlencoded name=wonuk&age=100 ``` ```java @ResponseBody @PostMapping("/v3/tutor") public String modelAttributeV3(Tutor tutor) { String name = tutor.getName(); int age = tutor.getAge(); return "tutor name = " + name + " age = " + age; } ``` - Spring에서는 @RequestParam이나 @ModelAttribute가 생략되면 - `String`, `int`, `Integer` 와 같은 기본 타입은 @RequestParam과 Mapping한다. V4 ```java @ResponseBody @PostMapping("/v4/tutor") public String requestParamV2( String name, int age ) { return "tutor name = " + name + " age = " + age; } ``` ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/309a0df9-bfcf-4734-8be8-47ff0c567111/image.png) - **나머지 경우들(객체)은 모두 @ModelAttribute 와 Mapping한다. V3** ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/b44aaf66-0a16-4830-8701-003985f41ca8/image.png)

Spring 요청 데이터 (2)

  • HTTP Message Body(요청) 💡 @RequestParam, @ModelAttribute는 GET + Query Parameter와, POST HTML Form Data를 바인딩하는 방법이다. - 이제부터 배울 내용은 HTTP Message Body에 직접적으로 Data가 전달되는 경우이다. - Request Body의 Data를 바인딩하는 방법이다. - REST API에서 주로 사용하는 방식이다. - HTTP Method POST, PUT, PATCH에서 주로 사용한다. - GET은 Request Body가 존재할 수는 있지만 권장하지 않는다. - **JSON**, XML, TEXT 등을 데이터 형식으로 사용한다. - HTTP Message 구조 ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/8ad60ea8-32ba-4ba7-99fe-4e21a2054dbd/Untitled.png) - HTTP Request, Response 예시 ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/48f0de45-d83b-4fed-b79a-ed9093874336/Untitled.png) - Server에서 Request로 전달받은 Data를 처리하기 위해서 바인딩 해야 한다. **ex)** JSON → Object
  • TEXT 📚 HTTP Request Body에 Data가 전송되는 경우 HttpMessageConverter를 통해 바인딩된다. 💡 현대에는 Restful API를 주로 사용하고 있어서 대부분의 경우 JSON 형식으로 통신한다. 1. **HttpServletRequest 예시** - `request.getInputStream();` ```java @Slf4j @Controller public class RequestBodyStringController { @PostMapping("/v1/request-body-text") public void requestBodyTextV1( HttpServletRequest request, HttpServletResponse response ) throws IOException { ServletInputStream inputStream = request.getInputStream(); String bodyText = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); response.getWriter().write("response = " + bodyText); } } ``` - Postman - Request → Body → raw → Text ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/e380a320-e28d-4d1c-9648-ebfb124560ab/image.png) - Request Header Content-Type : text/plain ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/ebf55e0c-e286-4056-a66e-43d04c0c754c/image.png) 2. **I/O 예시** - **InputStream(읽기)** 파라미터 지원 - HTTP Request Body Data 직접 조회 - **OutputStream(쓰기)** 파라미터 지원 - HTTP Response Body 직접 결과 출력 ```java @PostMapping("/v2/request-body-text") public void requestBodyTextV2( InputStream inputStream, Writer responseWriter ) throws IOException { String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); responseWriter.write("response = " + bodyText); } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/d098a55e-c4b4-46b5-962d-dd887ee3ca04/image.png) 3. **HttpEntity 예시** - HttpMessageConverter 사용 → 추후 설명 - `HttpEntity`를 사용하면 HttpMessageConverter를 사용한다. ```java @PostMapping("/v3/request-body-text") public HttpEntity requestBodyTextV3(HttpEntity httpEntity) { // HttpMessageConverter가 동작해서 아래 코드가 동작하게됨 String body = httpEntity.getBody(); return new HttpEntity<>("response = " + body); // 매개변수 = Body Message } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/2f2251ba-c73b-450c-a551-057e73d2a393/image.png) - Spring의 HttpMessageConverter 덕분에 간편하게 Request Data에 접근할 수 있다. 1. HttpEntity를 사용하면 HttpMessageConverter가 동작하여 자동으로 매핑된다. 2. 요청 뿐만이 아닌 응답까지 HttpEntity 하나만으로 사용이 가능해진다. 💡 Converter는 ****어떤 뭔가를 다른 뭔가로 바꿔주는(Convert) 장치를 말한다. 예를 들면 태양빛을 전기로 바꿔주는 장치 또한 컨버터이다.

Spring 요청 데이터 (3)

  • HttpEntity 📚 HttpEntity는 HTTP Header, Body 정보를 편리하게 조회할 수 있도록 만들어준다. - **HttpEntity 역할** 1. Http Request Body Message를 직접 조회한다 2. Request 뿐만 아니라 Response도 사용할 수 있도록 만들어준다. 3. Response Header 또한 사용할 수 있다. 4. Request Parameter를 조회하는 기능들과는 아무 관계가 없다. 5. View를 반환하지 않는다. - **HttpEntity**를 상속받은 객체 - `RequestEntity<>` - HTTP Request Method, URL 정보가 추가 되어있다. - `ResponseEntity<>` - HTTP Response 상태 코드 설정이 가능하다. - 코드 예시 ```java @Controller public class RequestBodyStringController { @PostMapping("/v4/request-body-text") public HttpEntity requestBodyTextV4(RequestEntity httpEntity) { // HttpMessageConverter가 동작해서 아래 코드가 동작하게됨 String body = httpEntity.getBody(); // url, method 사용 가능 return new ResponseEntity<>("response = " + body, HttpStatus.CREATED); // Body Data, 상태코드 } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/e11cfe58-fd1d-49a2-bb00-a31c250d15a1/image.png) - 응답은 추후 응답 데이터 강의에서 다룹니다. - 위 방법을 적용해도 불편하다. - Data를 httpEntity에서 꺼내어 사용해야 한다. 💡 Spring은 Http RequestBody Message를 읽어서 String이나 Object로 자동으로 변환해준다. 이때 **`HttpMessageConverter`**가 사용된다.
  • @RequestBody, @ResponseBody 📚 Spring에서 @RequestBody, @ResponseBody 어노테이션을 사용하면 각각 Request, Response 객체의 Body에 편하게 접근하여 사용할 수 있다. - 코드 예시 ```java @Controller // @RestController = @Controller + @ResponseBody public class RequestBodyStringController { @ResponseBody @PostMapping("/v5/request-body-text") public String requestBodyTextV5( @RequestBody String body, @RequestHeader HttpHeaders headers ) { // HttpMessageConverter가 동작해서 아래 코드가 동작하게됨 String bodyMessage = body; return "request header = " + headers + " response body = " + bodyMessage; } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/e5068b66-1aa4-4ecc-8c19-1f698a321e8d/image.png) - **@RequestBody** - 요청 메세지 Body Data를 쉽게 조회할 수 있다. - **@RequestHeader** - 요청 헤더 정보 조회 - **@ResponseBody** - 응답 메세지 바디에 값을 쉽게 담아서 전달할 수 있도록 해준다. - View가 아닌 데이터를 반환한다. - **요약** 1. 요청 파라미터, HTML Form Data에 접근하는 경우 - @RequestParam, @ModelAttribute 를 사용한다. 2. Http Message Body에 접근하는 경우 - @RequestBody를 사용한다. (**JSON**, XML, TEXT)

Spring 요청 데이터 (4)

  • JSON 💡 Json은 @RestController 에서 가장 많이 사용되는 데이터 형식이다. 현재 대부분의 API는 Request, Response 모두 JSON 형태로 통신한다. ⛔ Json 형태로 Data를 전송할 때는 Request Header의 content-type이 꼭 **application/json** 이여야 한다, 1. **HttpServletRequest 사용** ```java @Data public class Tutor { private String name; private int age; } @RestController public class JsonController { private ObjectMapper objectMapper = new ObjectMapper(); @PostMapping("/v1/request-body-json") public void requestBodyJsonV1( HttpServletRequest request, HttpServletResponse response ) throws IOException { // request body message를 Read ServletInputStream inputStream = request.getInputStream(); // UTF-8 형식의 String으로 변환한다. String requestBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // String requestBody를 ObjectMapper를 사용하여 변환 "{\"name\":\"wonuk\", \"age\":10}" Tutor tutor = objectMapper.readValue(requestBody, Tutor.class); // 응답 response.getWriter().write("tutor" + tutor); } } ``` - Postman - Content-Type 헤더 확인 : application/json ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/fad113c4-fef2-4e66-a866-2d2765a5d111/image.png) ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/feafd473-fce2-4752-aa29-1782e56a39f4/image.png) - HttpServletRequest를 사용하여 HTTP Message Body 데이터를 Read하여 문자로 변환한다. - 문자로 만들어진 JSON을 Jackson 라이브러리의 objectMapper를 사용하여 Object로 변환 ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/7633e539-5057-4fd0-aacc-3b15c8c9acb8/Untitled.png) 2. **@RequestBody 사용** ```java @RestController public class JsonController { private ObjectMapper objectMapper = new ObjectMapper(); @PostMapping("/v2/request-body-json") public String requesBodytJsonV2(@RequestBody String requestBody) throws IOException { Tutor tutor = objectMapper.readValue(requestBody, Tutor.class); return "tutor.getName() = " + tutor.getName() + "tutor.getAge() = " + tutor.getAge(); } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/36e773f9-929f-4ad6-ad4c-0a906749b7a6/image.png) - @RequestBody를 사용하여 HTTP Request Body의 Data에 접근한다. ❓ ObjectMapper가 계속 반복되는데 @ModelAttribute처럼 객체로 바로 반환은 안되나요? 🧑‍🎓 Spring은 개발에 필요한 대부분의 기능이 구현되어 있습니다. 3. **ObjectMapper 제거** ```java @RestController public class JsonController { @PostMapping("/v3/request-body-json") public String requestBodyJsonV3(@RequestBody Tutor tutor) { Tutor requestBodyTutor = tutor; return "tutor = " + requestBodyTutor; } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/b3505ffe-d5f2-4dd5-b66a-3e25a9182224/image.png) ❓ 위 Controller가 동작하는 이유는 무엇인가요? - **@RequestBody** - `@RequestBody` 어노테이션을 사용하면 **Object를 Mapping**할 수 있다. - `HttpEntity<>`, `@RequestBody`를 사용하면 **HTTPMessageConverter**가 Request Body의 Data를 개발자가 원하는 String이나 Object로 변환해준다. - JSON to Object의 Mapping 또한 가능하다. - `MappingJackson2HttpMessageConverter` 의 역할 ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/a1cadfb9-b99c-4251-a2fa-182d10ca5fa2/Untitled.png) - 쉽게 설명하면 HTTP Message Converter가 ObjectMapper를 대신 실행한다. 4. **@RequestBody는 생략할 수 없다.** 💡 @RequstParam, @ModelAttribute는 생략이 가능하다. ```java @Slf4j @RestController public class JsonController { @PostMapping("/v4/request-body-json") public String requestBodyJsonV4(Tutor tutor) { // @RequestBody 생략시 @ModelAttribute가 된다. Tutor requestBodyTutor = tutor; return "tutor.getName() = " + requestBodyTutor.getName() + " tutor.getAge() = " + requestBodyTutor.getAge(); } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/c68565a9-407d-4dab-87ff-1f1c16c74b63/image.png) - **생략하면 @ModelAttribute가 된다.** - 요청 파라미터를 처리하도록 설정된다. - Request Header의 contentType은 꼭 application/json 이여야 한다. - 위 설정 정보를 기반으로 MessageConverter가 실행된다. 5. **HttpEntity 사용** ```java @RestController public class JsonController { @PostMapping("/v5/request-body-json") public String requestBodyJsonV5( HttpEntity httpEntity ) { // 값을 꺼내서 사용해야한다! Tutor tutor = httpEntity.getBody(); return "tutor.getName() = " + tutor.getName() + " tutor.getAge() = " + tutor.getAge(); } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/53830925-de7d-407a-9c27-c7433bd09b17/image.png) - `HttpEntity` - Generic Type으로 Tutor가 지정되어 있기 때문에 해당 Class로 반환된다. 6. **@ResponseBody** ```java @Controller public class JsonController { @ResponseBody // @RestController = @Controller + @ResponseBody @PostMapping("/v6/request-body-json") public Tutor requestJson(@RequestBody Tutor tutor) { return tutor; } } ``` - Postman ![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/cf807dd5-e826-43c5-a13b-43fa404ffeff/image.png) - View를 조회하지 않고 Response Body에 Data를 입력해서 직접 반환한다. - 요청 뿐만이 아니라 응답에도 `HttpMessageConverter`가 동작한다. - `MappingJackson2HttpMessageConverter` 적용 - 응답 객체인 Tutor가 JSON으로 변환되어 반환된다. - HttpEntity를 사용해도 된다. - **요약** 1. 요청 데이터는 `@RequestBody`를 사용해서 바인딩 하면 된다. 2. `@RequestBody` 는 생략이 불가능하다. - `@ModelAttribute`가 적용되기 때문 3. `HttpMessageConverter` 가 요청 응답 데이터를 모두 변환할 수 있다. - JSON은 `MappingJackson2HttpMessageConverter` 를 사용한다. - Request Header의 Content-Type은 application/json 이어야 한다. - Header로 어떤 Converter가 동작할지 판별한다.
  • HTTPMessageConverter 📚 Spring Framework에서 HTTP 요청과 응답을 변환하는 인터페이스이다. 클라이언트와 서버 간에 데이터를 주고받을 때, 요청 데이터를 자바 객체로 변환하거나 자바 객체를 응답 본문으로 변환하는 역할을 수행한다. 💡 `MappingJackson2HttpMessageConverter`는 JSON을 처리하는 대표적인 `HTTPMessageConverter`의 구현체이다. - **HttpMessageConverter의 역할** - 데이터를 Obejct로 변환한다. 대표적으로 JSON을 변환한다. ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/83c75a39-3aba-4ba4-a792-7aefe4b07895/c7b4ffad-27ce-4c49-997b-cb38e3ea16a2/Untitled.png) - `@RequestBody` - 요청 데이터 + Request Header를 참고하여 Object로 변환한다. - HTTP Request Body(JSON Data) → Converter(Jackson) → Object - Reqeust Header → Content-Type : application/json(전달할 데이터 형식) - `@ResponseBody` - 응답 데이터 + Accept Header를 참고하여 원하는 데이터 형식으로 변환한다. - Object → Converter(Jackson) → HTTP Response Body(JSON Data) - Request Header → Accept : application/json(허용할 데이터 형식)
profile
<포르투갈어> 솜씨 있는. 재간 있는. 능란한. 기민한. (재주가) 비상한.

0개의 댓글