백엔드 공부하기/TIL
230829 TIL : [Spring Boot] Validation 처리
개발중인 감자
2023. 8. 30. 01:46
📌 230829 TIL : [Spring Boot] Validation 처리
1. Validation?
- Spring-boot-starter-validation 을 통해 처리한다.
- 데이터에 대한 유효성 처리를 진행할 수 있다.
- 예를 들어, 데이터가 빈값이거나 공백값이면 안되게 유효성 처리를 진행한다.
- @Valid 혹은 @Validated 어노테이션을 통해 유효성을 검증한다.
- 만약, 유효성 검증에 실패했다면, MethodArgumentNotValidException 에러 발생한다.
- 어노테이션을 사용함으로써 코드의 가독성을 높일 수 있다.
2. 종류
@Size : 문자열의 길이 측정 (not int type)
@NotNull : null x
@NotEmpty : null x, “” x
@NotBlank : null “”, “ “(공백) x
@Pattern : 정규식 적용
@Max, @Min : 최댓값/최솟값
@AssertTrue/False : 별도의 logic 적용
@Vaild : 해당 logic validation 진행
= 날짜 관련 =
@Past : 과거 날짜 체크
@PastOrPresent : 오늘이거나 과거 날짜 체크
@Future : 미래 날짜 체크
@FutureOfPresent : 오늘이거나 미래 날짜 체크
3. 방법
1) gradle에 의존성 추가
2) model에 가서 유효성 검증하고자 하는 변수에 어노테이션 붙이기.
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRegisterRequest {
private String name;
private String nickName;
@NotBlank
@Size(min = 1, max = 12)
private String password;
@NotNull
@Min(1)
@Max(100)
private Integer age;
@Email
private String email;
@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "휴대폰 번호 양식에 맞지 않습니다.")
private String phoneNumber;
@FutureOrPresent //현재 혹은 미래.
private LocalDateTime registerAt;
@AssertTrue(message = "name 또는 nickName은 반드시 1개가 존재해야합니다. ")
public boolean isNameCheck() {
if (Objects.nonNull(name) && !name.isBlank()) return true;
if (Objects.nonNull(nickName) && !nickName.isBlank()) return true;
return false;
}
}
- @AssertTrue 를 통해 name 혹은 nickName 둘 중 하나만 존재해도 true를 반환하도록 해주는 검증을 구현했다.
3) 에러 메세지를 담을 Api 클래스
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class Api<T> {
@Valid //유효성 검사 진행
private T data;
private String resCode, resMsg;
private Error error;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public static class Error {
private List<String> errorMsg;
}
}
- data 변수의 유효성 검사를 진행하여 에러가 발생하면 그 에러 메세지를 담을 에러 클래스를 내부적으로 선언해주었다.
4) MethodArgumentNotValidException 에러를 처리할 ExceptionHandler 클래스 구현.
- 해당 클래스를 구현함으로써, 컨트롤러 클래스에서 에러가 발생하면 여기서 처리하도록 해준다.
package com.example.validation.exception;
@RestControllerAdvice
@Slf4j
public class ValidationExceptionHandler {
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResponseEntity<Api> validationException(
MethodArgumentNotValidException exception
) {
log.error("", exception);
var errorMsgList = exception.getFieldErrors().stream()
.map(it-> {
var format = "%s : { %s } 은 %s";
var msg = String.format(format, it.getField(), it.getRejectedValue(), it.getDefaultMessage());
return msg;
}).collect(Collectors.toList());
var error = Api.Error.builder().errorMsg(errorMsgList).build();
var errorResponse = Api.builder()
.resCode(String.valueOf(HttpStatus.BAD_REQUEST))
.resMsg(HttpStatus.BAD_REQUEST.getReasonPhrase())
.error(error)
.build();
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}
}
5) Controller 구현
- 여기서는 검증에 통과한 값들만 처리를 한다.
- 코드는 더보기란
더보기
@Slf4j
@RestController
@RequestMapping("/api/user")
public class UserApiController {
@PostMapping ("")
public Api<UserRegisterRequest> register(
@Valid
@RequestBody
Api<UserRegisterRequest> userRegisterRequest
) {
log.info("init : {}", userRegisterRequest);
/*
검증에 위배되는 값을 입력시 MethodArgumentNotValidException 에러 발생.
-> exceptionHandler 처리.
*/
var body = userRegisterRequest.getData();
Api<UserRegisterRequest> response = Api.<UserRegisterRequest>builder()
.resCode(String.valueOf(HttpStatus.OK.value()))
.resMsg(HttpStatus.OK.name())
.data(body).build();
return response;
}
}
📝 실행 결과
<보낸 요청 Body>
<결과>
<콘솔>