Spring Boot에서 DTO Validation을 적용하는 방법을 처음부터 실무 방식까지 자세히 정리해 드립니다. @Valid, @Validated, Bean Validation, 주요 검증 애노테이션, 커스텀 검증, 예외 처리까지 예제와 함께 쉽게 이해하실 수 있습니다.

Spring Boot로 REST API를 개발하다 보면 사용자가 전달하는 데이터를 검증하는 과정이 반드시 필요합니다. 이메일 형식이 맞는지, 비밀번호 길이가 적절한지, 필수 값이 비어 있지는 않은지 등을 확인하지 않으면 잘못된 데이터가 그대로 저장될 수 있습니다.
Spring Boot에서는 Bean Validation(Jakarta Validation)을 이용하여 이러한 입력값 검증을 매우 간단하게 구현할 수 있습니다. 특히 요청(Request) DTO에서 Validation을 수행하면 Controller에 도착하는 순간 잘못된 데이터를 차단할 수 있어 유지보수성과 안정성이 크게 향상됩니다.
이번 글에서는 Spring Boot 요청 DTO Validation 사용 방법을 중심으로 실무에서 가장 많이 사용하는 방식과 예제를 차근차근 살펴보겠습니다.
01. Validation이 필요한 이유
이번 섹션에서는 Validation을 사용하는 목적과 DTO에서 검증해야 하는 이유를 알아보겠습니다.
잘못된 입력값을 검증하지 않으면 다양한 문제가 발생할 수 있습니다.
- 빈 문자열 저장
- 이메일 형식 오류
- 음수 금액 저장
- 너무 긴 문자열 저장
- Null 데이터 저장
예를 들어 회원가입 API에서 다음과 같은 요청이 들어왔다고 가정해 보겠습니다.
- 이메일 : abc
- 비밀번호 : 1
- 이름 : 빈 문자열
Validation이 없다면 이러한 값도 그대로 저장될 가능성이 있습니다.
반면 Validation을 적용하면 Controller에 도달하는 순간 요청이 거부되므로 서비스 계층이나 데이터베이스까지 잘못된 데이터가 전달되지 않습니다.
즉, Validation은 데이터 무결성을 보장하는 가장 첫 번째 방어선이라고 볼 수 있습니다.
02. Validation 의존성 추가
이번 섹션에서는 Validation을 사용하기 위한 라이브러리를 살펴보겠습니다.
✓ Maven
Spring Boot 3에서는 다음 의존성을 추가하면 됩니다.
- spring-boot-starter-validation
✓ Gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'
Spring Boot Starter를 사용하면 Bean Validation 구현체도 함께 사용할 수 있습니다.
03. DTO에 Validation 적용하기
이번 섹션에서는 가장 기본적인 DTO Validation 예제를 살펴보겠습니다.
예를 들어 회원가입 DTO가 있다고 가정해 보겠습니다.
public class UserRequest {
@NotBlank(message = "이름은 필수입니다.")
private String name;
@Email(message = "이메일 형식이 아닙니다.")
@NotBlank
private String email;
@Size(min = 8, max = 20)
private String password;
}
위 DTO에서는 다음과 같은 검증이 자동으로 수행됩니다.
애노테이션설명
| @NotNull | Null을 허용하지 않습니다. |
| @NotBlank | Null, 공백 문자열을 허용하지 않습니다. |
| @NotEmpty | Null과 빈 컬렉션을 허용하지 않습니다. |
| 이메일 형식을 검사합니다. | |
| @Size | 문자열 또는 컬렉션의 길이를 검사합니다. |
| @Min | 최소값을 검사합니다. |
| @Max | 최대값을 검사합니다. |
| @Positive | 양수만 허용합니다. |
| @PositiveOrZero | 0 이상만 허용합니다. |
| @Negative | 음수만 허용합니다. |
| @Pattern | 정규식을 이용한 검증을 수행합니다. |
실무에서는 이들 애노테이션만으로도 대부분의 요청 데이터를 검증할 수 있습니다.
04. Controller에서 @Valid 사용하기
이번 섹션에서는 DTO Validation을 실제로 적용하는 방법을 알아보겠습니다.
Validation은 Controller에서 @Valid를 붙여야 동작합니다.
@PostMapping("/users")
public ResponseEntity<?> createUser(
@Valid @RequestBody UserRequest request) {
return ResponseEntity.ok().build();
}
클라이언트가 잘못된 데이터를 보내면 Controller 내부 코드가 실행되기 전에 Validation이 먼저 수행됩니다.
검증에 실패하면 예외가 발생하며 정상적인 로직은 실행되지 않습니다.
05. @Valid와 @Validated 차이
이번 섹션에서는 많이 헷갈리는 두 애노테이션의 차이를 살펴보겠습니다.
항목@Valid@Validated
| 표준 여부 | Jakarta Validation 표준 | Spring 제공 |
| Validation 그룹 | 지원하지 않습니다. | 지원합니다. |
| 일반적인 DTO 검증 | 권장됩니다. | 사용 가능합니다. |
| 그룹별 검증 | 지원하지 않습니다. | 권장됩니다. |
일반적인 REST API에서는 @Valid만 사용해도 충분한 경우가 대부분입니다.
반면 생성(Create)과 수정(Update)에서 서로 다른 Validation 규칙을 적용해야 한다면 @Validated와 Validation Group을 함께 사용하는 방법이 적합합니다.
06. Validation 실패 시 예외 처리
이번 섹션에서는 Validation 오류를 사용자에게 보기 좋게 반환하는 방법을 알아보겠습니다.
Validation이 실패하면 대표적으로 다음과 같은 예외가 발생합니다.
- MethodArgumentNotValidException
실무에서는 전역 예외 처리기를 만들어 사용하는 경우가 많습니다.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handle(
MethodArgumentNotValidException ex) {
return ResponseEntity.badRequest().body("Validation Error");
}
}
실제로는 오류 메시지를 리스트 형태로 반환하는 경우가 많습니다.
예를 들어 다음과 같은 응답 구조를 사용할 수 있습니다.
{
"code":"VALIDATION_ERROR",
"message":"입력값이 올바르지 않습니다.",
"errors":[
{
"field":"email",
"message":"이메일 형식이 아닙니다."
}
]
}
이처럼 응답 형식을 통일하면 프론트엔드에서도 처리하기 쉬워집니다.
07. 자주 사용하는 Validation 애노테이션
이번 섹션에서는 실무에서 많이 사용하는 Validation을 정리해 보겠습니다.
✓ 문자열
- @NotBlank
- @NotEmpty
- @Size
- @Pattern
✓ 숫자
- @Min
- @Max
- @Positive
- @PositiveOrZero
- @Negative
✓ 날짜
- @Past
- @PastOrPresent
- @Future
- @FutureOrPresent
✓ 이메일
✓ Null 검사
- @NotNull
대부분의 프로젝트는 위 애노테이션만으로도 충분히 입력값을 검증할 수 있습니다.
08. DTO에서 Validation을 사용하는 이유
이번 섹션에서는 DTO Validation을 권장하는 이유를 살펴보겠습니다.
간혹 Entity에도 Validation을 작성하는 사례를 볼 수 있지만, 요청 데이터 검증은 일반적으로 DTO에서 수행하는 방식이 많이 사용됩니다.
그 이유는 다음과 같습니다.
- API 요청 검증과 데이터베이스 모델을 분리할 수 있습니다.
- Entity 변경이 API에 직접 영향을 주지 않습니다.
- 다양한 요청 DTO를 만들 수 있습니다.
- 유지보수가 쉬워집니다.
- 계층 간 역할이 명확해집니다.
실무에서는 Request DTO → Validation → Service → Entity 흐름을 많이 사용합니다.
09. 커스텀 Validation 만들기
이번 섹션에서는 기본 애노테이션으로 해결하기 어려운 경우를 알아보겠습니다.
예를 들어 아이디에 특수문자를 허용하지 않거나 회사 정책에 맞는 비밀번호 규칙을 적용해야 할 수도 있습니다.
이 경우에는 다음과 같은 방식으로 구현합니다.
- 커스텀 Annotation 작성
- ConstraintValidator 구현
- DTO에 애노테이션 적용
대표적인 활용 사례는 다음과 같습니다.
- 사업자등록번호 검증
- 주민등록번호 형식 검증
- 비밀번호 정책 검증
- 전화번호 형식 검증
- 특정 문자열 금지
복잡한 비즈니스 규칙은 커스텀 Validation으로 분리하면 재사용성과 가독성이 좋아집니다.
10. 실무에서 추천하는 Validation 구성
이번 섹션에서는 실무에서 많이 사용하는 구조를 정리해 보겠습니다.
권장하는 구조는 다음과 같습니다.
- Request DTO에서 Validation 수행
- Controller에서 @Valid 사용
- Service에서는 비즈니스 검증 수행
- Entity는 데이터 저장 역할에 집중
- ControllerAdvice에서 Validation 예외를 공통 처리
- 응답 형식을 통일하여 프론트엔드와 협업
이와 같은 구조를 사용하면 계층 간 책임이 명확해지고 코드의 유지보수성도 높아집니다.
FAQ
Q1. DTO와 Entity 중 어디에 Validation을 작성하는 것이 좋을까요?
일반적인 REST API에서는 요청 데이터 검증을 DTO에서 수행하는 방식이 많이 사용됩니다. Entity는 데이터 모델의 역할에 집중하고, DTO는 외부 입력값 검증을 담당하도록 분리하면 유지보수가 수월해집니다.
Q2. @NotNull과 @NotBlank는 무엇이 다른가요?
@NotNull은 값이 Null인지만 검사합니다. 반면 @NotBlank는 Null뿐 아니라 공백 문자열과 빈 문자열도 허용하지 않습니다. 문자열 검증에는 대부분 @NotBlank를 사용하는 경우가 많습니다.
Q3. @Valid와 @Validated는 언제 사용하면 좋을까요?
일반적인 DTO 검증은 @Valid만으로 충분한 경우가 많습니다. 생성과 수정처럼 서로 다른 검증 규칙을 적용해야 하는 경우에는 @Validated와 Validation Group을 함께 사용하는 방법을 고려할 수 있습니다.
Q4. Validation 실패 시 어떤 예외가 발생하나요?
@RequestBody와 함께 사용하는 경우에는 일반적으로 MethodArgumentNotValidException이 발생합니다. 프로젝트에 따라 전역 예외 처리기를 통해 원하는 응답 형식으로 변환하여 사용할 수 있습니다.
Q5. Validation만으로 모든 검증을 처리할 수 있나요?
기본적인 형식과 입력값 검증에는 Validation이 적합합니다. 다만 중복 검사나 권한 확인, 데이터베이스 조회가 필요한 비즈니스 규칙은 Service 계층에서 처리하는 것이 일반적인 설계 방식입니다.
관련글
'실무개발' 카테고리의 다른 글
| 폐쇄망 Spring Boot vscode 개발 환경에서 핫리로드 적용하는 방법 (0) | 2026.07.10 |
|---|---|
| 시퀀스 다이어그램 그리는 법, diagrams.net 실전 예제 (0) | 2026.07.10 |
| Java record와 Lombok 비교: DTO에는 어떤 방식을 선택해야 할까? (1) | 2026.07.05 |
| Nuxt에서 이미지 압축하는 방법: 브라우저 기반 JPG PNG WEBP 압축 구현 (0) | 2026.07.02 |
| Nuxt로 QR 코드 생성하는 방법: qrcode 라이브러리 실무 소스코드 정리 (0) | 2026.07.02 |
