Assert를 커스텀custom해보자
커스텀한 애노테이션을 통해 파라미터 검증하기을 통해 파라미터 검증하는 방법을 알아보았다. 하지만 이는 @RequestBody이나 @ModelAttribute에나 적용이 가능하기 때문에 Request로 들어오는 파라미터만 아닌 일반 파라미터에 대한 것도 파라미터 검증이 필요할 때가 있다.
이 때 AOP를 사용해도 되지만 너무 어렵다는 단점이 있다. 간단하게 파라미터 검증을 할 수 있는 방법이 있다.
바로 Assert이다

1. 기본 Assert

Assert 스프링공식문서를 확인하거나 직접 코드를 import해서 확인해보면 기본의 Assert 사용법이 잘 나와있다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class Assert {
  ...

  public static void isTrue(boolean expression, String message) {
      if (!expression) {
          throw new IllegalArgumentException(message);
      }
  }

  ...
}

Integer i = 0;
Assert.isTrue(i > 0, "The value must be greater than zero"); //실제사용코드
  • 위 코드에서 6번째 라인을 보면 IllegalArgumentException을 던지는 것을 확인할 수 있다.
  • 하지만 IllegalArgumentException이 아니라 자기가 원하는 Exception을 던지고 싶을수 있다.

2. 기본 Assert 튜닝

2.1 원하는 Exception 던지기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class CustomAssert extends Assert {

  ...

  public static void notNull(@Nullable Object object, String message, final Class<? extends RuntimeException> exceptionClass) {
      if (object == null) {
          throw throwException(message, exceptionClass);
      }
  }

  ...
  private static RuntimeException throwException(String message, final Class<? extends RuntimeException> exceptionClass) {
      try {
          return exceptionClass.getDeclaredConstructor( String.class ).newInstance( message );
      } catch (Exception e) {
          e.printStackTrace();
          throw new AssertException("예외 처리 중 오류가 발생했습니다. "+e.getMessage());
      }
  }
}

public class ValidateKeyException extends RuntimeException{
  public ValidateKeyException() {}
  
  public ValidateKeyException(String message) {
      super(message);
  }
  
  public ValidateKeyException(String message, Throwable cause) {
      super(message, cause);
  }
  
  public ValidateKeyException(Throwable cause) {
      super(cause);
  }
}

2.2 다음과 같이 사용

1
2
3
4
5
6
7
8
9
10
11
  @Transactional
  public boolean verifyEmail(String key) {

      //Redis를 이용하여 Key값의 Value값을 통해 사용자 메일 정보를 가져온다
      RequestMail requestMail = redisService.getMailData(key,RequestMail.class);

      // 유효한 Key가 아닐경우 email = null
      CustomAssert.notNull(requestMail,"유효한 키가 아닙니다", ValidateKeyException.class);

      ...
  }
  • 여기서의 8번째줄의 ValidateKeyException는 직접 만든 Exception이며 이 Exception은 RuntimeException을 상속받아야 한다
  • 12번째의 AssertException 또한 직접 만든 Exception이다
  • throwException()ValidateKeyException을 인스턴스화시켜준다

3. 심화

  • 사용자의 ID 입력값을 확인하여 패턴에 맞는 ID가 들어오는지 검증해보자
  • 패턴은 다음과 같다
    • [a-zA-Z0-9]{6,20}
    • 영어나 숫자로만 6-20글자

3.1 CustomAssert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class CustomAssert extends Assert {

    private static final String NUM_ALPHA_PATTERN = "^[a-zA-Z0-9]{6,20}$";

    public static void isLoginPattern(String object, String message) {
        isMatched(object, NUM_ALPHA_PATTERN, message, PatternException.class);
    }

    public static void isMatched(String object, String pattern, String message, Class<PatternException> patternExceptionClass) {
        if(object == null || "".equalsIgnoreCase(object)) return;

        if(!object.matches(pattern)) {
            throw throwException(message, patternExceptionClass);
        }
    }

    private static RuntimeException throwException(String message, final Class<? extends RuntimeException> exceptionClass) {
        try {
            return exceptionClass.getDeclaredConstructor( String.class ).newInstance( message );
        } catch (Exception e) {
            e.printStackTrace();
            throw new AssertException("예외 처리 중 오류가 발생했습니다. "+e.getMessage());
        }
    }
}

public class PatternException  extends AccountStatusException {

    public PatternException() {
        super("패턴 오류");
    }
    public PatternException(String message) {
        super(message);
    }
    public PatternException(String message, Throwable cause) {
        super(message, cause);
    }

}
  • 27번째 라인의 PatternExceptionAccountStatusException을 상속한다
  • 여기서는 Spring Security를 사용하기때문에 AccountStatusException를 상속했지만 Spring Security을 사용하지않는 일반적인 경우에는 RuntimeException을 상속받는다
    public class PatternException  extends RuntimeException {
    
  • 패턴에 맞지않는 값이 들어올 경우 6번째 라인에 의해 PatternException이 던져진다

3.2 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException {

    //파라미터 검증
    CustomAssert.isLoginPattern(loginId, "아이디나 비밀번호 패턴이 맞지않습니다");

    LoginUserDto loginUserDto = this.findUserByLoginId(loginId);


    ...

    return member;
}

댓글 쓰기