Spring
Spring Validation
김예나
2025. 2. 6. 16:15
BindingResult
- Validation 오류를 보관하는 객체
- 여기서 검증할 때 사용했던 객체임!
@PostMapping
public ResponseEntity<ScheduleResponseDto> createSchedule(@RequestBody @Valid ScheduleSaveRequestDto requestDto, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
throw new InvalidRequestException(INVALID_REQUEST_EXCEPTION);
}
return new ResponseEntity<>(scheduleService.saveSchedule(requestDto), HttpStatus.CREATED);
}
- 파라미터에 BindingResult가 없는 경우 -> 검증 오류(400 Bad Request)가 발생하고 Controller가 호출되지 않음
- 파라미터에 BindingResult가 있다면 -> BindingResult에 오류가 보관되고 Controller는 정상적으로 호출
- ModelAttribute는 파라미터를 필드 하나하나에 바인딩함 -> 만약 그중 하나의 필드에 오류가 발생하면 해당 필드를 제외하고 나머지 필드들만 바인딩 된 후 Controller가 호출
- 제대로 입력한 값은 잘 전달되어 화면에 나타나고, 에러는 BindingResult에 보관됨
@Controller
public class BindingResultController {
@PostMapping("/v2/member")
public String createMemberV2(
// 1. @ModelAttribute 뒤에 2. BindingResult가 위치한다.
@ModelAttribute MemberCreateRequestDto request,
BindingResult bindingResult,
Model model
) {
System.out.println("/V2/member API가 호출되었습니다.");
// BindingResult의 에러 출력
List<ObjectError> allErrors = bindingResult.getAllErrors();
System.out.println("allErrors = " + allErrors);
// Model에 저장
model.addAttribute("point", request.getPoint());
model.addAttribute("name", request.getName());
model.addAttribute("age", request.getAge());
return "complete";
}
=> BindingResult를 이용해서 에러를 검증하는 것은 Controller의 크기가 커지고, 단일 책임 원칙에 위반 ⭐️
=> Bean Validation을 사용하자 ~!
@Valid, @Validated 차이점 (너무 궁금했음)
- @Valid - 자바 표준 / @Validated - 스프링 표준
- @Validated는 Group Validation이나 Controller이외 계층에서 검증 가능
- @Valid -> MethodArgumentNotValidException 발생
- @Validated -> ConstraintViolationException 발생
Validator
- 라이브러리를 추가하면 Bean Validator가 @Valid, @Validated를 통하여 검증할 수 있게 해줌
- @Valid, @Validated를 사용하는 것 = Validator 사용
- Bean Validator는 바인딩에 실패한 필드는 Bean Validation을 적용하지 않음 (BindingResult에 에러 추가하고 값은 null값)
- 바인딩 실패 : String타입에 Integer입력한 것
에러 메세지 수정법
1. message 속성 사용
@NotBlank(message = "메세지 수정 가능")
private String stringField;
2. Object Error : 로직으로 구현
public class BeanValidationController {
@PostMapping("/object-error")
public String objectError(
@Validated @ModelAttribute OrderRequestDto requestDto,
BindingResult bindingResult
) {
// 합이 10000원 이상인지 확인
int result = requestDto.getPrice() * requestDto.getCount();
if (result < 10000) {
// Object Error
bindingResult.reject("totalMin", new Object[]{10000, result}, "총 합이 10000 이상이어야 합니다.");
}
return "성공";
}
}
Bean Validation 충돌 : Dto 나누기
- Dto를 나눌 것 (요청 Dto, 수정 Dto)
- 명확하고, 유지보수성 측면에서 좋음
@ModelAttribute, @RequestBody의 차이
@ModelAttribute
- 파라미터, Form Data(x-www-urlencoded)를 다룰 때 사용
- 필드 단위로 변환하기 때문에 특정 필드 변환 바인딩이 실패해도 Controller를 호출하고 나머지 필드는 Validation 적용함
@RequestBody
- HTTP Body Data를 Object로 변환
- 객체 단위로 적용되기에 변환이 완벽하게 되야만 Validation 적용
- 변환이 실패된다면 Controller를 미호출