createdAt: 2022-03-01 @이영훈
updatedAt: 2024-01-10 @이영훈
Validation 의존성 설정
build.gradle.kts 파일에 spring-boot-starter-validation을 추가합니다
// build.gradle.kts
dependencies {
// ...
implementation("org.springframework.boot:spring-boot-starter-validation")
// ...
}
Kotlin
복사
@RequestBody 유효성 검사
컨트롤러에서 검사하고 싶은 CreateJobDto 앞에 @Validated 어노테이션을 붙입니다
// JobController.kt
import org.springframework.validation.annotation.Validated
@PostMapping("/v1/jobs")
fun createJob(
@RequestBody @Validated createJob: CreateJobDto,
) {
// logic...
}
Kotlin
복사
그리고 CreateJobDto에 제약조건 어노테이션을 붙입니다
Kotlin에서는 다음 사항을 유의해야 합니다
•
◦
@get: 을 해도 되고 @field: 로 되는데, 아래 코드에서는 @field: 로 하였습니다
// CreateJobDto.kt
import jakarta.validation.constraints.FutureOrPresent
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.NotEmpty
class CreateJobDto(
// 제목: not null, not blank
@field:NotEmpty(message = "title should not be null nor empty")
val title: String,
// 작업 시작 시간: 현재나 미래만 가능
@field:FutureOrPresent(message = "startAt should not be past")
val startAt: OffsetDateTime,
// 수당: 500 이상 (같거나 초과)
@field:Min(500)
val pay: Int,
)
Kotlin
복사
@Neested class 유효성 검사
nested class의 유효성 검사에는 @Valid 어노테이션을 붙입니다
이번에도 마찬가지로 getter(@get:)나 field(@field:)에 붙여야 합니다
class CreateJobDto(
// 제목: not null, not blank
@get:NotEmpty(message = "title should not be null nor empty")
val title: String,
// 작업 시작 시간: 현재나 미래만 가능
@get:FutureOrPresent(message = "startAt should not be past")
val startAt: OffsetDateTime,
// 수당: 500 이상 (같거나 초과)
@get:Min(500)
val pay: Int,
// 참여 개발자: 참여 개발자는 null일 수 없습니다
@get:NotNull(message = "developers should not be null")
// nested class 유효성 검사
@get:Valid
val developers: List<DeveloperDto>,
)
class DeveloperDto(
@get:NotEmpty(message = "developer's name should not be null nor empty")
val name: String,
@get:NotEmpty(message = "developer's position should not be null nor empty")
val position: String,
)
Kotlin
복사
@RequestParam 유효성 검사
Controller 클래스에 @Validated 어노테이션을 붙여줍니다
그리고 @RequestParam의 파라미터에 제약 조건을 선언합니다
예제에는 page가 1부터 시작한다라는 제약 조건을 걸었습니다 (@Min)
@Validated
@RestController
@RequestMapping(produces = [MediaType.APPLICATION_JSON_VALUE])
class JobController(
private val jobService: JobService,
) {
@GetMapping("/v1/jobs")
fun getJobs(
@Min(1) @RequestParam page: Int,
) {
// logic...
}
}
Kotlin
복사
@PathVariable 유효성 검사
@RequestParam 유효성 검사와 동일합니다. Controller 클래스에 @Validated 어노테이션을 선언하고, @PathVariable의 파라미터에 제약 조건을 붙여줍니다.
@Validated
@RestController
@RequestMapping(produces = [MediaType.APPLICATION_JSON_VALUE])
class JobController(
private val jobService: JobService,
) {
@GetMapping("/v1/jobs/{jobId}")
fun getJob(
@Min(10) @PathVariable("jobId") jobId: Long,
) {
// logic...
}
}
Kotlin
복사
@ExceptionHandler로 예외처리하기
1.
@RequestBody의 파라미터가 유효하지 않으면 MethodArgumentNotValidException 에러가 발생합니다
2.
@RequestParam과 @PathVariable의 파라미터가 유효하지 않으면 ConstraintViolationException 에러가 발생합니다
해당 에러 두개를 @RestControllerAdvice와 @ExceptionHandler를 사용하여 처리합니다.
@RestControllerAdvice
class ExceptionControllerAdvice {
// @RequestBody의 파라미터가 유효하지 않으면 MethodArgumentNotValidException 에러가 발생합니다
@ExceptionHandler
fun handleInvalidRequestBodyException(e: MethodArgumentNotValidException): ResponseEntity<ExceptionResult> {
val allErrors = e.bindingResult.allErrors.map { it.defaultMessage }
val statusCode = HttpStatus.BAD_REQUEST
val exceptionResult = ExceptionResult(statusCode.value(), allErrors, null)
return ResponseEntity(exceptionResult, statusCode)
}
// @RequestParam과 @PathVariable의 파라미터가 유효하지 않으면 ConstraintViolationException 에러가 발생합니다
@ExceptionHandler
fun handleConstraintViolationException(e: ConstraintViolationException): ResponseEntity<ExceptionResult> {
val statusCode = HttpStatus.BAD_REQUEST
val exceptionResult = ExceptionResult(statusCode.value(), e.message!!, null)
return ResponseEntity(exceptionResult, statusCode)
}
}
Kotlin
복사