어제 오늘 내일

[Spring Boot] 사용자의 입력은 믿지 마세요! 유효성 검사(Validation) 정복하기 (@Valid vs @Validated) 본문

IT/SpringBoot

[Spring Boot] 사용자의 입력은 믿지 마세요! 유효성 검사(Validation) 정복하기 (@Valid vs @Validated)

hi.anna 2026. 3. 11. 07:22

"이메일 형식이 아닙니다.", "비밀번호는 8자 이상이어야 합니다."
회원가입 할 때 이런 메시지 많이 보셨죠? 프론트엔드에서도 막을 수 있지만, 백엔드에서의 검증은 필수입니다. (해커들은 프론트엔드를 우회해서 요청을 보내니까요!)

오늘은 if문 도배 없이, 어노테이션 몇 개로 깔끔하게 데이터를 검증하는 Bean Validation을 알아보겠습니다.

 


 

1. 의존성 추가부터! (build.gradle)

Spring Boot 2.3 버전 이상부터는 별도의 라이브러리를 추가해야 합니다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}

 

 


 

2. DTO에 규칙 정하기 (어노테이션의 마법)

데이터를 받는 객체(DTO)에 "이 필드는 어떤 규칙을 지켜야 해!"라고 딱지(Annotation)를 붙여줍니다.

@Getter
public class MemberRequestDto {

    @NotBlank(message = "이름은 필수입니다.") // null, "", " " 모두 허용 안 함!
    private String name;

    @Email(message = "이메일 형식이 올바르지 않습니다.")
    private String email;

    @Min(value = 18, message = "18세 이상만 가입 가능합니다.")
    private int age;
}

💡 면접 단골 질문: @NotNull vs @NotEmpty vs @NotBlank 차이점?

문자열(String) 검증할 때 가장 헷갈리는 3대장입니다. 확실히 정리해 드릴게요.

어노테이션 null ""(빈 문자열) " " (공백) 추천 상황
@NotNull X O O 숫자, 객체 등
@NotEmpty X X O 리스트, 배열 등
@NotBlank X X X String (문자열)

꿀팁: 문자열 입력받을 땐 고민하지 말고 그냥 @NotBlank 쓰세요.
사용자가 스페이스바만 눌러서 가입하는 걸 막아줍니다.

 


 

3. 컨트롤러에서 검사 시작! (@Valid)

규칙을 정했으니, 이제 검사하라고 시켜야겠죠? 컨트롤러의 파라미터 앞에 @Valid만 붙이면 끝입니다.

@PostMapping("/signup")
public ResponseEntity<String> signup(@Valid @RequestBody MemberRequestDto request) {
    // 1. 요청이 들어오면 @Valid가 DTO를 검사합니다.
    // 2. 문제가 있으면? -> 여기서 즉시 에러 발생! (메서드 실행 안 됨)

    memberService.signup(request);
    return ResponseEntity.ok("가입 성공");
}

 

 


4. 에러는 어디서 잡나요? (MethodArgumentNotValidException)

@Valid 검사에 실패하면 MethodArgumentNotValidException이라는 긴 이름의 예외가 발생 합니다.
지난 포스팅에서 배운 @RestControllerAdvice 기억나시죠? 거기서 잡으면 됩니다!

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();

        // 에러가 난 필드와 메시지를 쏙쏙 뽑아서 맵에 담기
        ex.getBindingResult().getAllErrors().forEach(c -> errors.put(((FieldError) c).getField(), c.getDefaultMessage()));

        return ResponseEntity.badRequest().body(errors);
    }
}

결과 (클라이언트가 받는 응답):

{
    "name": "이름은 필수입니다.",
    "email": "이메일 형식이 올바르지 않습니다."
}

 

 


 

5. 심화: @Valid vs @Validated 차이가 뭔가요?

  • @Valid: 자바 표준 스펙(JSR-303)입니다. 가장 기본적으로 쓰입니다.
  • @Validated: 스프링이 제공하는 기능입니다. 그룹(Groups) 기능을 사용할 수 있습니다.

"그룹 기능이 뭐죠?"

회원가입 할 때는 id가 없어야 하고(Null), 정보 수정 할 때는 id가 필수(NotNull)여야 한다면?
하나의 DTO를 쓰면서 상황에 따라 검증 로직을 다르게 적용하고 싶을 때 @Validated를 씁니다. (하지만 실무에서는 복잡해서 DTO를 분리하는 것을 더 추천합니다!)

 


 

마치며

오늘의 핵심 요약입니다.

  1. Validation 라이브러리를 추가한다.
  2. DTO에 @NotBlank, @Email 등의 규칙을 붙인다.
  3. 컨트롤러 파라미터에 @Valid를 붙여 검사를 수행한다.
  4. 발생한 예외는 전역 핸들러에서 깔끔하게 처리한다.
  5.  

이제 여러분의 서버는 잘못된 데이터로부터 안전합니다!

다음 포스팅에서는 Spring Boot 데이터 접근 기술의 꽃: JPA와 영속성 컨텍스트(Persistence Context)에 대해 알아보겠습니다. 드디어 DB 이야기네요!

도움이 되셨다면 좋아요와 댓글 부탁드립니다! 😊

반응형
Comments