Java 아키텍처·Spring 용어 사전
Spring MVCBean Validation · ProblemDetail · @RestControllerAdvice

@Valid

요청 값 검증을 경계에서 선언적으로 수행하는 것. 실패는 예외 핸들러가 표준 형식으로 변환한다.

요청 값이 컨트롤러 경계를 넘기 전에 규칙을 만족하는지 검사하는 것.

경계에서 막는다

record OrderRequest(
    @NotNull Long productId,
    @Min(1) @Max(999) int quantity,
    @Email String contactEmail,
    @NotBlank @Size(max = 200) String address
) {}

@PostMapping("/orders")
OrderResponse create(@Valid @RequestBody OrderRequest req) { ... }

서비스 안에서 if (quantity < 1) throw ...를 흩뿌리는 대신 한곳에 모은다. 규칙이 타입 선언 옆에 있어 읽기도 쉽다.

@Valid와 @Validated

  • @Valid — 표준(Jakarta Bean Validation). 중첩 객체까지 따라 들어간다
  • @Validated — Spring 확장. 검증 그룹(group)을 지정할 수 있고
    • 클래스에 붙이면 서비스 메서드 파라미터도 검증한다(AOP)
@Service @Validated
class OrderService {
    void cancel(@NotNull Long id) { }   // 서비스 계층에서도 검증된다
}

중첩 검증은 @Valid를 또 붙여야 한다

record OrderRequest(
    @NotNull @Valid AddressDto address,     // ← @Valid 가 없으면 안쪽은 검사되지 않는다
    @NotEmpty List<@Valid ItemDto> items    // 컬렉션 요소도 마찬가지
) {}

가장 자주 놓치는 지점이다. 바깥만 붙이고 안쪽 규칙이 조용히 무시된다.

실패를 표준 형식으로

@RestControllerAdvice
class ApiExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    ProblemDetail handle(MethodArgumentNotValidException e) {
        var pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
        pd.setTitle("검증 실패");
        pd.setProperty("errors", e.getBindingResult().getFieldErrors().stream()
            .map(f -> Map.of("field", f.getField(), "message", f.getDefaultMessage())).toList());
        return pd;      // RFC 9457 application/problem+json
    }
}

ProblemDetailRFC 9457 표준 오류 포맷이라 클라이언트가 일관되게 처리할 수 있다.

커스텀 검증

@Constraint(validatedBy = PhoneValidator.class)
@Target(FIELD) @Retention(RUNTIME)
public @interface Phone { String message() default "전화번호 형식이 아닙니다"; ... }

어디까지 검증할 것인가

  • 형식 검증 (길이 · 필수 · 패턴) — → @Valid 로 경계에서

  • 업무 규칙 (재고가 충분한가) — → 도메인 안에서

  • "이메일 형식" 은 형식이고 "이미 가입된 이메일인가" 는 업무 규칙이다

면접 함정

  • "@Valid만 붙이면 다 검증된다" → 중첩 객체는 안쪽에도 붙여야 한다.
  • "검증 실패는 500" → 클라이언트 잘못이므로 400이다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 웹 — MVC·REST·검증·예외·WebSocket/STOMP