[Spring] Bean Validation(@Valid, @Validated)과 예외 종류에대해 알아보자

narisarit·2025년 3월 9일
0
post-thumbnail

스프링을 사용할 때 HTTP 요청에 포함된 데이터를 DTO를 통해 객체 형태로 받거나, 메서드의 매개변수로 직접 전달받을 수 있다.

애플리케이션 내에서는 도메인에 따라 전달받은 데이터를 검증해야 하는 경우가 있다.

public ResponseEntity xxxService(MemberRequest memberRequest) {
	String name = member.name();
    if (name == null || name.isEmpty()){
    	throw new ...
    }
    if (name.length() > 255) {
    	throw new ...
    }
}

전달받은 데이터를 Service 클래스에서 검증하는 예시이다.

이렇게 Service 계층에서 예외를 처리하게 되면 예외 처리 로직과 비즈니스 로직이 섞이는 단점이 있다.
또한, null, 빈 문자열("")과 같은 단순 검증을 반복적으로 작성해야 하는 문제가 발생한다.

@Valid와 @Validated

spring-boot-starter-validation 의존성을 추가하면 Bean Validation을 사용할 수 있다.
기본적으로 Bean Validation은 객체 필드에 검증 애노테이션을 달아 제약 조건을 정의하는 방식으로 동작한다.
다음 3가지 요청 데이터에 대해 유효성 검증을 할 수 있다.

  • request body
  • path에 포함된 variables (ex. /foos/{id}의 id)
  • query parameters

간단히 @Valid는 자바에서 제공, @Validated는 스프링에서 제공한다고 생각하면 된다.

@Valid

디스패처 서블릿에서 컨트롤러로 요청이 전달될 때, @RequestBody가 붙은 파라미터는 JSON 형식의 데이터를 Java 객체로 자동 매핑한다.

이때 @Valid와 필드에 검증 애노테이션이 있으면 해당 필드에 대한 유효성을 검사한다.

유효성 검사에서 실패하는 경우 MethodArgumentNotValidException 예외가 발생하며,
기본적으로 Spring은 이 예외를 HTTP 상태 코드 400 (Bad Request)로 변환한다.

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<List<String>> handleInvalidFormatException(final MethodArgumentNotValidException exception) {
        List<String> errors = new ArrayList<>();
        exception.getBindingResult().getFieldErrors().forEach(fieldError -> {
            errors.add("["+ fieldError.getField() + "] " + fieldError.getDefaultMessage());
        });

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(errors);
    }

위와 같이 해당 예외를 처리할 수 있다.
이는 클라이언트에게 상세한 오류 정보를 제공하는 방식의 예시이다.

@Validated

스프링에서 제공하는 기능으로, 클래스 레벨에 @Validated를 적용하고 파라미터에 @Valid를 명시하면 해당 객체에 대해 검증을 진행할 수 있다.

@Valid와 달리 클래스 레벨에 @Validated를 설정하기 때문에, 컨트롤러 이외의 클래스에서도 파라미터 유효성 검사가 가능하다.

또한, @Validated유효성 검증 그룹을 지정할 수 있는 기능을 제공하지만, 일반적으로 자주 사용되지 않으므로 여기서는 다루지 않는다.

유효성 검사에 실패하면 ConstraintViolationException이 발생하며,
Spring이 기본적으로 해당 예외를 처리하지 않기 때문에 HTTP 상태 코드 500 (Internal Server Error)로 변환된다.

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity constraintViolationException(final ConstraintViolationException exception) {
        log.warn("message: ", exception);

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body("입력값이 유효하지 않습니다.");
    }

위와 같이 해당 예외를 처리할 수 있다.
이는 클라이언트에게 많은 정보를 제공하지 않는 예시이다.

profile
식물축구코딩롤

0개의 댓글