저번 글에서 진행했던 ExceptionHandler는 Dto의 값을 보고 Exception을 던졌는데, 이번 글에는 서비스에서 걷어내고 Dto에서 바로 validation 체크를 하겠다.
우선 maven repository에 들어가서 필요한것을 가져와야한다.

24-03-31 기준 가장 최신인 3.2.4 버전의 Gradle( Kotlin)의 내용을 복사하고 어차피 버전은 dependencies안에 들어갔을때 삭제해도 상관없으므로 무시한다.


build.gradle.kts 의 dependencies안에 넣어주고 버전은 삭제해도 된다.
원래 dependencies에 링크 넣으면 우측상단에 코끼리가 나타나 누르면 sync가 됐는데 이번에는 안떠서 직접했다.
방법은View-Tool Windows- Gradle에 들어가서

나타난 창 새로고침 버튼을 누르면 Sync가 된다.

이제 Validator 추가하기 전에 enum class도 체크할수있도록 추가하겠다.

core 밑에 annotation이라는 패키지를 만들고 안에 ValidEnum 이라는 파일을 생성한다.
그리고 아래에 내용을 붙여넣는다.
만약 에러가 발생한다면 import를 해주면된다.
package com.example.study.core.annotation
import jakarta.validation.Constraint
import jakarta.validation.ConstraintValidator
import jakarta.validation.ConstraintValidatorContext
import jakarta.validation.Payload
import kotlin.reflect.KClass
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Constraint(validatedBy = [ValidEnumValidator::class])
annotation class ValidEnum(
val message: String = "Invalid enum value",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = [],
val enumClass: KClass<out Enum<*>> // specify the enum class to be validated
)
class ValidEnumValidator : ConstraintValidator<ValidEnum, Any> {
private lateinit var enumValues: Array<out Enum<*>>
override fun initialize(annotation: ValidEnum) {
enumValues = annotation.enumClass.java.enumConstants
}
override fun isValid(value: Any?, context: ConstraintValidatorContext): Boolean {
if (value == null) {
return true // null values are validated with the @NotNull annotation
}
return enumValues.any { it.name == value.toString() }
}
}
Enum이 작성이 됐으면 Dto에서 validation 체크를 해야하는데, 기본적으로 null이 들어올수있으므로 기존에 방식에서 ?을 추가해준다.

그리고 이 값들에 validation을 추가한다.

이렇게 설정하면 query라는 프로퍼티 안에 있는 필드에다가 notblank라는 validation을 걸게 되는데, query라는 프로퍼티의 값이 비어져있다면 위의 메시지가 출력이된다.
나머지 것들도 동일하게 작성한다.
package com.example.study.blog.dto
import com.example.study.core.annotation.ValidEnum
import com.fasterxml.jackson.annotation.JsonProperty
import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotNull
data class BlogDto (
@field:NotBlank(message = "query parameter required")
val query: String?,
@field:NotBlank(message = "sort parameter required")
@field:ValidEnum(enumClass = EnumSort::class, message = "sort parameter one of ACCURACY and RECENCY")
val sort: String?,
@field:NotNull(message = "page parameter required")
@field:Max(50, message = "page is more than max")
@field:Min(1, message = "page is less than min")
@JsonProperty("page")
val page: Int?,
@field:NotNull(message = "size parameter required")
val size: Int?) {
private enum class EnumSort {
ACCURACY,
RECENCY
}
}
위의 작업을 하고 나서는 Dto에서 validation으로 exception을 하는것이므로 searchkakao에 exception 하는 내용들은 삭제하거나 주석처리해준다.

그러고 서버를 실행시키고 나서 postman을 이용해서 테스트 해본다.


Error 코드 400이 발생하면서 json 형태로 에러가 출력되는것을 확인할 수 있다.
ExceptionHandler를 추가해서 보기 편하게 수정하겠다.
customExceptionHandler 파일안에 들어가서 내용을 추가해준다

package com.example.study.core.exception
import com.example.study.core.response.ErrorResponse
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.validation.FieldError
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
@RestControllerAdvice
class CustomExceptionHandler {
//어떤것이 들어왔을때 반응한다는 핸들러로
@ExceptionHandler(InvalidInputException::class) //InvalidInput 이 들어왔을때 Exception 한다는 핸들러이다.
fun invalidInputException(ex: InvalidInputException):ResponseEntity<ErrorResponse>{
val errors = ErrorResponse(ex.message ?: "Not Exception Message")
return ResponseEntity(errors, HttpStatus.BAD_REQUEST)
}
@ExceptionHandler(MethodArgumentNotValidException::class)
protected fun handleValidationExceptions(ex: MethodArgumentNotValidException): ResponseEntity<Map<String, String>> {
val errors = mutableMapOf<String, String>()
ex.bindingResult.allErrors.forEach { error ->
val fieldName = (error as FieldError).field
val errorMessage = error.getDefaultMessage()
errors[fieldName] = errorMessage ?: "NULL"
}
return ResponseEntity(errors, HttpStatus.BAD_REQUEST)
}
}
이렇게 작성하고 서버를 재시작 한다음 postman으로 틀린 요청을 해보자.

문제만 출력된다.

이어서 query, sort, page를 틀리게 해서 요청을 하니 세개 모두에 대한 오류 내용만 출력이된다.