Spring 에서는 클라이언트 측에서 보낸 데이터를 Java 코드에서 사용할 수 있는 객체로 만들어주는 것과 관련해서 @RequestParam
, @ModelAttribute
그리고 @RequestBody
3가지 어노테이션을 제공합니다.
Spring 을 이용한 개발을 할때 각 어노테이션의 세부 수행 동작 원리에 대해 완벽하게 숙지하지 않은 채로 진행한다면, 얘기치 못한 에러의 늪에 빠지기 쉽습니다.
이번 포스팅에서는 3가지 어노테이션의 세부 기능과 차이점에 대해 알아보겠습니다 👨💻
@RequestParam은 1개의 Http 요청 파라미터(변수) 를 받기 위해서 사용됩니다.
@Slf4j
@ResponseBody // 응답 값을 Json 형태로 변환하여 Http Body 에 담는다.
@RequiredArgsConstructor
@RestController
public class TestController {
@GetMapping("/request-param-test")
public ResponseDto requestParamTest(
@RequestParam(name = "name") String name,
@RequestParam(name = "age") Integer age) {
return new ResponseDto(name,age);
}
}
@Getter
@Setter
@Builder
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class ResponseDto {
private String name;
private Integer age;
private String girlFriendYn;
}
@RequestParam
를 이용하여 클라이언트로 부터 넘어온 두 개의 Http 요청 파라미터를 받고 있습니다.
name
과 age
Http 요청 파라미터를 받고 있는데, 만약 Http 요청 파라미터와 컨트롤러의 매개변수 이름이 동일하다면 (name = "xxx")
을 생략할 수 있습니다.
해당 예제를 Postman
으로 간단하게 테스트 해보겠습니다 💪
해당 이미지는 Postman
을 통해 요청을 보내는 이미지입니다.
Http 요청 파라미터로 알맞은 값을 설정하여 보내는 것을 확인할 수 있습니다.
@ResponseBody
를 이용했기 때문에 반환된 ResponseDto
객체가 Json 형태로 직렬화 되어 반환된 것을 확인할 수 있습니다.
@RequestParam 에는 required 속성과 defaultValue 속성이 있습니다.
@RequestParam.required
True
(요청 파라미터 필수)False
일때 Http 요청 파라미터가 넘어가지 않는 경우 null
로 채워짐@RequestParam.defaultValue
required
속성이 의미가 없음예제
@Slf4j
@ResponseBody
@RequiredArgsConstructor
@RestController
public class TestController {
@GetMapping("/request-param-test")
public ResponseDto requestParamTest(
@RequestParam(required = true) String name,
@RequestParam(required = false) Integer age,
@RequestParam(defaultValue = "N") String girlFriendYn) {
return new ResponseDto(name, age, girlFriendYn);
}
}
동일하게 Postman
을 통해 예제 테스트를 해보겠습니다.
1. required
속성 값이 true인 name 요청 파라미터를 생략하고 요청을 보내기👨💻
해당 요청에 대한 결과를 보겠습니다.
필수값인 name
이 설정되지 않았으니 에러가 뜨는것을 예상할 수 있겠죠?
예상처럼 400 에러
가 뜨는 것을 확인할 수 있습니다 😰
어플리케이션 콘솔창에도 동일하게
Required request parameter 'name' for method parameter type String is not present
에러 정보가 뜨는 것을 확인할 수 있습니다.
2. required
속성 값이 false인 age 요청 파라미터를 생략하고 요청을 보내기👨💻
이번에는 required
속성이 false
인 즉, 필수 요청 파라미터가 아닌 age 를 생략하고 요청을 보내보겠습니다.
예상대로 null
로 채워지는 것을 확인할 수 있습니다 ✌️
defalutValue
속성 테스트해보기 👨💻이번에는 defaultValue
속성값을 가지는 girlFriendYn
요청 파라미터를 생략하고 요청을 보내보겠습니다.
우리가 설정한 것처럼 "N"
으로 채워지는 것을 확인할 수 있습니다.
girlFriendYn
을 빈 문자열로 보내도 같은 결과를 확인할 수 있습니다.
@ModelAttribute 는
multipart/form-data
형태의 HTTP body (form)내용과 HTTP 요청 파라미터를 Java 객체에 매핑(바인딩)합니다.
@GetMapping("/model-attribute-test")
public HelloData modelAttributeTest(
@ModelAttribute HelloData helloData
) {
return helloData;
}
@Getter
@Setter
@Builder
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class HelloData {
private String name;
private Integer age;
private Double height;
private Double weight;
}
1. Http 요청 파라미터를 요청으로 보내기 👨💻
앞과 동일하게 Postman
을 통해 테스트를 해보겠습니다 👩🔬
HelloData
객체에 매핑(바인딩) 하기 위해 name
,age
,height
,weight
요청 파라미터를 보냈습니다.
앞선 테스트와 동일하게 @ResponseBody
어노테이션을 사용하는 예제이기에 Json 형식으로 데이터가 정상적으로 넘어가는것을 확인할 수 있습니다.
2. Http 요청 Body 에 Form 데이터로 요청 보내기 👨💻
Json 형식의 요청을 보냈습니다. 결과는 어떨까요?
예상과 다르게 모든 필드가 null
로 반환된것을 확인할 수 있습니다 😰
@ModelAttribute 는 Query String 형태 혹은 Http 요청 Body 에 삽입되는 Form 형태의 데이터만 처리할 수 있습니다.
Form 형태의 데이터로 다시 요청을 보내보겠습니다 👀
Http 요청 Body 를 form-data 로 변경하니 우리가 예상한 결과가 나온것을 확인할 수 있습니다 💪
지금까지 @ModelAttribute
에 대해 알아보았습니다.
하지만 여기서 우리가 넘어간 정보가 있습니다.
그것은 매핑(바인딩) 할 객체는 Setter
가 있어야한다는 사실입니다❗️
앞에서 예제로 든 HelloData
클래스에서 Setter
를 제거해보고 다시 테스트 해보겠습니다.
@Getter
//@Setter -> Setter 생략
@Builder
@ToString
//@AllArgsConstructor -> 모든 필드에 대한 생성자 생략
@NoArgsConstructor
public class HelloData {
private String name;
private Integer age;
private Double height;
private Double weight;
}
HelloData
클래스를 이와 같이 변경 후 다시 테스트해보겠습니다.
Setter
를 생략하니 매핑(바인딩) 되지 않는 것을 확인할 수 있습니다 🤔
@RequestBody 는 클라이언트가 보내는 Http 요청 Body(Json 및 XML) 을 Java 객체로 변환하는 역할을 합니다.
여기서 @RequestBody
는 @ModelAttribute
경우의 매핑(바인딩) 방식과 달리 변환
한다는 점에서 차이가 있습니다.
@GetMapping("/request-body-test")
public ResponseDto requestBody(
@RequestBody ResponseDto request
) {
return request;
}
앞과 동일하게 ResponseDto
객체를 사용해서 테스트해보겠습니다 👨💻
Http 요청 Body를 Json 형식으로 변경 후 테스트 진행했습니다.
요청에 대해서 정상적인 처리가 이루어진것을 확인할 수 있습니다 💪
지금까지 @RequestBody
에 대해 알아보았습니다.
하지만 @RequestBody
는 @ModelAttribute
와 달리 Setter
나 생성자
가 없이 기본 생성자
만 있어도 정상 작동하는데
두 방식에 차이에 대해 더 알아보겠습니다.
앞에서 강조했던 것처럼 @RequestBody
는 클라이언트의 Json 요청 데이터를 매핑(바인딩) 하는 것이 아니라 변환 과정을 통해 Java 객체를 생성합니다.
이때 작동하는 것이 Spring 의 여러 MessageConverter 중 하나인 MappingJackson2HttpMessageConverter
입니다.
해당 클래스를 통해 내부적으로 ObjectMapper
로 인해서 요청으로 들어온 Json 값을 Java 객체로 역직렬화
합니다.
여기서 역직렬화
란 생성자를 거치지 않고 Reflection
을 통해 객체를 구성하는 매커니즘입니다.
Reflection 이란 클래스의 구체적인 정보를 모른 채 동적으로 객체를 생성할 수 있도록 도와주는 API 를 의미합니다.
즉, Reflection
이라는 마법같은 기능을 통해 해당 객체를 생성할 수 있습니다.
Reflection API 간단히 알아보자 을 참고해주세요 ❗️
@RequestBody를 사용하면 요청 본문의 JSON, XML, Text 등의 데이터가 적합한 HttpMessageConverter를 통해 파싱되어 Java 객체로 변환 됩니다.
@RequestBody를 사용할 객체는 필드를 바인딩할 생성자나 setter 메서드가 필요없습니다.
다만 직렬화를 위해 기본 생성자는 필수 입니다.
getter/setter 중 1가지는 정의되어야 합니다.
1. @RequestBody와 @ModelAttribute
[TIL] #6. 스프링 MVC - 기본 기능 ②
[Spring] @RequestParam, @RequestBody, @ModelAttribute 차이