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를 미호출