[TIL] 시간 범위 검증 어노테이션

YJin·2025년 7월 15일

[내배캠 Spring 6기_TIL]

목록 보기
55/56

프로젝트 작업 중 시간 범위 검증 어노테이션이 필요할 것 같아 따로 만들었다.


  • 단위는 ChronoUnit 에서 지원하는 단위라면 모두 사용 가능하다. (초분시, 일, 월 etc.)
  • 현재를 기준으로 min ~ max 를 검사
  • 현재 이전으로 하고 싶다면 min or max 값에 음수 값을 넣어주면 된다.
  • 현재는 LocalDate, LocalDateTime에 대해서만 검증 가능하므로 다른 시간 타입을 사용하고 싶다면 추가하면 된다.
  • initialize 에서 min ≤ max 를 검사하나, 컴파일 타임에 검사되지 않는 것이 아쉽다. 개선하고 싶으나 이건 컴파일러 플러그인을 새로 구현해야 한다고 하니 패스.



DateRange

@Target(value = {ElementType.FIELD, ElementType.PARAMETER})
@Retention(value = RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {DateRangeValidator.class})
public @interface DateRange {
	String message() default "날짜는 현재 시간 기준 {min} {unit}부터 {max} {unit}까지 입력 가능합니다.";

	Class<?>[] groups() default {};

	Class<? extends Payload>[] payload() default {};

	int min() default 0;

	int max() default 7;

	ChronoUnit unit() default ChronoUnit.DAYS;
}



DateRangeValidator

public class DateRangeValidator implements ConstraintValidator<DateRange, Object> {
	private DateRange annotation;
	private int min;
	private int max;
	private ChronoUnit unit;

	@Override
	public void initialize(DateRange constraintAnnotation) {
		this.annotation = constraintAnnotation;
		this.min = constraintAnnotation.min();
		this.max = constraintAnnotation.max();
		this.unit = constraintAnnotation.unit();

		if (min > max) {
			throw new ConstraintDeclarationException(
				String.format("@DateRange 설정 오류: min(%d)이 max(%d)보다 클 수 없습니다.", min, max));
		}
	}

	@Override
	public boolean isValid(Object value, ConstraintValidatorContext context) {
		if (value == null) {
			return true;
		}

		if (value instanceof LocalDate) {
			return validLocalDate((LocalDate)value);
		} else if (value instanceof LocalDateTime) {
			return validLocalDateTime((LocalDateTime)value);
		} else {
			return false;        // 지원하지 않는 타입
		}
	}

	private boolean validLocalDate(LocalDate value) {
		// 시간이 min unit ~ max unit 내의 날짜인지 검사
		LocalDate now = LocalDate.now();
		LocalDate minBound = now.plus(min, unit);
		LocalDate maxBound = now.plus(max, unit);

		return (value.isAfter(minBound) || value.isEqual(minBound))
			&& (value.isBefore(maxBound)) || value.isEqual(maxBound);
	}

	public boolean validLocalDateTime(LocalDateTime value) {
		// 시간이 min unit ~ max unit 내의 날짜인지 검사
		LocalDateTime now = LocalDateTime.now();
		LocalDateTime minBound = now.plus(min, unit);
		LocalDateTime maxBound = now.plus(max, unit);

		return (value.isAfter(minBound) || value.isEqual(minBound))
			&& (value.isBefore(maxBound) || value.isEqual(maxBound));

	}
}
profile
백엔드 개발도 락이다

0개의 댓글