
1. 유효성 검사의 개요
- 폼데이터를 전달할때 유효하지 않은 값이 전송 될 수 있다. 이를 방지하기 위해
- 유효성 검사는 폼 페이지의 입력 값이 서버로 전송되기전 정해진 규칙에 따라 입력되어 있는지, 값이 타장한지 검사합니다.
1. 유효성 검사를 위한 설정
- @Valid
- 의존 라이브러리 등록
implementation 'org.springframework.boot:spring-boot-starter-validation'
2. 유효성 검사의 유형
- Bean Validation: 특정 도메인 클래스의 멤버 변수 → 제약사항 애너테이션
- ConstraintValidation: 멤버 변수에 사용자 정의 애너테이션을 정의 → 유효성 검사 수행, 클래스 제약 조건 부여
- Validator: 스프링부트에서 제공하는 Validator 인터페이스 → 애플리케이션의 모든 계층에서 유효성 검사
- 유효성 검사 폼데이터
- 입력 데이터가 null인가
- 형식에 맞는가 (날짜, 이메일)
- 나이를 숫자로 입력했는가
- 입력 길이를 초과하는가
- 로그인 인증 시 아이디와 패스워드가 유효한가
- 회원가입 시 아이디가 중복되는가
2. Bean Validation을 이용한 유효성 검사
- 제약사항 애너테이션 선언으로 필드에 대한 유효성 검사
- @제약 사항 애너테이션 -> @Valid -> 뷰 페이지 오류 메시지 출력
1. 제약사항 애너테이션
public class 클래스 이름 {
@제약사항 애너페이션( 속성, [, message = "오류 메시지 [오류 코드]"])
private String 멤버 변수;
}
2. Bean Validation 제약 조건 어노테이션 유형
| 유형 | 설명 | 주요 속성 |
| @AssertFalse | 프로퍼티 값이 거짓(false) 인지 검사 | - |
| @AssertTrue | 프로퍼티 값이 참(true) 인지 검사 | - |
| @DecimalMax | 프로퍼티 값이 가질 수 있는 최대 실수 값 검사 | value (값), inclusive (포함 여부) |
| @DecimalMin | 프로퍼티 값이 가질 수 있는 최소 실수 값 검사 | value (값), inclusive (포함 여부) |
| @Digits | 프로퍼티 값이 가진 소수점 위치를 포함하는 정수 및 소수점 자리수 검사 | integer (정수 자리수), fraction (소수 자리수) |
| 이메일 형식인지 검사 | - | |
| @Future | 프로퍼티 값이 미래 날짜/시간인지 검사 | - |
| @FutureOrPresent | 프로퍼티 값이 현재 또는 미래 날짜/시간인지 검사 | - |
| @Max | 프로퍼티 값이 가질 수 있는 최대 정수 값 검사 | value (최대 값) |
| @Min | 프로퍼티 값이 가질 수 있는 최소 정수 값 검사 | value (최소 값) |
| @Negative | 프로퍼티 값이 음수인지 검사 | - |
| @NegativeOrZero | 프로퍼티 값이 음수 또는 0인지 검사 | - |
| @NotBlank | 프로퍼티 값이 null이 아니고 공백이 아닌 문자를 포함하는지 검사 | - |
| @NotEmpty | 프로퍼티 값(문자열, 컬렉션 등)이 비어있지 않은지 검사 | - |
| @NotNull | 프로퍼티 값이 null이 아닌지 검사 | - |
| @Null | 프로퍼티 값이 Null인지 검사 | - |
| @Past | 프로퍼티 값이 과거 날짜/시간인지 검사 | - |
| @PastOrPresent | 프로퍼티 값이 과거 또는 현재 날짜/시간인지 검사 | - |
| @Pattern | 프로퍼티 값이 정의된 정규 표현식에 일치하는지 검사 | regexp (정규 표현식) |
| @Positive | 프로퍼티 값이 양수인지 검사 (0 제외) | - |
| @PositiveOrZero | 프로퍼티 값이 양수 또는 0인지 검사 | - |
| @Size | 프로퍼티 값이 가질 수 있는 최대/최소 크기 검사 (문자열 길이, 컬렉션 크기 등) | min (최소 값), max (최대 값) |
3. 제약사항 어노테이션의 기본 메시지
- 제약사항을 위반하면 hibernate-validator-xxx.jar에서 제공하는 메시지 출력
- 어노테이션들이 message 속성을 지정하지 않았을 때 기본으로 출력하는 오류 메시지를 보여줍니다.
| 유형 | 기본 메시지 |
| @AssertFalse | false여야 합니다. |
| @AssertTrue | true여야 합니다. |
| @DecimalMax | 다음 값 이하여야 합니다({value}). |
| @DecimalMin | 다음 값 이상이어야 합니다({value}). |
| @Digits | 숫자 값이 한계를 초과합니다({fraction}자리) |
| 올바른 형식의 이메일 주소여야 합니다. | |
| @Future | 미래 날짜여야 합니다. |
| @FutureOrPresent | 현재 또는 미래 날짜여야 합니다. |
| @Max | ${value} 이하여야 합니다. |
| @Min | ${value} 이상이어야 합니다. |
| @Negative | 0 미만이어야 합니다. |
| @NegativeOrZero | 0 이하이어야 합니다. |
| @NotBlank | 공백일 수 없습니다. |
| @NotEmpty | 비어 있을 수 없습니다. |
| @NotNull | null이어서는 안됩니다. |
| @Null | null이어야 합니다. |
| @Past | 과거 날짜여야 합니다. |
| @PastOrPresent | 과거 또는 현재 날짜여야 합니다. |
| @Pattern | ${regexp}와 일치해야 합니다. |
| @Positive | 0보다 커야 합니다. |
| @PositiveOrZero | 0 이상이어야 합니다. |
| @Size | 크기가 ${min}에서 ${max} 사이여야 합니다. |
public class 클래스 이름 {
@NotEmpty
@Size(min=4, max=10)
@Min(value=0)
}
@PostMapping
public String submitForm( @Valid @ModelAttribute("product") Product product, // @Valid로 검사 실행
BindingResult bindingResult) { // 검사 결과를 받음
if(bindingResult.hasErrors()) {
// 유효성 검사 실패 시, 다시 폼 페이지를 호출 (에러 메시지는 Thymeleaf가 처리)
return "viewPage01";
}
// 유효성 검사 성공 시, 결과 페이지 호출
return "viewPage01_result";
}
}
< !--viewPage01.html-->
<body>
<h3>유효성 검사</h3>
<form th:object="${product}" action="/exam01" method="post">
<p>품명 : <input type="text" th:field="*{name}"> <span th:errors="*{name}"></span>
<p>가격 : <input type="text" th:field="*{price}"> <span th:errors="*{price}"></span>
<p><input type="submit" value="확인"/>
<input type="reset" value="취소"/>
</form>
</body>
//viewPage_result01.html
<body>
<h3>유효성 검사</h3>
<p>품명 : [[${product.name}]]
<p>가격 : [[${product.price}]]
</body>
< !--viewPage01_result.html-->
<body>
<h3>유효성 검사</h3>
<p>품명 : [[${product.name}]]
<p>가격 : [[${product.price}]]
</body>
4. 제약사항 애너테이션에 사용자 정의 오류 메시지 사용하기
- 메시지 속성 값에 출력할 오류 메시지를 직접 정의한다.
- 오류코드 = 출력할 오류 메시지
# 제약사항 어노테이션.필드이름 = 출력할 오류 메시지 형식 사용
NotEmpty.product.name = 값을 입력해 주세요
Min.product.price = 0 이상의 값을 입력해 주세요
# 메시지 리소스 파일의 기본 이름 설정 (경로는 src/main/resources가 기본)
spring.messages.basename = messages
# 메시지 리소스 파일의 인코딩을 UTF-8로 설정 (한글 깨짐 방지)
spring.messages.encoding = UTF-8
// Product.java (메시지 리소스 파일 사용 시)
public class Product {
// message를 생략하면 Spring이 messages.properties에서 키를 찾아 출력
@NotEmpty
@Size(min=4, max=10, message="4자~10자 이내로 입력해 주세요")
private String name;
@Min(value=0) // messages.properties의 Min.product.price 키와 매핑됨
private int price;
}
-> 도매인 클래스의 프로퍼티 값에 대한 중복 여부를 체크할 수 없다.
( 독립적인 라이브러리이기 때문에, 기본적으로 DB나 외부 시스템에 접근하는 로직을 포함하고 있지 않습니다: 아이디 중복 검사 x)
3. ConstraintValidator 인터페이스를 이용한 유효성 검사
1. 사용자 정의 애너체이션 생성
- 사용자 정의 애너테이션을 이용해 유효성 검사
- 사용자 정의 애너테이션 선언, 유효성 검사, 오류 메시지
@Target({ElementType.FIELD}) // 1. 애너테이션을 적용할 위치 (필드)
@Retention(RetentionPolicy.RUNTIME) // 2. 런타임까지 유지
@Constraint(validatedBy = {CustomValidator.class}) // 3. 검증 로직을 구현할 클래스 지정
public @interface CustomConstraint {
// 4. 필수 요소: 오류 메시지 설정
String message() default "기본 사용자 정의 오류 메시지입니다.";
// 5. 필수 요소: 유효성 검사 그룹 지정 (기본값)
Class<?>[] groups() default {};
// 6. 필수 요소: 페이로드 지정 (기본값)
Class<? extends Payload>[] payload() default {};
// 7. (선택적) 사용자 정의 속성 추가
int minLength() default 5;
}
- @Constraint : 제약사항을 생성하는 애너테이션
- @Retention : 지속 시간 결정
- @Target : 적용 위치 결정
2. 구현체 생성
- ConstraintValidator 구현체 -> 유효성 검사 클래스
- initialize() -> 사용자 정의 애너테이션 정보 초기화
- isValid() -> 유효성 검사 로직 수행
// domain
// ConstraintValidator<애너테이션, 검사 대상 타입>
public class CustomValidator implements ConstraintValidator<CustomConstraint, String> {
private int requiredMinLength;
// 1. 초기화 메서드
@Override
public void initialize(CustomConstraint constraintAnnotation) {
// 정의한 애너테이션의 속성값(여기선 minLength)을 가져와 초기화합니다.
this.requiredMinLength = constraintAnnotation.minLength();
}
// 2. 검증 로직 메서드
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// null 값은 @NotNull로 처리하는 것이 일반적이므로 여기서는 true 반환
if (value == null) {
return true;
}
// 실제 유효성 검사 로직
// 입력된 문자열의 길이가 애너테이션에서 설정한 minLength보다 크거나 같으면 true 반환
return value.length() >= requiredMinLength;
}
}
public class UserDto {
// @CustomConstraint를 사용
@CustomConstraint(minLength = 8, message = "아이디는 최소 8자 이상이어야 합니다.")
private String userId;
}
4. Validator 인터페이스를 이용한 유효성 검사
- Valudator인터페이스 구현체생성 -> @InitBinder메서드 추가 -> @Valid를 이용한 유효성 검사 -> 뷰페이지 오류 메시지 출력
1. 구현체 생성
- Validator의 인터페이스 메서드
- supports(Class<?> clazz): 지원 여부 확인:
- 객체를 유효성 검사할 수 있는지 여부를 반환합니다.
- (Product.class.isAssignableFrom(clazz) 등)
- validate(Object target, Errors errors): 실제 검증 로직: 유효성 검사를 수행.
- target은 검사 대상 객체(DTO/Entity)이며,
- errors는 검사 실패 시 오류 정보(에러 코드, 메시지 등)를 담는 객체입니다.
- supports(Class<?> clazz): 지원 여부 확인:
- ValidationUtils의 메서드
- rejectIfEmpty(Errors errors, String field, String errorCode):
- 빈 값 검사:
- 지정된 field의 값이 null이거나 빈 문자열 ("")인 경우, errors 객체에 errorCode로 오류를 등록합니다.
- rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode): 빈 값/공백 검사지정된 field의 값이 null, 빈 문자열, 또는 공백 문자(" ")로만 구성되어 있는 경우, errors 객체에 errorCode로 오류를 등록합니다. (가장 흔하게 사용)
- invokeValidator(Validator validator, Object target, Errors errors):
- 검증기 호출:
- 다른 Validator 인스턴스를 호출하여 특정 객체에 대한 유효성 검사를 위임하는 데 사용됩니다.
- rejectIfEmpty(Errors errors, String field, String errorCode):
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.springboot.domain.Product;
public class ProductValidator implements Validator {
// 1-1. supports(): 이 검증기가 어떤 클래스를 지원하는지 정의
@Override
public boolean supports(Class<?> clazz) {
// Product 클래스 타입인지 확인
return Product.class.isAssignableFrom(clazz);
}
// 1-2. validate(): 실제 유효성 검사 로직 구현
@Override
public void validate(Object target, Errors errors) {
Product product = (Product) target;
// **(1) ValidationUtils를 이용한 빈 값(NotBlank) 검사**
// "name" 필드가 비어있으면 "notBlank" 에러 코드를 Errors 객체에 추가
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "notBlank");
// **(2) 직접 코드를 이용한 특정 조건(Min/Max) 검사**
if (product.getPrice() < 0 || product.getPrice() > 100000) {
// "price" 필드가 조건에 맞지 않으면 "priceRange" 에러 코드를 Errors 객체에 추가
errors.rejectValue("price", "priceRange");
}
}
}
2. @initBinder를 선언한 메서드 추가
- @initBinder가 선언된 메서드에 Validator을 미리 등록하면 해당 컨트롤러의 모든 메서드에서 유효성 검사를 할 수 있음.
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.stereotype.Controller;
// ... (다른 import 생략)
@Controller
// @RequestMapping("/exam01")
public class Example01Controller {
// 2. @InitBinder 메서드: 요청 처리 전에 실행되며 검증기를 WebDataBinder에 등록
@InitBinder
public void initBinder(WebDataBinder binder) {
// ProductValidator의 인스턴스를 생성하여 WebDataBinder에 추가
binder.addValidators(new ProductValidator());
}
// ... (GetMapping, PostMapping 메서드)
}
5. [도서 쇼핑몰] 도서 등록 데이터의 유효성 검사하기
1. Bean Validation으로 유효성 검사하기
- 의존 라이브러리 등록
- 메시지 리소스 파일 작성 (message.properties)
- 도서 클래스에 제약사항 애너테이션 설정 (Book.java)
- 도서 등록 데이터의 우효성 검사 실행 (BookController)
- 도서 등록 페이지 수정 (addBook.html)
2. ConstraintValidator 인터페이스 유효성 검사하기
- 메시지 리소스 파일 작성
- 도서 클래스에 자용사 정의 애너테이션 설정 (Book.java)
- 사용자 정의 애너페이션 생성 (BookId.java)
- 도서 아디이의 유효성 검사 클래스 생성 (VookVAlidator.java)
3. Validator 인터헤이스로 유효성 검사하기
- 메시지 리소스 파일 설정
- 제약사항의 유효성 검사 클래스 생성 (UnitslnStockValidator.java)
- 도서 재고수의 유효성 검사 실행 (BookController.java)
- 도서 등록 페이지 수정 (addBook.html)
4. 제약사항 애너테이션과 VAlidator 인터페이스를 연동한 유효성 검사하기
- 유효성 검사클래스 생성 (BookValidator.java)
- 오류 메시지 가져오기 (Book.java)
- 유효성 검사 실행 (BookController.java)
- 유효성 검사 연동 (ValidationConfig.java)
연습문제
출처 : 송미영, 『 스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스 (2024).
'DEVELOPMENT > Spring' 카테고리의 다른 글
| [스프링부트 완전정복] 11. 예외 처리 (0) | 2025.12.30 |
|---|---|
| [스프링부트 완전정복] 10장. 시큐리티 처리 (0) | 2025.12.16 |
| [스프링부트 완전정복] 8. 다국어 처리 (1) | 2025.11.15 |
| [스프링부트 완전정복] 7. 파일 업로드 처리 (0) | 2025.11.10 |
| [스프링부트 완전정복] 6. 폼 태그 (0) | 2025.11.03 |