안녕하세요! 오랜만에 글을 작성하는 재영입니따
개인 프로젝트를 또 끄적끄적... 하는 중인데, 프로젝트를 진행하면서 놓치고 있던 부분을 다시 정리해 보겠습니다.
저는 사용자가 입력하는 필드를 만들어두면, Spring MVC의 사용자 입력 검증 방식을 활용하여 유효성 검사를 수행하는데,
HTML5에서 기본적으로 유효성 검사를 할 수 있어서, 그 방식과 제가 기존에 사용하던 검증 방식을 정리하겠습니다.
두 가지 검증 방식을 설명하면서, 두 가지 검증 방식을 함께 사용하면 발생하는 문제와 해결 방법도 함께 설명하려고 합니다.
시~~~작

1. 개요
스프링 MVC와 HTML5는 각각의 방식으로 사용자 입력을 검증할 수 있습니다. 이번 글에서는 두 검증 방식을 함께 사용할 때 발생할 수 있는 문제와 이를 해결하는 방법을 설명합니다.
2. Spring MVC와 Bean Validation
스프링 MVC에서는 @Valid와 BindingResult를 활용해 서버 측 유효성 검사를 수행할 수 있습니다.
DTO 클래스에 @NotBlank, @Size, @NotNull 등의 Bean Validation 어노테이션을 적용해 세부적인 검증을 설정할 수 있으며, 검증 오류가 발생하면 서버에서 설정한 맞춤 오류 메시지를 사용자에게 보여줄 수 있습니다.
일단, 스프링에서 @Valid와 같은 검증 어노테이션을 사용하려면, spring-boot-starter-validation 의존성을 추가해야 합니다. Gradle을 사용하신다면, build.gradle에 아래와 같이 의존성을 추가해 주세요.
dependencies {
// 검증 어노테이션 사용을 위한 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
그다음에, 사용하는 DTO 클래스에 Bean Validation 어노테이션을 적용해서 세부적인 검증을 설정하고, 맞지 않는 데이터가 넘어오는 경우에는 각 어노테이션에 message 속성을 추가해서 사용자에게 표시될 오류 메시지를 설정할 수 있습니다.
그리고 필드마다 여러 어노테이션을 조합해서 복잡한 유효선 검증을 설정할 수 있습니다. 저는 아래 예시에 @NotBlank, @Size를 함께 사용했는데, 이는 사용자가 필드를 필수 입력하면서 특정 길이의 문자열을 입력하도록 제한할 수 있습니다.
public class SampleDto {
@NotBlank(message = "제목은 필수 입력 값입니다.")
private String title;
@NotBlank(message = "상세 설명은 필수 입력 값입니다.")
@Size(max = 255, message = "상세 설명은 255자를 넘을 수 없습니다.")
private String description;
@NotNull(message = "기한은 필수 입력 값입니다.")
@Future(message = "기한은 미래 날짜여야 합니다.")
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm") // HTML datetime-local과 맞추기 위한 패턴
private LocalDateTime dueDate;
}
많이 사용하는 어노테이션은 @NotNull, @NotEmpty, @NotBlank, @Size 등이 있습니다.
저도 제가 자주 사용하는 어노테이션만 알고 있었는데, 여기에 추가로 많이 사용하는 Bean Validation 어노테이션을 정리하겠습니다. 여기서 필요한 거 찾아서 사용하시면 될 것 같습니다.
- @NotNull
- 기능: 해당 필드가 null이 아니어야 함을 검증합니다.
- 적용 예시: 주로 필수 입력 값이 필요할 때 사용합니다. 그러나 빈 문자열(“”)은 허용되므로, 빈 문자열을 막고 싶다면 @NotBlank 또는 @NotEmpty를 고려하세요.
- @NotEmpty
- 기능: 문자열, 컬렉션, 배열 등이 비어 있지 않음을 검증합니다. 즉, null이거나 "" (빈 문자열), 빈 컬렉션/배열을 허용하지 않습니다.
- 적용 예시: 비어 있지 않은 문자열이나 컬렉션을 필수로 해야 하는 경우 사용합니다.
- @NotBlank
- 기능: 문자열이 null, 빈 문자열(""), 또는 공백(whitespace)으로만 이루어져 있지 않음을 검증합니다.
- 적용 예시: 사용자 입력에서 공백만 입력하는 것을 막고 싶을 때 유용합니다.
- @Size
- 기능: 문자열, 컬렉션, 배열 등의 길이나 크기가 특정 범위 내에 있는지 검증합니다.
- 속성: min, max로 최소, 최대 길이를 지정할 수 있습니다.
- 적용 예시: 예를 들어, 사용자 이름의 최소/최대 길이를 제한하고 싶을 때 @Size(min=2, max=50)과 같이 설정합니다.
- @Min, @Max
- 기능: 숫자가 특정 범위에 있는지 검증합니다.
- 적용 예시: 나이, 가격 등의 최소/최대 값을 설정할 때 유용합니다.
- 속성: @Min(value = 18), @Max(value = 65)와 같이 사용하여 숫자의 범위를 제한합니다.
- @Positive, @PositiveOrZero
- 기능: 숫자가 양수인지 또는 0을 포함한 양수인지 검증합니다.
- 적용 예시: 재고 수량이나 나이와 같은 값이 0 이상의 양수여야 할 때 사용합니다.
- @Negative, @NegativeOrZero
- 기능: 숫자가 음수인지 또는 0을 포함한 음수인지 검증합니다.
- 적용 예시: 수량, 온도 값 등이 음수이어야 할 때 사용합니다.
- @Pattern
- 기능: 문자열이 특정 정규식 패턴과 일치하는지 검증합니다.
- 속성: regexp 속성으로 정규식을 지정합니다.
- 적용 예시: 이메일, 전화번호, 비밀번호의 형식을 제한하고 싶을 때 유용합니다. 예를 들어, @Pattern(regexp = "^[a-zA-Z0-9]+$")는 알파벳과 숫자만 허용합니다.
- @Email
- 기능: 이메일 형식이 올바른지 검증합니다.
- 적용 예시: 이메일 입력 필드에 적용하여 유효한 이메일 형식인지 검사할 수 있습니다.
- @Future, @FutureOrPresent
- 기능: 날짜/시간이 현재 시간보다 미래인지 또는 현재를 포함한 미래인지 검증합니다.
- 적용 예시: 예약 날짜, 기한과 같이 미래의 날짜여야 할 때 사용합니다.
- @Past, @PastOrPresent
- 기능: 날짜/시간이 현재 시간보다 과거인지 또는 현재를 포함한 과거인지 검증합니다.
- 적용 예시: 생년월일과 같이 과거의 날짜여야 하는 경우 사용합니다.
이런 어노테이션을 사용해서 서버 측에서 사용자의 입력 데이터를 검증합니다. 추가로 뷰에서 사용자에게 오류 메시지를 보여줄 때는 컨트롤러 계층에서 @Valid와 BindingResult를 사용해서 검증을 진행할 수 있습니다.
@PostMapping
public String createSample(@Valid @ModelAttribute("sample") SampleDto sampleDto,
BindingResult result) {
if (result.hasErrors()) {
return "sample/create";
}
Long userId = getLoggedInUserId();
smapleService.createSample(sampleDto, userId);
return "redirect:/samples";
}
다만, 컨트롤러에서 @Valid와 BindingResult를 사용할 때에는 코드 작성 순서에 유의해야 하는데, @Valid 어노테이션이 BindingResult 바로 앞에 있어야만 검증을 진행합니다. 순서가 다르다면 검증이 제대로 이루어지지 않습니다!!!
3. HTML5 기본 유효성 검사 사용하기
위에서 설명한 스프링 MVC의 Bean Validation을 사용하지 않고, HTML에서 기본적인 입력 검증을 수행할 수 있습니다.
input 태그 안에 required 속성을 넣어주면 되는데, 아래와 같이 추가해서 사용하시면 됩니다.
<form th:action="@{/todos}" th:object="${todo}" method="post">
<div class="mb-3">
<label for="title" class="form-label">제목 (Title)</label>
<input type="text" class="form-control" id="title" name="title" th:field="*{title}" required>
</div>
</form>
이러면 required 속성이 추가된 input요소는 "이 입력란을 작성하세요."라는 기본 메시지가 사용자에게 노출됩니다.

저 박스 안의 메시지를 커스텀하는 방법은 JavaScript를 사용하면 되는데, 이 글은 자바스크립트를 중심으로 다루는 글이 아니니까 생략하겠습니다. (사실 잘 몰라요 진짜죄송합니다)
4. HTML5 기본 검증 비활성화하기
근데 스프링 MVC의 Bean Validation 어노테이션을 사용해서 맞춤 오류 메시지를 사용하려면 HTML5의 기본 검증을 비활성화해야 합니다. 저도 사실 알고 싶지 않았는데, 아무 생각 없이 required 속성을 input 태그 안에 넣어서 작성 폼을 만들었다가 제가 설정한 오류 메시지가 출력되지 않는 것을 보고 적잖이 당황했습니다...
해결 방법은 사실 너무 간단합니다.
- input 태그 안에 추가한 required 속성을 삭제하기
- required 속성을 다 찾아다니면서 삭제하기 귀찮다면, form 태그에 novalidate 속성을 추가하기
둘 중 어떤 방법이 덜 귀찮은지 판단해서 해결하시면 될 것 같습니다. 쓰면서도 너무 간단해서 머쓱해지네요 😅
저 같은 경우 사용한 input 태그가 좀 많아서, 그냥 form 태그에 novalidate 속성을 추가해 줬습니다. 이렇게 추가해 두면, 서버 측에서 설정한 맞춤 오류 메시지를 표시할 수 있게 됩니다.

아무튼 이런 검증 방식을 통해 사용자 경험을 향상시키고, 클라이언트와 서버 측에서 일관된 검증 환경을 제공할 수 있습니다.
5. 결론
사용자의 입력 데이터를 검증할 때, 커스텀 메시지를 보여줄 필요가 없다면, HTML5에서 제공하는 required 속성을 사용해서 입력값을 검증하고, 사용자에게 기본 오류 메시지를 보여줄 수 있습니다.
Spring 개발 환경에서 각 항목별로 메시지를 커스텀해서 보여주고 싶다면 DTO클래스에서 Bean Validation 어노테이션을 사용해서 필드별로 오류 발생 시 노출되는 메시지를 설정할 수 있습니다.
또한, javax.validation 또는 jakarta.validation 패키지에서 제공하는 다양한 어노테이션을 조합해서 사용자가 입력하는 데이터의 유효성 검사를 편리하게 진행할 수 있습니다.
오늘의 정리 끝! 그럼 안녕~
🍀
좋아하는 것을 계속 좋아하세요!
반드시 행복해집니다
[Github] https://github.com/chujaeyeong
[E-mail] chujy1224@gmail.com
'Study > Spring' 카테고리의 다른 글
| Redis 라이브러리를 활용해서 동시성 제어하기 (Lettuce, Redisson) (2) | 2024.08.17 |
|---|---|
| MySQL에서 제공하는 Lock을 이용해서 동시성 제어하기 (Pessimistic Lock, Optimistic Lock, Named Lock) (0) | 2024.08.17 |
| 선착순 쿠폰 발급 시스템 로직 변경으로 Redis의 Set 자료구조 찍먹해보기 (0) | 2024.07.02 |
| 선착순 쿠폰 발급 시스템 개발을 통해 Redis 랑 Kafka 찍먹해보기 (2) | 2024.07.01 |
| [Spring] Validation (검증) 처리 방법 및 properties 파일의 한글이 출력되지 않는 문제 해결 방법 (2) | 2023.10.12 |