@Valid로 @RequestBody 검증하기

gillog·2021년 9월 7일
0

Java

목록 보기
27/28

#import

@Valid 를 이용해 @RequestBody 객체 검증하기[쟈미의 devlog]

Spring Boot의 @Valid 어노테이션이 동작하지 않는 이슈[Chasing Yesterday - Jinhong]

Spring MethodArgumentNotValidException (@Valid 예외처리)[BrantHwang]

@Valid 세팅 및 사용법[Simple is Best!]


@Valid

@Valid Annotationjavax.validation에 포함된 Dependency로,

@RequestBody Annotation으로 Mapping되는 Java 객체유효성 검증을 수행하는 Annotation이다.


Dependency 추가하기

해당 글Maven을 통해 pom.xml에 추가하는 방식으로,

Dependency를 추가하는 방법을 설명한다.

pom.xml에 아래 내용을 추가하자.

<dependency>
	<groupId>org.hibernate.validator</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>6.2.0.Final</version>
</dependency>

주의사항!!

아래 groupId가 javax.validation인 dependency현재 Spring 최신 버전들에서 지원되지 않고 있다.

만약 아래
<groupId>javax.validation</groupId>pom.xml에 추가해둔 상태라면
,

<groupId>org.hibernate.validator</groupId>를 사용하자.

본인은 이것 때문에 꽤 애먹었다.


사용하기

먼저 간단하게 사용법을 알아보면,

아래 세 가지 과정을 거치면 된다.

1. 검증 객체에 Constraints Annotation 추가

유효성 검증을 원하는 객체에 아래와 같이 javax.validation.constraints에 있는 Annotation을 객체 Field에 선언해주자.

관련 Annotation에 대한 설명은 아래에서 설명

import java.util.Date;
import javax.validation.constraints.NotNull;
import project.enums.jwt.JWTRoles;

public class JWTBody {
	private String subject;
	private String issuer;
	@NotNull(message = "UserID는 필수 값입니다.")
	private String userId;
	private String platformId;
	private JWTRoles role;
	private Date expireDate;

2. Controller Parameter @RequestBody 옆에 @Valid Annotation 추가

@RequestBody를 통해 Binding하는 Controller의 method parameter에서,

유효성 검증을 원하는 해당 객체 옆@Valid Annotation을 추가하자.

import javax.validation.Valid;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import project.model.auth.JWTBody;
import project.model.common.CommonResponse;
import project.service.auth.AuthService;

@RequestMapping("tokens")
@RestController
public class TokenController {
	@SuppressWarnings("unused")
	private static final Logger LOG = Logger.getLogger(TokenController.class);
	@Autowired
	private AuthService authService;

	@PostMapping("")
	public ResponseEntity<CommonResponse<String>> createToken(@RequestBody @Valid JWTBody jwtBody) {
		return ResponseEntity.ok(new CommonResponse<>(authService.createJWT(jwtBody)));
	}
}

3. MethodArgumentNotValidException Exception Handler 추가

이제 유효성 검증을 원하는 객체에 선언Constraint Annotation에 따라서,

유효성 검증에 실패할 경우 MethodArgumentNotValidException이 발생하는데 이 Exception을 Handling해주자.

본인은 현재 ControllerAdvice를 통해 Handling 하는중이다.

import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class ExceptionController {
	@SuppressWarnings("unused")
	private static final Logger LOG = Logger.getLogger(ExceptionController.class);

	@ExceptionHandler(MethodArgumentNotValidException.class)
	protected Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
		return e.getBindingResult().getAllErrors();
	}
}

Error 내용 확인해보기

이제 해당 객체에 유효하지 않은 @RequestBody를 요청하면,

아래와 같이 유효성을 설정한대로 Error 내용을 확인할 수 있다.


Constraints Annotation

좀 더 유연한 유효성 검증을 위해 Validator에서 제공하는,

Constraints Annotation에 대해 설명하면 검증 유형 별로 아래와 같다.


문자열 검증(@NotNull, @NotEmpty, @NotBlank, @Null)

@NotBlank

null 이 아닌 값으로, 공백이 아닌 문자를 하나 이상 포함한다.

반드시 값이 존재하고 공백 문자를 제외길이가 0보다 커야 한다.

@NotBlank(message = "이름은 필수 값 입니다.")
private String name;

@NotEmpty

null 이거나 empty(빈 문자열)가 아닌 값.

반드시 값이 존재하고 길이 혹은 크기가 0보다 커야한다.

@NotEmpty(message = "이름은 필수 값 입니다.")
private String name;

@NotNull

null 이 아닌 어떤 타입이든 수용 한다.

반드시 값이 있어야 한다.

@NotNull(message = "이름은 필수 값 입니다.")
private String name;

@Null

어떤 타입이든 수용하며 Null이어야 한다.


여기서 문자열에 Null을 허용하지 않는,

@NotNull, @NotEmpty, @NotBlank를 비교해보면 아래와 같다.

Annotationnull""" "
@NotNullInvalidValidValid
@NotEmptyInvalidInvalidValid
@NotBlankInvalidInvalidInvalid

최대 최소 검증(@Max, @Min, @DecimalMax, @DecimalMin)

@Max

지정된 최대 값보다 작거나 같아야 한다.

Annotation에 필수적으로 int value로 max 값을 지정한다.

@Max(value = 99999)
private int max;

@Min

지정된 최소 값보다 크거나 같아야 한다.

Annotation에 필수적으로 int value로 min 값을 지정한다.

@Min(value = 1)
private int min;

@DecimalMax

지정된 최대 값보다 작거나 같아야 한다.

Annotation에 필수적으로 String value로 max 값을 지정한다.

@DecimalMax(value = "999999")
private BigInteger decimalMax;

@DecimalMin

지정된 최소 값보다 크거나 같아야 한다.

Annotation에 필수적으로 String value로 min 값을 지정한다.


@DecimalMin(value = "1")
private BigInteger decimalMin;

해당 Annotation들이 적용 가능한 Type은,

BigDecimal, BigInteger, CharSequence, byte, short, int, long과 이에 대응하는 Wrapper Class 들이다.

double, floatRounding Error 때문에 지원 X

null도 valid로 간주된다.


여기서 @DecimalMax, @DecimalMin과,

@Max, @Min의 차이범위 값의 차이이다.

@.*?max(value = String or Integer)

value property에 String을 사용하느냐,

Integer를 사용하느냐에 따라 범위 값이 현저히 달라지기 때문이다.


범위 값 검증(@Positive, @PositiveOrZero, @Negative, @NegativeOrZero)

@Positive

양수 값만 가능하다.

@Positive
private int positive;

@PositiveOrZero

양수 거나 0인 값만 가능하다.

@PositiveOrZero
private int positiveOrZero;

@Negative

음수 값만 가능하다.

@Negative
private int negative;

@NegativeOrZero

음수 거나 0인 값만 가능하다.

@NegativeOrZero
private int negativeOrZero;

해당 Annotation들이 적용 가능한 Type은,

BigDecimal, BigInteger, CharSequence, byte, short, int, long, double, float과 이에 대응하는 Wrapper Class 들이다.

null도 valid로 간주된다.


시간 검증(@Future, @FutureOrPresent, @Past, @PastOrPresent)

@Future

현재보다 미래의 날짜, 시간만 가능하다.

@Future
private Date future;

@FutureOrPresent

현재거나 미래의 날짜, 시간만 가능하다.

@FutureOrPresent
private Date futureOrPresent;

@Past

현재보다 과거의 날짜, 시간만 가능하다.

@Past
private Date past;

@PastOrPresent

현재거나 과거의 날짜, 시간만 가능하다.

@PastOrPresent
private Date pastOrPresent;

해당 Annotation들이 적용 가능한 Type은 아래와 같다.

  • java.util.Date java.util.Calendar
  • java.time.Instant java.time.LocalDate
  • java.time.LocalDateTime java.time.LocalTime
  • java.time.MonthDay java.time.OffsetDateTime
  • java.time.OffsetTime java.time.Year
  • java.time.YearMonth java.time.ZonedDateTime
  • java.time.chrono.HijrahDate
  • java.time.chrono.JapaneseDate
  • java.time.chrono.MinguoDate
  • java.time.chrono.ThaiBuddhistDate

null도 valid로 간주된다.


현재의 기준ClockProvider의 가상 머신에 따라 정의하며,

필요한 경우 default time zone을 적용한다.


이메일 검증(@Email)

@Email

@가 포함된 올바른 이메일 양식만 가능하다.

@Email
private String email;

자릿수 검증(@Digits)

@Digits

허용 범위 내의 숫자 값만 가능하다.

@Digitsint integer property허용되는 최대 정수 자릿 수를 설정하고,

int fraction property허용되는 최대 소수 자릿 수를 설정한다.

@Digits(integer = 3, fraction = 4)
private Integer digits;

해당 Annotation이 적용가능한 Type은,

BigDecimal, BigInteger, CharSequence, byte, short, int, long과 이에 대응하는 Wrapper Class이다.

null도 valid로 간주된다.


Boolean 검증(@AssertTrue, @AssertFalse)

@AssertTrue

항상 True인 값이어야 한다.

@AssertTrue
private boolean true;

@AssertFalse

항상 False인 값이어야 한다.

@AssertFalse
private boolean false;

해당 Annotation이 적용가능한 Type은,
Boolean, boolean 이다.


크기 검증(@Size)

max, min property로 설정한 경계(포함)에 있는 크기의 값이어야 한다.

int max property보다 크기가 작거나 같아야 하고,

int min property보다 크거나 같아야 한다.

해당 property 들은 필수

@Size(max = 3, min = 1)
private String size;

해당 Annotation이 적용가능한 Type은,

CharSequence (character sequence의 length),
Collection (collection 의 size),
Map (map 의 size),
Array (array 의 length) 이다.

null도 valid로 간주된다.


정규식 검증(@Pattern)

지정한 정규식에 대응되는 문자열 값만 가능하다.

String regexp는 필수 property로 정규식 문자열을 지정한다.

Java Pattern Pacakge Convension을 따름

@Pattern(regexp = "^.*?([0-9]+)[\?|!]$")
private String pattern;

해당 Annotation이 적용가능한 Type은,

CharSequence이다.

null도 valid로 간주된다.

정규 표현식 설명

profile
🚀 기록보단 길록을 ⭐

0개의 댓글