1) 어느 dependency 소속일까?: spring boot starter validation
2) 어떤 어노테이션과 같이 쓸까?
@Email : 이메일 형식 검사@Size: 값이 min과 max사이에 속해야함 @Size(max = 10, min = 1)@NotBlank: 값이 비어있으면 X @NotNull: Null값이 아니어야 한다.@PositiveOrZero : 0이거나 양수인 값이다.@Future: 날짜 필드가 미래여야 함을 지정@FutureOrPresent: Now 거나 미래의 날짜, 시간이어야 한다.@Max(9999) Min : 최대 9999까지 허용 (최소도 마찬가지)@Pattern: 비밀번호 유효성 검사에 주로 사용3) 어노테이션들은 어디에다 붙여야 할까?
@Valid: Controller/@RequestBody @Valid DTO@NotNull, @Pattern...: DTO field 위@Valid는 자바 표준 스펙이고 @Validated는 스프링에서 제공하는 어노테이션@Validated를 통해 그룹 유효성 검사나 Controller가 아닌 다른 계층에서 유효성 검증 가능 @Valid는 MethodArgumentNotValidException 예외를, @Validated는 ConstraintViolationException 예외를 발생시킨다.isNumberisAlpha1단계. 회원가입할 때 조건!
이름이 NotBlank
비밀번호 최소 8자리 이상
이메일 주소 이메일 형식에 맞게
조건에 만족하지 않는 필드를 모두 보여준다.
그럼 위 내용들을 에러처리하고 싶은 데 어떻게 해야할까?
@PostMapping("/join")
public void join(@RequestBody @Valid UserJoinReq userJoinReq, Errors errors){
//userService.join(userJoinReq);
for (FieldError fieldError: errors.getFieldErrors()){
System.out.println("Error field : "+fieldError.getField());
}
System.out.println("회원가입 완료");
}

Spring validator가 검증/검사 하다가 오류가 발생하면, 오류가 난 특정 필드 or 객체를 Errors(BindingResult)에 담음!
BindingResult와 Errors중 어느것이 더 구체적일까?
BindingResult extends Errors
: Errors를 상속하는 BindingResult가 더 구체적이다.
=> Errors를 상속받았기 때문에 Errors 인터페이스를 통해서 BindingResult의 유효성 검증 에러 관련 멤버에 접근 가능!
cf. BindingResult는 "데이터 바인딩 에러" + "유효성 검증 에러"
ex. @ModelAttribute Model model
그래서 유효성 검사 중 에러가 나면 어디에 담기는걸까?
AbstractBindingResult의 필드에 addError메소드를 통해 저장함

@PostMapping("/join")
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> join(@RequestBody @Valid UserJoinReq userJoinReq, Errors errors){
//userService.join(userJoinReq);
Map<String, String> errormessages=userService.handleErrors(errors);
//Map => new ApiError(errorMessages);
return errormessages;
}
@Component
public class UserService {
public Map<String, String > handleErrors(Errors errors){
Map<String, String> errorMessages=new HashMap<>();
for (FieldError fieldError: errors.getFieldErrors()){
String errorField=fieldError.getField();
String errorMessage=fieldError.getDefaultMessage();
errorMessages.put(errorField, errorMessage);
}
return errorMessages;
}
}

Controller
@PostMapping("/signup")
public ResponseEntity<String> signup(@RequestBody @Valid UserJoinReq userJoinReq) {
userService.join(userJoinReq); // 유효성 검사를 통과한 경우 처리 로직 호출
return ResponseEntity.status(HttpStatus.CREATED).body("success"); // 성공 응답 반환
}
Global Exception
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String > handleValidationExceptions(MethodArgumentNotValidException errors) {
Map<String, String> errorMessages=new HashMap<>();
for (FieldError fieldError: errors.getFieldErrors()){
String errorField=fieldError.getField();
String errorMessage=fieldError.getDefaultMessage();
errorMessages.put(errorField, errorMessage);
}
return errorMessages;
}