티스토리 뷰
Validation
객체를 생성하거나 수정하는 등의 작업을 할 때 올바르지 않은 데이터를 걸러내고 유지하기 위해 데이터 검증이 적용됩니다.
Client Side 뿐만 아니라 Server Side에서도 데이터 유효성을 반드시 검사해야합니다. 클라이언트 쪽에서 데이터를 변조하여 서버로 쉽게 보낼 수 있는 상황이 생길 수 있기 때문에 반드시 서버측에서도 데이터 유효성 검사를 실시해야합니다.
스프링에서는 @validated 에노테이션을 이용하여 검증할 수 있습니다.
그러나 검증 기능을 매번 코드로 작성하는 것은 번거롭습니다. 객체 값 하나하나를 If 문을 통해서 검증하는 것은 코드를 작성하는 과정에서 반복적이고 효율적이지 않습니다. 게다가 특정 필드에 대한 검증 로직은 대부분 빈 값(Null)인지 아닌지, 특정 크기를 넘는지 아닌지와 같이 매우 일반적인 로직입니다.
Item 객체가 있을 때, 이 객체가 등록하는 과정과 수정하는 과정에서 요구되는 범위나 값의 범위가 다를 수 있습니다.
예를 들어 등록하는 과정에서는 가격이 최대 백만원이지만, 수정하는 과정에서는 제한을 두지 않는다고 하면 적용하기 힘들어집니다.
그러므로 아래와 같은 검증 로직을 모든 프로젝트에 적용할 수 있게 하는 것이 Bean Validation입니다.
특정 클래스 필드에 @어노테이션을 적용하여 필드가 갖는 제약 조건을 정의하는 구조로 이루어져있습니다.
public class Item {
private Long id;
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000, max = 1000000)
private Integer price;
}
@NotBlank: 빈값 + 공백만 있는 경우를 허용하지 않음
@NotNull: null 값을 허용하지 않음
@Range(min =100, max=1000): 범위 안의 값이면 허용, 아니면 허용하지 않음
@Max(999): 최대 999까지만 허용
@Valid, @Validated
@Valid는 JAVA에서 지원하는 어노테이션이고, @Validated는 스프링에서 지원해주는 어노테이션입니다.
검증시 둘 다 사용가능합니다. 둘 중 아무거나 사용해도 동일하게 작동하지만,
@Validated는 내부에 groups라는 기능을 포함하고 있다는 특징이 있습니다.
Bean Validation
우선 빈 검증을 사용하기 위해서는 build.gradle에 의존관계 하나를 추가해야합니다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
검증 순서
1. @ModelAttribute 각각의 필드에 타입 변환 시도
- 성공하면 다음으로
- 실패하면 typeMismatch로 FieldError 추가
2. Validator 적용
바인딩에 성공한 필드만 Bean Validation을 적용한다.
즉, @ModelAttribute -> 각각의 필드 타입 변환시도 -> 변환에 성공한 필드만 BeanValidation 적용
Item 저장 API 예시
먼저 Item 객체와 객체 필드를 다음과 같이 정의한다고 가정합니다.
HTTP Message -> ItemSaveForm -> Controller -> Item 생성 -> Repository 과정을 거친다고 가정합니다.
@Data
public class Item {
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
public Item() {}
public Item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
Item을 저장하는 ItemSaveForm을 만들어 Item을 저장하는 검증 폼을 생성합니다.
@Data
public class ItemSaveForm {
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000, max = 1000000)
private Integer price;
@NotNull
@Range(max = 9999)
private Integer quantity;
}
Item 객체를 저장하는 REST API 형식의 컨트롤러를 정의합니다.
POST 방식으로 전송하며 @RequestBody를 사용하여 Body 형태로 데이터를 전송받습니다.
참고
@ModelAttribute는 HTTP Request Parameter를 다룰 때 사용하고
@RequestBody는 HTTP Body의 데이터를 객체로 변환할 때 사용합니다. 주로 API JSON 요청을 다룰 때 사용.
@Slf4j
@RestController
@RequestMapping("/validation/api/items")
public class ValidationItemApiController{
@PostMapping("/add")
public Object addItem(@RequestBody @Validated ItemSaveForm form, BindingResult bindingResult){
log.info("API 컨트롤러 호출");
if (bindingResult.hasErrors()) {
log.info("검증 오류 발생 errors={}", bindingResult);
return bindingResult.getAllErrors();
}
log.info("성공 로직 실행");
return form;
}
}
Postman을 사용해서 성공 테스트와 실패 테스트를 각각 진행해보면 로그가 다르게 찍히는 것을 확인할 수 있습니다.
API 요청의 경우 3가지 경우를 나눠 생각해야합니다.
1. 성공요청: 성공
- 객체의 모든 데이터가 형태와 값이 올바르게 입력되었을 경우
2. 실패요청: JSON을 객체로 생성하는 것 자체가 실패
- 객체의 데이터가 하나라도 형태가 맞지 않아 객체를 생성하는 과정부터 실패
3. 검증 오류 요청: JSON을 객체로 생성하는 것은 성공했고, 검증에서 실패
- 객체의 데이터 형태는 모두 올바르지만, 데이터 값이 검증 범위내에 들어있지 않아 실패
@ModelAttribute vs @RequestBody
@ModelAttribute
HTTP 요청 파라미터를 처리하는 @ModelAttribute는 객체의 필드 단위로 적용되기 때문에 특정 필드에 타입이 맞지 않는 오류가 발생해도 나머지 필드는 정상적으로 처리할 수 있습니다.
@ModelAttribute는 필드 단위로 정교하게 바인딩이 적용됩니다. 특정 필드가 바인딩 되지 않아도 나머지 필드는 정상 바인딩 되고, Validator를 사용한 검증도 적용할 수 있습니다.
@RequestBody
HttpMessageConverter는 @ModelAttribute와 다르게 각각의 필드 단위로 적용되는 것이 아니라, 전체 객체 단위로 적용됩니다.
따라서 메시지 컨버터의 작동이 성공해서 객체를 만들어야 @Valid, @Validated가 적용됩니다.
@RequestBody는 HttpMessageConvert 단계에서 JSON 데이터를 객체로 변경하지 못하면 이후 단계 자체가 진행되지 않고 예외가 발생합니다. 컨트롤러 호출도 되지 않고 Validator도 적용할 수 없습니다.
'Spring' 카테고리의 다른 글
[Spring] 서블릿 필터, 스프링 인터셉터 (Filter/Interceptor) (0) | 2022.04.05 |
---|---|
[Spring] 쿠키와 세션을 이용한 간단한 로그인/로그아웃 (0) | 2022.03.29 |
[Spring] Model 객체과 @ModelAttribute (0) | 2022.03.15 |
[Spring] FrontController를 활용한 MVC 패턴 전개 - (2) (0) | 2022.02.09 |
[Spring] FrontController를 활용한 MVC 패턴 전개 - (1) (0) | 2022.02.05 |
- Total
- Today
- Yesterday
- 알림기능개선기
- 런칭 페스티벌
- dm-zoned
- ZNS SSD
- 5주차 회고
- java
- 3차 데모데이
- 스프링MVC
- 8주차 회고
- 스프링 프레임워크
- 백준
- 피움 6주차 회고
- 우테코 회고
- 회고
- 파이썬
- 스프링 부트
- 프로젝트
- Spring
- 우테코
- 네트워크
- CI/CD
- ZNS
- 2차 데모데이
- 팀프로젝트
- 피움
- dm-zoned 코드분석
- 스프링 Logback
- jpa
- 환경 별 로깅 전략 분리
- 알림개선기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |