Validation in Spring(유효성 검사) - 3

Gihongg·2024년 3월 23일

Spring Boot

목록 보기
13/24

Custom Validation

지금까지는 단일조건(예외)를 잡아냈지만 이번에는 복합조건을 잡아내도록 한다

애매한 부분

만약 아래 두가지 요소 둘 중 하나만 있어도 된다면?

  1. private String name
  2. private String nickName

이러한 조건을 제공하는 어노테이션은 없다.

그렇기 때문에 커스텀한 Validation이 필요하다.

전에 만들었던 UserRegisterRequest.java파일에 private String name 변수가 있을텐데 그 아래에 private String nickName을 추가해준다.

그리고 이름과 별명이 들어있는지 판별하는 메서드를 하나 만들어준다.
작성하면 아래와 같다.

package org.example.validation.model;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.xml.namespace.NamespaceContext;
import java.time.LocalDateTime;
import java.util.Objects;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRegisterRequest {

//    @NotNull // !=null
//    @NotEmpty // ! = null && name != ""
//    @NotBlank // !=null && name != "" && name != "  "
    private String name;

//    @NotBlank
    private String nickName;


    @Size(min=1,max = 12)
    @NotBlank
    private String password;

    @NotNull //문자가 아니라서 NotBlank나 NotEmpty를 넣어줄 수 없다.
    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 or nickname should exist at least one") //리턴값이 트루라면 메시지 설정
    public boolean isNameCheck(){	

     if(Objects.nonNull(name)&&!name.isBlank()){
         return true;
     }

     if(Objects.nonNull(nickName)&&!nickName.isBlank()){
            return true;
     }
        return false;
    }

}

이제 닉네임과 이름 둘다 판별한다.

아래의 전화번호를 검사할 때 사용한 정규식 부분을 새로운 어노테이션을 만들어 검사하도록 해보자.

@Pattern(regexp = "^\d{2,3}-\d{3,4}-\d{4}$",message = "휴대폰 번호 양식에 맞지 않습니다.")
private String phoneNumber;

PhoneNumberValidator.java 파일을 하나 만들어주고 그안을 아래와 같이 작성한다.

package org.example.validation.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.example.validation.annotation.PhoneNumber;

import java.util.regex.Pattern;

public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber,String> {

    private String regxp; // 정규식 패턴을 저장할 변수

    @Override
    public void initialize(PhoneNumber constraintAnnotation) {
        this.regxp= constraintAnnotation.regexp(); // PhoneNumber 어노테이션에서 정의된 정규식 패턴을 가져와 변수에 저장합니다.
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        // validation이 실행될 때 실행되는 메서드입니다. constraintValidatorContext를 사용하여 유효성 검사를 수행합니다.

        boolean result = Pattern.matches(regxp, s); // 주어진 문자열 s가 정규식 패턴 regxp와 일치하는지 여부를 판별합니다.

        return result; // 결과를 반환합니다.
    }
}

이 파일에서는 검증할 규칙을 지정해주고, 결과를 반환한다.

이제 이 규칙을 가지고 어노테이션을 만들어 보자

annotation.java 파일을 만들고 아래와 같이 작성한다.

package org.example.validation.annotation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import org.example.validation.validator.PhoneNumberValidator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = {PhoneNumberValidator.class}) //어떤 클래스로 검증할 것인지를 알려주는 어노테이션
@Target({ElementType.FIELD})
//이 어노테이션이 적용될 대상을 지정합니다. 여기서는 ElementType.FIELD로 지정되어 있으므로, 이 어노테이션은 필드에만 적용될 수 있습니다. 다른 가능한 값으로는 METHOD, PARAMETER, TYPE, 등이 있습니다
@Retention(RetentionPolicy.RUNTIME)
//이 어노테이션의 유지 정책을 지정합니다. 여기서는 RetentionPolicy.RUNTIME으로 지정되어 있으므로, 이 어노테이션은 런타임 시에도 유지됩니다. 이것은 리플렉션을 통해 어노테이션 정보에 접근할 수 있음을 의미합니다. 다른 유지 정책으로는 SOURCE (소스 코드까지만 유지), CLASS (컴파일된 클래스 파일까지 유지) 등이 있습니다.
public @interface PhoneNumber {
    String message() default "핸드폰번호 양식에 맞지 않습니다. ex) 000-0000-0000";

    String regexp() default "^\\d{2,3}-\\d{3,4}-\\d{4}$";

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

}

그리고 UserRegisterRequest의 PhoneNumber에 이 어노테이션 @PhoneNumber을 붙여주면 완성이다.

profile
전천후 개발자

0개의 댓글