Jakarta.validation.constraints

Jerry·2025년 8월 12일

1. 기본 개념

  • jakarta.validation.constraints 패키지는 표준 제약 애노테이션의 집합입니다. 필드/파라미터/리턴값 등에 부착하여 유효성 규칙을 선언합니다.
  • 모든 제약 애노테이션은 공통 속성을 가집니다
    • message: 실패 메시지(메시지 키 사용 권장)
    • groups: 검증 그룹(시나리오별 선택적 검증)
    • payload: 메타데이터 용도(일반적으로 잘 쓰지 않음)
  • 검증 트리거
    • 스프링 MVC/웹: @Valid(표준) 또는 @Validated(스프링)와 함께 @RequestBody, @ModelAttribute 등에 적용 시 자동 검증
    • 메서드 검증: 클래스에 @Validated를 붙이고, 파라미터/리턴값에 제약 부착
    • 객체 그래프 전파: 참조 필드에 @Valid를 붙이면 연쇄(cascaded) 검증이 수행됩니다.

2. 대표 표준 제약 애노테이션

널/공백/컨테이너

애노테이션의미비고
@NotNullnull 금지빈 문자열, 빈 컬렉션은 허용
@Null반드시 null주로 상태 전이 제어 등 특수 케이스
@NotEmpty비어있지 않아야 함문자열/컬렉션/맵/배열 대상, 공백만 문자열은 통과
@NotBlank공백 아닌 문자가 최소 1개문자열 전용, 사용자 입력에 가장 실용적

문자열 패턴·형식

애노테이션의미비고
@Size(min, max)길이/크기 범위문자열, 컬렉션, 맵, 배열
@Pattern(regexp, flags)정규식 일치이메일 등 커스텀 형식 제약
@EmailRFC 준수 이메일 형식형식만 검증, 실제 존재 여부 아님

숫자·자릿수

애노테이션의미비고
@Min, @Max정수 최소/최대long 기준 비교
@DecimalMin, @DecimalMax실수 최소/최대경계 포함 여부 inclusive 지정
@Positive, @PositiveOrZero양수/양수 또는 0부동소수/정수 모두
@Negative, @NegativeOrZero음수/음수 또는 0
@Digits(integer, fraction)정수부/소수부 자리 제한통화/금액 등의 자리수 제약

날짜·시간

애노테이션의미비고
@Past, @PastOrPresent과거(또는 현재 포함)java.time.*, Date, Calendar 지원
@Future, @FutureOrPresent미래(또는 현재 포함)

논리·기타

애노테이션의미비고
@AssertTrue, @AssertFalse불리언 조건 강제도메인 계산 프로퍼티 등에 사용
@Negative*, @Positive*위 숫자 섹션 참고

3. 컨테이너 요소 제약(Type-Use) — 제네릭 내부까지 검증

Bean Validation 2.0+부터 타입 사용 위치에 제약을 부착할 수 있습니다.

class PostForm {
	private List<@NotBlank String> tags;				// 리스트 요소 각각이 NotBlank
    private Map<@NotBlank String, @NotNull UUID> map;	// 키/값 각각 제약
}

4. 검증 그룹(groups)과 시퀀스

서로 다른 시나리오(생성, 수정 등)에서 다른 제약을 적용하려면 그룹을 사용합니다.

interface Create {}
interface Update {}

class UserReq {
	@NotNull(groups = Update.class)	// 수정 시에만 필수
    private UUID id;
    
    @NotBlank(groups = {Create.class, Update.class})
    private String username;
}
  • 스프링에서는 컨트롤러/서비스에서 @Validated(Create.class)처럼 그룹을 지정합니다.
  • GroupSequence로 실행 순서를 갖에할 수 있습니다(앞 그룹이 통과해야 다음 그룹 진행).

5. 메시지 처리와 국제화

  • 기본 메시지는 키 형식으로 구성되어 있고, 국제화 리소스 번들에서 오버라이드합니다.
    • 예: "{jakarta.validation.constraints.NotBlank.message}"
  • 프로젝트 messages.properties(또는 커스텀 번들)에서 키를 재정의하거나, 애노테이션 message에 커스텀 키를 넣으십시오.
  • 메시지의 {validatedValue}, {min}, {max} 같은 플레이스홀더를 활용할 수 있습니다.

6. 조합(합성) 제약과 메타 애노테이션

여러 제약을 하나의 커스텀 제약으로 합칠 수 있습니다.

@Documented
@Constraint(validatedBy = {}) // 별도 Validator 불필요(순수 조합)
@Target({ FIELD, METHOD, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@NotBlank
@Size(max = 20)
@ReportAsSingleViolation
public @interface Username {
    String message() default "{app.constraint.Username.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
  • @Constraint: 제약임을 선언. validatedBy에 Validator 클래스를 지정(조합만 하는 경우 빈 배열).
  • @ReportAsSingleViolation: 내부 여러 제약 중 하나라도 실패 시 하나의 제약 실패로 보고(메시지 단순화).
  • 대부분의 표준 제약은 @Repeatable이므로 동일 제약을 여러 번 부착할 수 있습니다(필요 시).

7. 스프링에서의 사용 포인트

  • 의존성: spring-boot-starter-validation 추가 시 Hibernate Validator가 기본 구현체로 등록됩니다.
  • DTO 검증: 컨트롤러에서 @Valid 또는 @Validated와 함께 DTO에 제약 선언.
  • 메서드 레벨 검증: 빈에 @Validated 부착 → 파라미터/리턴값에 제약 선언 시 AOP로 검증.
  • 연쇄 검증: 중첩 객체/컬렉션 필드에 @Valid를 붙여 내부까지 검증.
  • 그룹: 스프링 전용 @Validated(그룹.class)로 활성화. @Valid는 그룹 지정 기능이 없습니다.

8. 실무 팁

  • 문자열 입력에는 @NotBlank를 우선 고려하십시오. @NotEmpty는 공백 문자열을 통과시킵니다.
  • 숫자 범위는 의미에 맞게 선택하십시오. 양/음/0 포함 여부가 명확하면 @Positive*/@Negative*가 가독성이 좋습니다.
  • 날짜/시간은 비즈니스 기준시(예: ZoneId)와 시점 생성 시기를 일관되게 관리해야 오검출이 줄어듭니다.
  • 엔티티보다는 DTO에 제약을 집중하는 편이 이식성과 API 안정성에 유리합니다. 엔티티에 둘 경우 JPA 라이프사이클과 충돌할 수 있습니다.
  • 메시지는 키 기반으로 관리하고, i18n 번들에서 일괄 관리하십시오.
  • 컨테이너 요소 제약(Type-Use)을 적극 사용하면 복합 구조에서도 누락 없이 검증 가능합니다.

9. 간단 예시

// DTO
public record RegisterUserReq(
    @NotBlank @Size(max = 20) String username,
    @Email @NotBlank String email,
    @NotBlank @Size(min = 8, max = 64) String password,
    @Past LocalDate birthDate,
    List<@NotBlank String> roles
) {}

// Controller
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
@Validated // 메서드/파라미터 검증 활성화(그룹 사용 시 필요)
public class UserController {

    @PostMapping
    public ResponseEntity<Void> register(@Valid @RequestBody RegisterUserReq req) {
        // 검증 통과 후 로직
        return ResponseEntity.ok().build();
    }

    // 메서드 파라미터 검증 예시
    @GetMapping("/{id}")
    public UserDto find(@PathVariable @NotNull UUID id) {
        // ...
    }
}

10. 표준 vs 구현체 전용 구분(중요)

  • 표준 패키지: jakarta.validation.constraints.*
    → 위 표의 제약들은 전부 표준이며, 구현체 교체에 독립적입니다.
  • 구현체 전용(예: Hibernate Validator): org.hibernate.validator.constraints.*
    @URL, @Range, @Length, @CreditCardNumber 등. 사용 시 구현체 종속성이 생깁니다.
profile
Backend engineer

0개의 댓글