Validation 시리즈
1. Spring Bean Validation - Basic
2. Spring Bean Validation - Custom Annotation
3. Spring Bean Validation - Test Code
앞 포스팅은 기본으로 작성되어있는 애너테이션을 사용하여, 유효성 검사를 했다. 하지만 프로젝트를 진행하다보면 여러가지 유효성을 검사해야할 일이 있다. 핸드폰 번호, 비밀번호 등 자주 쓰는 기능을 애너테이션으로 작성하여 재사용한다면 생산성은 높아질 것이다.
이러한 Custom 애너테이션을 위하여 인터페이스를 제공하고 있으며, 이를 구현하여 원하는 기능의 Validation 애너테이션을 만들 수 있다. 이번 포스팅에서 패스워드에 관련된 애너테이션을 작성해 보려고 한다
패스워드의 복잡성은 다음과 같다
- 영문, 숫자, 특수문자 중 2종류 이상을 조합하여 최소 10자리 이상 또는 3종류 이상을 조합하여 최소 8자리 이상의 길이로 구성
- 연속적인 숫자나 생일, 전화번호 등 추측하기 쉬운 개인정보 및 아이디와 비슷한 비밀번호는 사용하지 않는 것을 권고
위의 내용을 토대로 패스워드 Validation Annotation을 만들어 보자
Annotation Class
먼저 Annotation Class를 작성한다.
const val DEFAULT_PASSWORD_RULES : String =
"""^(?=.*[a-zA-Z])((?=.*\d)(?=.*\W)).{8,16}${'$'}"""
const val DEFAULT_PASSWORD_INVALID_MESSAGE =
"비밀번호는 영문, 숫자, 특수문자를 포함 8자리 이상 입력하세요"
@Constraint(validatedBy = [PasswordValidation::class])
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class PasswordValid(
val patterns : Array<String> = [DEFAULT_PASSWORD_RULES],
val message : String = DEFAULT_PASSWORD_INVALID_MESSAGE,
val groups : Array<KClass<*>> = [],
val payload : Array<KClass<out Payload>> = []
)
Bean Validation Annotation을 만들 때 message, groups, payload 를 작성해야 하는데 해당 내용은 Hibernate Validator를 참고하면된다
message : 제약조건을 위반한 경우 출력할 메세지
groups : 유효성 검사 그룹을 위한 속성, (Default 는 빈 배열이여야한다)
payload : 실제 Validator API 자체에서 사용되진 않지만 사용자 지정 Payload를 사용할 수 있게 해준다
ConstraintValidator 구현
위의 애너테이션의 실제 로직을 구현하기 위해서 ConstraintValidator 인터페이스를 구현해야한다
class PasswordValidation : ConstraintValidator<PasswordValid, String> {
lateinit var patterns : Array<String>
override fun initialize(constraintAnnotation: PasswordValid) {
patterns = constraintAnnotation.patterns
}
override fun isValid(value: String, context: ConstraintValidatorContext): Boolean {
return patterns.all { pattern ->
Pattern.compile(pattern).matcher(value).matches()
}
}
}
ConstraintValidator은 2개의 메소드가 있다
initialize() : isValid를 호출하기 전에 초기화를 해야하는 항목이 있다면 여기서 하면 된다. 위의 코드에서는 유효성 검사를 위한 패턴들을 받는다. Default는 아무것도 작동하지 않는다
isValid() : 유효성검사 로직을 작성한다. 이 메소드는 필수로 구현해 주어야 한다
위의 코드를 작성하고 패스워드 프로퍼티에 PasswordValid 애너테이션을 붙여주고 테스트를 해보자
data class Member(
...(생략)
@field:PasswordValid
val password: String
)
@WebMvcTest(controllers = [MemberController::class])
internal class MemberControllerTest {
@Autowired
private lateinit var mockMvc: MockMvc
@Nested
@DisplayName("패스워드 유효성 검사")
inner class PasswordRules {
@Test
@DisplayName("성공")
fun validPasswordRule() {
var request = """
{
"name":"브라운",
"email":"brown@email.com",
"age":19,
"count":4,
"password":"1qaz2wsx!@"
}""".trimIndent()
mockMvc.perform(
MockMvcRequestBuilders.post("/members")
.contentType(MediaType.APPLICATION_JSON)
.content(request)
)
.andExpect(status().isOk)
}
@Test
@DisplayName("자릿수 실패")
fun invalidPasswordRule_Length() {
var request = """
{
"name":"브라운",
"email":"brown@email.com",
"age":19,
"count":4,
"password":"12345"
}""".trimIndent()
mockMvc.perform(
MockMvcRequestBuilders.post("/members")
.contentType(MediaType.APPLICATION_JSON)
.content(request)
)
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.errorCode").value("E001"))
.andExpect(jsonPath("$.errorMessage")
.value("password : 비밀번호는 영문, 숫자, 특수문자를 포함 8자리 이상 입력하세요"))
}
@Test
@DisplayName("특수문자 실패")
fun invalidPasswordRule_char() {
var request = """
{
"name":"브라운",
"email":"brown@email.com",
"age":19,
"count":4,
"password":"1234567890"
}""".trimIndent()
mockMvc.perform(
MockMvcRequestBuilders.post("/members")
.contentType(MediaType.APPLICATION_JSON)
.content(request)
)
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.errorCode").value("E001"))
.andExpect(jsonPath("$.errorMessage")
.value("password : 비밀번호는 영문, 숫자, 특수문자를 포함 8자리 이상 입력하세요"))
}
}
}
이와 같이 Custom 애너테이션을 작성해 두면 원하는 유효성 검사를 만들 수 있다.
출처
'개발 > Kotlin' 카테고리의 다른 글
Gradle - dependency-management Plugin (0) | 2022.06.19 |
---|---|
QueryDSL 설정 - Kotlin (0) | 2022.06.19 |
Kotlin Basic - 함수와 변수 (0) | 2022.06.18 |
Spring Bean Validation - Test Code (0) | 2022.06.14 |
Spring Bean Validation - Basic (0) | 2022.04.12 |