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 요청

- log 출력결과

**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

- Log 출력

💡 `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

- 출력결과

⭐ 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

- 출력결과

- “속성값”과 변수명이 같으면 생략이 가능하다.
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

- 출력결과

- **@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

- 출력결과

⭐ 위의 방식은 권장하지 않습니다. 명시적으로 표시되어있지 않으면 팀의 협의가 있지 않는 경우 다른 개발자들에게 혼동을 주게 됩니다. 최소 `@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`

- Exception이 발생하는 경우
**ex)** `http://localhost:8080/v4/request-param?age=100`

**ex)** `http://localhost:8080/v4/request-param`

- `required = false` 설정이 되어있으면 해당 파라미터는 없어도 된다.
- **주의!** `http://localhost:8080/v4/request-param?name=sparta` 요청한다면?

- 500 Error가 발생한다.
- int Type에는 null을 넣을 수 없다. 0이라도 들어가야 한다.

- 따라서 보통 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=`

- null과 빈 문자열 “”은 다르다!
- 위 형태는 빈 문자열 “” 로 인식한다, True지만 통과가 되어버린다. **주의**해야 한다.

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`


- age Parameter의 값이 없으면 기본적으로 1 으로 설정한다.
**ex)** `http://localhost:8080/v5/request-param?name=wonuk`


**ex)** `http://localhost:8080/v5/request-param`


- **주의!** defaultValue 속성을 설정하게 되면 “” 빈 문자열의 경우에도 기본 값이 설정된다.
**ex)** `http://localhost:8080/v5/request-param?name&age`


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`


- 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`


💡 파라미터 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
```

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
```

- @ModelAttirubte 동작 순서
1. 파라미터에 @ModelAttribute가 있으면 파라미터인 Tutor 객체를 생성한다.
2. 요청 파라미터 이름으로 객체 필드의 Setter를 호출해서 바인딩한다.
1. 파라미터 이름이 `name` 이면 `setName(value);` 메서드를 호출한다.
2. 파라미터 이름과 필드 이름이 반드시 같아야 한다!
- @Data의 Setter가 없다면?
```java
@Getter
public class Tutor {
private String name;
private int age;
}
```

- 객체 필드에 값이 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
```

- 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;
}
```

- **나머지 경우들(객체)은 모두 @ModelAttribute 와 Mapping한다. V3**

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 구조

- HTTP Request, Response 예시

- 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

- Request Header Content-Type : text/plain

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

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

- 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

- 응답은 추후 응답 데이터 강의에서 다룹니다.
- 위 방법을 적용해도 불편하다.
- 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

- **@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


- HttpServletRequest를 사용하여 HTTP Message Body 데이터를 Read하여 문자로 변환한다.
- 문자로 만들어진 JSON을 Jackson 라이브러리의 objectMapper를 사용하여 Object로 변환

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

- @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

❓ 위 Controller가 동작하는 이유는 무엇인가요?
- **@RequestBody**
- `@RequestBody` 어노테이션을 사용하면 **Object를 Mapping**할 수 있다.
- `HttpEntity<>`, `@RequestBody`를 사용하면 **HTTPMessageConverter**가 Request Body의 Data를 개발자가 원하는 String이나 Object로 변환해준다.
- JSON to Object의 Mapping 또한 가능하다.
- `MappingJackson2HttpMessageConverter` 의 역할

- 쉽게 설명하면 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

- **생략하면 @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

- `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

- 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을 변환한다.

- `@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(허용할 데이터 형식)