관리 메뉴

ballqs 님의 블로그

[Spring] @Valid 유효성 검사 , @ExceptionHandler , MethodArgumentNotValidException 본문

코딩 공부/Spring

[Spring] @Valid 유효성 검사 , @ExceptionHandler , MethodArgumentNotValidException

ballqs 2024. 8. 12. 17:10

받아오는 Parameter에 따라 유효성 검사를 할수 있다는 기능이 있다고 해서 알아보았다.

@Valid 라는 기능이다. 이건 어떻게 사용하는지 작성해보겠다.


의존성 추가

build.gradle 파일을 찾아서 추가하자.

implementation 'org.springframework.boot:spring-boot-starter-validation'

사용방법

@RestController
@RequestMapping("/api/schedule")
public class ScheduleController {

    private final ScheduleService scheduleService;

    @Autowired
    ScheduleController(ScheduleService scheduleService) {
        this.scheduleService = scheduleService;
    }

    @PostMapping("/create")
    public ResponseEntity<ResponseDto<ScheduleSelectDto>> createSchedule(@Valid @RequestBody ScheduleInsertDto scheduleInsertDto) {
        return ResponseEntity
                .status(HttpStatus.OK)
                .body(new ResponseDto<>(HttpStatus.OK.value(), scheduleService.createSchedule(scheduleInsertDto) , "성공적으로 등록완료했습니다."));
    }
}

 

createSchedule 메서드를 살펴보면 @Valid 와 @RequestBody 가 쓰여져 있다.

@RequestBody는 클라이언트에서 body에 담아서 요청보낸 것들이라는 의미이다.

@Valid는 받은 값들의 유효성 검사를 하겠다는 의미이다.

 

ScheduleInsertDto에는 @Valid가 쓰여져있어서 아래와 같이 작성되었다.

만약! @Valid가 없는 곳에서 ScheduleInsertDto에 담는 것이라면 @Size , @NotNull , @NotBlank 등이 동작하지 않는다.

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;

@Getter
public class ScheduleInsertDto {
    @Size(max=200, min=1)
    @NotNull
    private String content;
    @NotNull
    private Long managerIdx;
    @NotBlank
    private String pw;
}

 


종류

빈 문자열 = ""

공백 = " "

Annotation Comment
@NotNull 빈 문자열, 공백은 허용하지만 Null은 허용안함
@NotBlank Null, 빈 문자열, 공백 모두 허용하지 않음! 한 글자라도 쓰여져 있어야 함
@NotEmpty 공백은 허용하지만 Null과 빈 문자열은 허용하지 않음
@Size(max=,min=) 길이를 제한한다. min이 최소값이고 max가 최대값
@Email 이메일 형식을 검사
@Pattern(regexp=) 정규식 검사
@Null Null만 허용
@Max(value=) value 이하의 값만 허용
@Min(value=) value 이상의 값만 허용
@Positive 값을 양수로 제한
@PositiveOrZero 값을 양수와 0만 가능하도록 제한
@Negative 값을 음수로 제한
@NegativeOrZero 값을 음수와 0만 가능하도록 제한
@Future 현재 시간보다 미래의 날짜 , 시간이어야 함
@FutureOrPresent 현재 시간이거나 미래의 날짜 , 시간이어야 함
@Past 현재 시간보다 과거의 날짜 , 시간이어야 함
@PastFutureOrPresent 현재 시간이거나 과거의 날짜 , 시간이어야 함

 


MethodArgumentNotValidException 에러

@Valid 유효성 검사에 의해 발생되는 에러이다.

해당 에러는 어떤 값을 가져오는지 알기 위해 @ExceptionHandler 라는 어노테이션을 사용했다.

 

@ExceptionHandler

이 Annotation은 Controller에서만 사용이 가능하다고 한다. Service나 Repository에서 에러를 발생하면 Controller로 와서 해당 메서드에 들어가 실행문을 실행한다.

예를 들어 아래와 같이 동작한다.

public ScheduleSelectDto selectSchedule(Long idx) {
    Schedule schedule = scheduleRepository.findById(idx);
    if (Objects.nonNull(schedule)) {
        return new ScheduleSelectDto(schedule);
    } else {
        throw new NullPointerException("존재하지 않는 일정 정보입니다.");
    }
}

 

throw new NullPointerException() 를 통해 Controller에 있는 @ExceptionHandler가 선언되어 있는 곳을 찾아간다.

@ExceptionHandler(value = NullPointerException.class)
public ResponseEntity<ResponseDto<String>> nullPointerHandle(NullPointerException e) {
    // 실행 내용...
    return null;
}

 

@ExceptionHandler 에는 value값을 작성할 수 있는데 온갖 예외처리 부분을 작성해 넣을 수 있다.

그러면 value에 작성되어 있는 에러가 발생하면 해당 메서드로 들어와 내용물을 실행하게 되는 것이다.

 

 

MethodArgumentNotValidException 또한 @ExceptionHandler를 통해 작성되었고 내부가 어떤 내용이 있는지 살펴보자.

@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity<ResponseDto<String>> methodArgumentNotValidHabdle(MethodArgumentNotValidException e) {
    List<String> msgList = new ArrayList<>();
    for (int i = 0; i < e.getBindingResult().getFieldErrors().size(); i++) {
        String msg = e.getBindingResult().getFieldErrors().get(i).getField() + "는 " + e.getBindingResult().getFieldErrors().get(i).getDefaultMessage();
        msgList.add(msg);
    }
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseDto<>(HttpStatus.BAD_REQUEST.value() , "" , String.join(" , " , msgList)));
}

 

 

e 라는 변수를 통해 내용을 살펴보니 BindingResult가 있다.

 

 

 

깊게 파고 들어오니까 저부분이 List라는 것을 알수 있었고 그래서 size() 함수와 get()이 사용이 가능했던것이다.

 

getDefaultMessage()

기본 메세지만 가져오고 싶을 때

e.getBindingResult().getFieldErrors().get(i).getDefaultMessage()

 

getField()

필드 명을 가져오고 싶을 때

e.getBindingResult().getFieldErrors().get(i).getField()

 

이 이외에 자세하게 다룰라면 완전 뜯어봐야하는데 내가 그럴 재량이 아직 부족하다...

좀 더 자세하게 다룬 글은 여기에 있었다.

https://my-codinglog.tistory.com/56#check2

 

[예외처리] @Valid 예외처리에 사용되는 BindingResult 객체는 무엇일까?

이번 글에서는 @Valid 에 의해 발생되는 MethodArgumentNotValidException.class 에 속하는 'BindingResult' 에 대해 알아보고자 한다. 문제 상황, 찾아보게 된 계기 - MethodArgumentNotValidException 에 속하는 BindingResult

my-codinglog.tistory.com

나중에 자주 들릴 예정이라 링크만 첨부 해두자!


마무리

코드를 작성하면서 진짜 많이 어려웠고 Spring은 이제 시작이라는 생각이 든다.

모르는걸 점점 블로그 글로 메모하듯이 적어두자...