클라이언트에서 서버로 데이터를 전달하는 방법은 크게 세 가지로 나뉜다.
GET - Query Parameter (Query String)
http://localhost:8080/request-params?key1=value1&key2=value2@RequestParam을 사용한다.POST - HTML Form Data (x-www-form-urlencoded)
content-type: application/x-www-form-urlencoded로 전달된다.@RequestParam 또는 @ModelAttribute를 사용하여 데이터를 바인딩할 수 있다.HTTP Request Body (JSON, TEXT, XML 등)
@RequestBody를 사용하여 데이터를 처리한다.@RequestParam을 사용하면 URL에서 전달된 데이터를 메서드의 파라미터로 받을 수 있다.
예제:
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@GetMapping("/v1/request-param")
public String requestParamV1(@RequestParam("name") String userName,
@RequestParam("age") int userAge) {
log.info("name={}, age={}", userName, userAge);
return "success";
}
}
실행 시 URL에 http://localhost:8080/v1/request-param?name=sparta&age=20을 입력하면 로그가 출력된다.
@RequestParam("name") String name에서 속성값과 변수명이 같으면 생략 가능하다.@ResponseBody
@GetMapping("/v2/request-param")
public String requestParamV2(@RequestParam String name,
@RequestParam int age) {
log.info("name={}, age={}", name, age);
return "success";
}http://localhost:8080/v2/request-param?name=sparta&age=20int, String, Integer)일 경우 @RequestParam을 생략할 수 있다.@ResponseBody
@GetMapping("/v3/request-param")
public String requestParamV3(String name, int age) {
log.info("name={}, age={}", name, age);
return "success";
}@RequestParam(required=false)의 기본 값이 true이므로 생략하면 필수 입력값이 된다.@RequestParam(required=true)가 기본값이다.400 Bad Request 오류가 발생한다.required=false로 설정하면 없어도 된다.@ResponseBody
@GetMapping("/v4/request-param")
public String requestParamV4(@RequestParam(required = true) String name,
@RequestParam(required = false) Integer age) {
log.info("name={}, age={}", name, age);
return "success";
}http://localhost:8080/v4/request-param?name=sparta로 요청하면 age가 없어도 동작한다.int age는 null을 허용하지 않기 때문에 Integer로 변경해야 한다.@RequestParam에 기본값을 설정할 수 있다.@ResponseBody
@GetMapping("/v5/request-param")
public String requestParamV5(@RequestParam(defaultValue = "sparta") String name,
@RequestParam(defaultValue = "1") int age) {
log.info("name={}, age={}", name, age);
return "success";
}http://localhost:8080/v5/request-param?age=100으로 요청하면 name이 없지만 기본값 "sparta"가 들어간다.Map<String, String>으로 받을 수 있다.@ResponseBody
@GetMapping("/v6/request-param")
public String requestParamV6(@RequestParam Map<String, String> map) {
log.info("name={}, age={}", map.get("name"), map.get("age"));
return "success";
}http://localhost:8080/v6/request-param?name=sparta&age=100MultiValueMap<String, String>을 사용하면 같은 키에 여러 개의 값을 받을 수 있다.객체로 데이터를 받아야 할 때, @RequestParam을 사용하면 객체를 직접 생성해야 한다.
@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;
}
}
@ModelAttribute를 사용하면 객체를 자동으로 바인딩해준다.@ResponseBody
@PostMapping("/v2/tutor")
public String modelAttributeV2(@ModelAttribute Tutor tutor) {
return "tutor name = " + tutor.getName() + " age = " + tutor.getAge();
}http://localhost:8080/v2/tutorcontent-type: application/x-www-form-urlencodedname=wonuk&age=100을 전달하면 자동으로 Tutor 객체가 생성된다.@ModelAttribute가 있으면 Tutor 객체를 생성한다.name이면 setName(value)이 실행된다.@ModelAttribute를 생략해도 자동으로 매핑된다.@ResponseBody
@PostMapping("/v3/tutor")
public String modelAttributeV3(Tutor tutor) {
return "tutor name = " + tutor.getName() + " age = " + tutor.getAge();
}@RequestParam은 기본 데이터 타입(String, int, Integer)이면 자동 매핑된다.Tutor)는 @ModelAttribute가 자동 적용된다.@RequestParam@RequestParam, @ModelAttribute@ModelAttribute@RequestBody (다음 강의에서 다룸)@RequestParam과 @ModelAttribute는 쿼리 파라미터 또는 HTML Form 데이터를 처리하는 방식이다.HttpServletRequest를 사용하면 요청 본문을 직접 읽을 수 있다.
텍스트 데이터를 요청 본문에서 읽는 예제:
@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);
log.info("bodyText={}", bodyText);
response.getWriter().write("response = " + bodyText);
}
}
Postman 요청 예제
POST /v1/request-body-textBody -> raw -> TextContent-Type: text/plainbodyText=클라이언트에서 보낸 텍스트InputStream과 Writer를 직접 사용할 수도 있다.@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 = " + body);
}Writer responseWriter를 사용하면 response.getWriter().write() 없이 직접 응답을 작성할 수 있다.HttpEntity를 사용하면 요청 본문을 쉽게 읽을 수 있다.@PostMapping("/v3/request-body-text")
public HttpEntity<String> requestBodyTextV3(HttpEntity<String> httpEntity) {
String body = httpEntity.getBody();
return new HttpEntity<>("response = " + body);
}HttpEntity<T>는 요청 본문을 T 타입으로 변환해준다.HttpMessageConverter가 내부적으로 동작한다.@RequestBody를 사용하면 HttpServletRequest 없이 요청 본문을 바로 읽을 수 있다.
@RestController
public class RequestBodyStringController {
@PostMapping("/v5/request-body-text")
public String requestBodyTextV5(@RequestBody String body,
@RequestHeader HttpHeaders headers) {
return "request header = " + headers + " response body = " + body;
}
}
특징
@RequestBody String body: 요청 본문을 String으로 변환해준다.@RequestHeader HttpHeaders headers: 요청 헤더를 받을 수 있다.HttpMessageConverter가 자동으로 동작하여 본문을 변환한다.JSON 데이터를 요청 본문에서 읽고, ObjectMapper를 사용하여 객체로 변환할 수 있다.
@Data
public class Tutor {
private String name;
private int age;
}
@RestController
public class JsonController {
private final ObjectMapper objectMapper = new ObjectMapper();
@PostMapping("/v1/request-body-json")
public void requestBodyJsonV1(HttpServletRequest request,
HttpServletResponse response) throws IOException {
ServletInputStream inputStream = request.getInputStream();
String requestBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
Tutor tutor = objectMapper.readValue(requestBody, Tutor.class);
response.getWriter().write("tutor name=" + tutor.getName() + ", age=" + tutor.getAge());
}
}
작동 방식
JSON을 String으로 변환.ObjectMapper를 사용하여 Tutor 객체로 변환.@RequestBody를 사용하면 ObjectMapper 없이 JSON을 객체로 변환할 수 있다.
@RestController
public class JsonController {
private final ObjectMapper objectMapper = new ObjectMapper();
@PostMapping("/v2/request-body-json")
public String requestBodyJsonV2(@RequestBody String requestBody) throws IOException {
Tutor tutor = objectMapper.readValue(requestBody, Tutor.class);
return "tutor name = " + tutor.getName() + ", age = " + tutor.getAge();
}
}
Postman 요청 예제
POST /v2/request-body-json{
"name": "sparta",
"age": 25
}tutor name = sparta, age = 25@RequestBody를 사용하면 자동으로 JSON을 객체로 변환한다.
@RestController
public class JsonController {
@PostMapping("/v3/request-body-json")
public String requestBodyJsonV3(@RequestBody Tutor tutor) {
return "tutor name = " + tutor.getName() + ", age = " + tutor.getAge();
}
}
내부적으로 HttpMessageConverter가 작동하여 변환한다.
@RequestParam과 @ModelAttribute는 생략할 수 있지만, @RequestBody는 생략할 수 없다.@PostMapping("/v4/request-body-json")
public String requestBodyJsonV4(Tutor tutor) {
return "tutor name = " + tutor.getName() + ", age = " + tutor.getAge();
}@ModelAttribute가 적용되어 요청 본문을 읽지 못한다.HttpEntity<Tutor>를 사용하면 요청 본문을 자동으로 객체로 변환할 수 있다.
@RestController
public class JsonController {
@PostMapping("/v5/request-body-json")
public String requestBodyJsonV5(HttpEntity<Tutor> httpEntity) {
Tutor tutor = httpEntity.getBody();
return "tutor name = " + tutor.getName() + ", age = " + tutor.getAge();
}
}
텍스트 데이터 처리 방법
HttpServletRequestInputStreamHttpEntity<String>@RequestBody StringJSON 데이터 처리 방법
HttpServletRequest + ObjectMapper@RequestBody + ObjectMapper@RequestBody TutorHttpEntity<Tutor>Spring에서 HTTP 요청(Request)과 응답(Response)을 다룰 때, HttpEntity<T>는 HTTP 헤더(Header)와 본문(Body) 데이터를 쉽게 다룰 수 있도록 도와주는 클래스다.
요청(Request) 데이터 처리
@RequestParam이나 @ModelAttribute와 다르게 쿼리 파라미터(Query Parameter)를 사용하지 않는다. HttpServletRequest 없이 요청 데이터를 읽을 수 있다. 응답(Response) 데이터 처리
ResponseEntity<T>를 사용하면 응답 상태 코드도 설정할 수 있다. HttpEntity<String>을 사용하면 요청 본문 데이터를 String으로 받을 수 있다.
@RestController
public class HttpEntityController {
@PostMapping("/http-entity")
public HttpEntity<String> requestBodyText(HttpEntity<String> httpEntity) {
String body = httpEntity.getBody();
return new HttpEntity<>("Received: " + body);
}
}
요청 예제
POST /http-entity
Content-Type: text/plain
Hello Spring!
응답 결과
HTTP/1.1 200 OK
Content-Type: text/plain
Received: Hello Spring!
HttpEntity<T>를 사용하면 JSON 데이터를 자동으로 객체로 변환할 수 있다.
@Data
public class User {
private String name;
private int age;
}
@RestController
public class HttpEntityJsonController {
@PostMapping("/http-entity-json")
public HttpEntity<String> requestBodyJson(HttpEntity<User> httpEntity) {
User user = httpEntity.getBody();
return new HttpEntity<>("Received: " + user.getName() + ", age: " + user.getAge());
}
}
요청 예제
POST /http-entity-json
Content-Type: application/json
{
"name": "wonuk",
"age": 25
}
응답 결과
HTTP/1.1 200 OK
Content-Type: text/plain
Received: wonuk, age: 25
HttpEntity<T>는 View를 반환하지 않는다. @RequestParam이나 @ModelAttribute처럼 쿼리 파라미터(Query Parameter)를 처리하지 않는다. @RequestBody는 HttpEntity와 비슷하지만 더 간단한 방식으로 요청 본문을 읽을 수 있다.
HttpEntity<T>보다 직관적으로 사용할 수 있기 때문에 일반적으로 @RequestBody를 더 많이 사용한다.
@RestController
public class RequestBodyStringController {
@PostMapping("/request-body")
public String requestBodyText(@RequestBody String body) {
return "Received: " + body;
}
}
요청 예제
POST /request-body
Content-Type: text/plain
Hello Spring!
응답 결과
HTTP/1.1 200 OK
Content-Type: text/plain
Received: Hello Spring!
JSON 데이터를 @RequestBody를 사용하면 자동으로 객체로 변환할 수 있다.
@RestController
public class RequestBodyJsonController {
@PostMapping("/request-body-json")
public String requestBodyJson(@RequestBody User user) {
return "Received: " + user.getName() + ", age: " + user.getAge();
}
}
요청 예제
POST /request-body-json
Content-Type: application/json
{
"name": "wonuk",
"age": 25
}
응답 결과
HTTP/1.1 200 OK
Content-Type: text/plain
Received: wonuk, age: 25
@RequestBody는 쿼리 파라미터(Query Parameter)를 처리하지 않는다. @ModelAttribute와 다르게 Setter가 없어도 JSON 데이터가 매핑된다. HttpMessageConverter가 작동하여 JSON을 객체로 변환한다. @ResponseBody를 사용하면 View가 아니라 데이터 자체를 HTTP 응답 본문에 직접 반환한다.
@RestController를 사용하면 자동으로 모든 메서드에 @ResponseBody가 적용된다.
ResponseEntity<T>와 함께 사용하면 HTTP 상태 코드도 조정할 수 있다.
@RestController
public class ResponseBodyController {
@GetMapping("/response-body")
public String responseBody() {
return "Hello, ResponseBody!";
}
}
응답 결과
HTTP/1.1 200 OK
Content-Type: text/plain
Hello, ResponseBody!
@RestController
public class ResponseBodyJsonController {
@GetMapping("/response-body-json")
public User responseBodyJson() {
return new User("wonuk", 25);
}
}
응답 결과
HTTP/1.1 200 OK
Content-Type: application/json
{
"name": "wonuk",
"age": 25
}
@RestController
public class ResponseEntityController {
@GetMapping("/response-entity")
public ResponseEntity<User> responseEntity() {
User user = new User("wonuk", 25);
return new ResponseEntity<>(user, HttpStatus.CREATED);
}
}
응답 결과
HTTP/1.1 201 Created
Content-Type: application/json
{
"name": "wonuk",
"age": 25
}
텍스트 데이터 처리 방법
HttpServletRequest InputStream HttpEntity<String> @RequestBody String JSON 데이터 처리 방법
HttpServletRequest + ObjectMapper @RequestBody + ObjectMapper @RequestBody DTO 객체 HttpEntity<DTO> @ResponseBody를 사용하면 데이터를 직접 반환 ResponseEntity<T>를 사용하면 응답 상태 코드 조정 가능