User.java
<code />
@Size(min=6,max=6)
private String reqYearMonth; //yyyyMM

근데 그냥 6글자 넣어도 가능하다...
다른 방법으로 해보자
User.java
<java />
@AssertTrue(message = "yyyyMMdd의 형식에 맞지 않습니다")
public boolean isReqYearMonthValidation(){ //(3)boolean 하려면, 메소드명 앞에 is 붙여줘야 함
System.out.println("Assert True Call"); //(2)호출이 잘 되는지 확인하기 위해 콘솔프린트
this.reqYearMonth=getReqYearMonth()+"01"; //(1)dd까지 붙이기 위해 사전작업 한것이라고 하는데..
//파싱이 잘 되면 true, 안되면 false
try {LocalDate localDate= LocalDate.parse(this.reqYearMonth, DateTimeFormatter.ofPattern("yyyyMMdd"));
}catch (Exception e){
return false;}
return true;
}

하지만 위와 같이 코드를 짜버리면,
계속 중복코드들이 발생한다.
이것을 해결하는 방법은
우리가 직접 어노테이션을 만드는 것이다!
직접 어노테이션을 만들기 위해,
User.java의 Email 어노테이션을 클릭해서 훔쳐보자

annotation 패키지를 만들고,
YearMonth 어노테이션을 만든다.
그리고 Email 어노테이션의 형식을 가져온다.
<java />
package com.example.validation.annotation;
import javax.validation.Constraint;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {
String message() default "{javax.validation.constraints.Email.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}

Pattern 어노테이션에서도 정규식 받는 부분 가져온다.
<code />
package com.example.validation.annotation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {
String message() default "{javax.validation.constraints.Email.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
String pattern(); //정규식 받는 부분 가져와놓고선 pattern으로 바꿔버림
}
어노테이션 직접 만들었으니
직접 만든 어노테이션 붙여주기
<code />
@YearMonth(pattern="yyyyMM")
private String reqYearMonth; //yyyyMM
validator 패키지 만들고
YearMonthValidator.java만들기
<code />
package com.example.validation.validator;
import com.example.validation.annotation.YearMonth;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class YearMonthValidator implements ConstraintValidator<YearMonth,String> {//첫 번째로 우리가 원하는 어노테이션, 두 번째로 들어가는 값 지정
//처음으로 확인해야 하는 값은
private String pattern;
@Override
public void initialize(YearMonth constraintAnnotation) { //초기화
this.pattern=constraintAnnotation.pattern(); //이 패턴에 대해 정상적으로 잘 들어갔는지 확인할 것
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) { //검사할 값이 들어옴
//yyyyMM
//패턴 잘 들어갔는지 검사해보자
//파싱이 잘 되면 true, 안되면 false
try {
LocalDate localDate= LocalDate.parse(value+"01", DateTimeFormatter.ofPattern("yyyyMMdd"));
//YearMonth를 가져와야 하는데 우리가 가져와야할 값은 value, +"01" 해줘야 dd까지 나옴
//yyyyMMdd까지 해줘야하는데, dd를 1~31까지 임의의 수를 넣을 수 있지만 그냥 기본으로 01넣어주는 것임
}catch (Exception e){
return false;}
return true;
}
}
User.java에서
@Assert 없애버린다
<code />
package com.example.validation.dto;
import com.example.validation.annotation.YearMonth;
import javax.validation.constraints.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class User {
@NotBlank
private String name;
@Max(value=90, message="valid 어노테이션에는 어느 곳에나 메세지를 넣을 수 있습니다")
private int age;
@Email
private String email;
@Pattern(regexp="^\\d{2,3}-\\d{3,4}-\\d{4}$", message="핸드폰 번호의 양식과 맞지 않습니다. 01x-xxx(x)-xxxx")
private String phoneNumber;
@YearMonth(pattern="yyyyMM")
private String reqYearMonth; //yyyyMM
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getReqYearMonth() {
return reqYearMonth;
}
public void setReqYearMonth(String reqYearMonth) {
this.reqYearMonth = reqYearMonth;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
", reqYearMonth='" + reqYearMonth + '\'' +
'}';
}
}
YearMonth의 default message값 바꿔주기
<code />
package com.example.validation.annotation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {
String message() default "yyyyMM 형식에 맞지 않습니다.";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
String pattern() default "yyyyMM";
}
<code />
@Constraint(validatedBy = { })
위 어노테이션은 어떤 클래스를 가지고 우리를 검사할거냐? 라는 뜻
YearMonthValidator.class를 넘겨주었고,
<code />
package com.example.validation.annotation;
import com.example.validation.validator.YearMonthValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = {YearMonthValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {
String message() default "yyyyMM 형식에 맞지 않습니다.";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
String pattern() default "yyyyMM";
}
YearMonthValidator.java를 통해 검사할 것임.
<code />
package com.example.validation.validator;
import com.example.validation.annotation.YearMonth;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class YearMonthValidator implements ConstraintValidator<YearMonth,String> {//첫 번째로 우리가 원하는 어노테이션, 두 번째로 들어가는 값 지정
//어노테이션에 지정한 패턴 형태로 value 값이 잘 들어가있는지 검사 시작
private String pattern;
@Override
public void initialize(YearMonth constraintAnnotation) { //초기화할 때는 어노테이션에 지정된 패턴을 가지고 올 것임
this.pattern=constraintAnnotation.pattern();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) { //검사할 값이 들어옴
//yyyyMM
//위에서 들어온 패턴이 잘 지정됐는지 검사해보자
//파싱이 잘 되면 true, 안되면 false
try {
LocalDate localDate= LocalDate.parse(value+"01", DateTimeFormatter.ofPattern(this.pattern));
//YearMonth를 가져와야 하는데 우리가 가져와야할 값은 value, +"01" 해줘야 dd까지 나옴
//yyyyMMdd까지 해줘야하는데, dd를 1~31까지 임의의 수를 넣을 수 있지만 그냥 기본으로 01넣어주는 것임
}catch (Exception e){
return false;}
return true;
}
}
잘 넣었는데 에러가?

디버깅 해보자
try 아래 코드에 빨간 동그라미 넣어주고
곤충버튼 클릭
그리고 post 넣어주기


catch부분에도 빨간색 동그라미를 하고 디버그를 해봤더니,
dd를 넣어주지 않아서라고 한다. (나는 에러가 안잡혔는데 강의하시는 분께서는 에러가 잡혔다)
YearMonth.java에서
기본 패턴에 dd 붙여주고
<code />
package com.example.validation.annotation;
import com.example.validation.validator.YearMonthValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = {YearMonthValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {
String message() default "yyyyMM 형식에 맞지 않습니다.";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
String pattern() default "yyyyMMdd";
}
아하
강사분께서는 YearMonthValidator.java에
파싱하는 부분에서 text 부분에
value+"01"을 해주지 않으셨나보다.
내 코드대로 다시 실행
<code />
package com.example.validation.validator;
import com.example.validation.annotation.YearMonth;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class YearMonthValidator implements ConstraintValidator<YearMonth,String> {//첫 번째로 우리가 원하는 어노테이션, 두 번째로 들어가는 값 지정
//어노테이션에 지정한 패턴 형태로 value 값이 잘 들어가있는지 검사 시작
private String pattern;
@Override
public void initialize(YearMonth constraintAnnotation) { //초기화할 때는 어노테이션에 지정된 패턴을 가지고 올 것임
this.pattern=constraintAnnotation.pattern();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) { //검사할 값이 들어옴
//yyyyMMdd
//위에서 들어온 패턴이 잘 지정됐는지 검사해보자
//파싱이 잘 되면 true, 안되면 false
try {
LocalDate localDate= LocalDate.parse(value+"01", DateTimeFormatter.ofPattern(this.pattern));
//YearMonth를 가져와야 하는데 우리가 가져와야할 값은 value, +"01" 해줘야 dd까지 나옴
//yyyyMMdd까지 해줘야하는데, dd를 1~31까지 임의의 수를 넣을 수 있지만 그냥 기본으로 01넣어주는 것임
}catch (Exception e){
return false;}
return true;
}
}
내가 커스텀 어노테이션 @YearMonth를 만들었으니,
다른 어떠한 dto가 만들어지더라도
내가 만든 어노테이션을 붙이면 된다.
아까와 같은 @AssertTrue같은 메소드는
User.java 클래스 내 한정적인 공간에서 사용했지만
커스텀 어노테이션을 만들어줌으로써
여러 곳에서 재사용이 가능하도록 만들어주었다.
또 다른 예제!
User.java
<code />
package com.example.validation.dto;
import com.example.validation.annotation.YearMonth;
import javax.validation.constraints.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
public class User {
@NotBlank
private String name;
@Max(value=90)
private int age;
private List<Car> cars;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", cars=" + cars +
'}';
}
}
dto에 Car, Req.java 넣어줌
<code />
package com.example.validation.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotBlank;
public class Car {
@NotBlank
private String name;
@NotBlank
@JsonProperty("car_number")
private String carNumber;
@NotBlank
@JsonProperty("TYPE")
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCarNumber() {
return carNumber;
}
public void setCarNumber(String carNumber) {
this.carNumber = carNumber;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", carNumber='" + carNumber + '\'' +
", type='" + type + '\'' +
'}';
}
}
<code />
package com.example.validation.dto;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
public class Req {
@Size(min = 1 , max = 10)
@NotEmpty
private String name;
@Min(10)
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

@NotBlank를 넣었는데도
값을 안넣어서 보내도 에러가 없다
왜그렇지?
특정 클래스나 변수에 대해 검사를 하고 싶다면,
@Valid를 넣어주면 된다.

내가 만든 클래스 안에 다른 객체가 있다면, 또 그것이 오브젝트의 형태라면
@Valid를 붙여주어야
<code />
@Valid
private List<Car> cars;
그 객체 내의 어노테이션들이 정상적으로 작동할 수 있다.
(dto - Car.java)
<code />
package com.example.validation.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotBlank;
public class Car {
@NotBlank
private String name;
@NotBlank
@JsonProperty("car_number")
private String carNumber;
@NotBlank
@JsonProperty("TYPE")
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCarNumber() {
return carNumber;
}
public void setCarNumber(String carNumber) {
this.carNumber = carNumber;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", carNumber='" + carNumber + '\'' +
", type='" + type + '\'' +
'}';
}
}

1번은 재사용 불가능
2번은 재사용 가능!
'Spring' 카테고리의 다른 글
Validation 모범 사례 (0) | 2022.06.13 |
---|---|
Exception 처리 (0) | 2022.06.06 |
Validation (0) | 2022.06.01 |
Object Mapper 활용 - json 값 바꾸기 (0) | 2022.05.30 |
error: unmappable character (0x80) for encoding x-windows-949 오류 해결 (1) | 2022.05.29 |