관리 메뉴

제뉴어리의 모든것

커스텀 애노테이션 만들어서 DTO 유효성 검증하기 본문

Spring Boot

커스텀 애노테이션 만들어서 DTO 유효성 검증하기

제뉴어리맨 2022. 9. 10. 20:32

유효성 검증을 위해 스프링부트에서 지원하는 여러 애노테이션이 많지만,

해당 애노테이션이 적용되지 않는 경우도 있기 때문에 사용자 정의 애노테이션을 만들어서 DTO의 유효성 검증을 해야할때가 있다.

그럴 경우를 대비해서 직접 애노테이션을 만들어서 검증을 해보자

 

현재 상황은 DTO의 필드 중 Enum 타입이 존재할때이다.

필드 타입이 Enum일때는 @Range와 같은 애노테이션이 적용되지 않기 때문이다.

 

 

현재 아래와 같은 Entity 가 있다고 해보자.

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;

@NoArgsConstructor
@Setter
@Getter
@Entity
public class Student { //Entity 의 필드 조건은 DB 필드를 생각하여 설정

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long studentId;

    @Column(length = 10, nullable = false)
    String name;

    @Column(nullable = false, length = 20)
    Major major; // Enum타입의 필드

    @Column(nullable = false)
    Integer grade;

    @Column(nullable = false)
    Integer classs;

    @Column(nullable = false, unique = true)
    String securityNumber;

    @Getter
    public enum Major {
        DEFAULT("기본"),
        LIBERAL_ARTS("문과"),
        MATH_AND_SCIENCE("이과");

        String korName;

        Major(String korName)
        {
            this.korName = korName;
        }
    }
}

 

위에 코드에서 major라는 필드가 Enum 타입을 가지고 있다.

 

 

작업 순서는 다음과 같다.

  1. Custom Validator를 사용하기 위한 Custom Annotation을 정의한다.
  2. 정의한 Custom Annotation에 바인딩 되는 Custom Validator를 구현한다.
  3. 유효성 검증이 필요한 DTO 클래스의 멤버 변수에 Custom Annotation을 추가한다.

 

  1. Custom Annotation 생성
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD) // 해당 애노테이션은 필드에 적용
@Retention(RetentionPolicy.RUNTIME) // 해당 애노테이션은 앱이 실행중에 계속 적용됨
@Constraint(validatedBy = ValidMajorValidator.class) //ValidMajorValidator 클래스 이용하여 검증 
public @interface ValidMajor {
    String message() default "존재하지 않는 전공입니다"; //기본 메세지
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

 

2. Custom Validator 생성

import com.practice.example.student.entity.Student;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class ValidMajorValidator implements ConstraintValidator<ValidMajor, Student.Major> {
    @Override
    public boolean isValid(Student.Major value, ConstraintValidatorContext context) {

        int ordinal = value.ordinal();
        return ordinal >= 1 && ordinal <= Student.Major.values().length; // 유효성 검증 부분
    }

    @Override
    public void initialize(ValidMajor constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }
}

 

3. 생성한 애노테이션 DTO에 적용

import com.practice.example.student.entity.Student;
import com.practice.example.validator.ValidGrade;
import com.practice.example.validator.ValidMajor;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;


@Getter
@Setter
public class StudentPostDto { //DTO 의 필드 조건은 유효성 검증을 염두하여 설정


    @Pattern(regexp = "^([A-Za-z])(\\s?[A-Za-z])*$")
    @NotBlank(message = "학생의 이름은 비어있어선 안됩니다.")
    String name; //null, "", " ", 모두 불허용

    @ValidMajor //적용부분
    Student.Major major; //enum 에서 정해진 수까지만

    @ValidGrade //사용자 정의 애노테이션 사용해봄
    Integer grade; // 1 ~ 3 까지 허용

    @Range(min = 1, max = 10)
    Integer classs; // 1 ~ 10 까지만

    @Pattern(regexp = "^([0-9]{6})(-[0-9]{7})$") //우선 - 기준으로 앞에 6자 뒤에 7자 검사만 함
    @NotBlank(message = "주민번호는 비어 있어선 안됩니다.")
    String securityNumber;
}

위 코드에서 @ValidMajor가 적용된것을 알 수 있다.

 그럼 해당 필드는 1,2 의 값만 받을 수 있다.