스프링부트 검증기능 사용하기(Bean Validation)
들어가기에 앞서
웹 페이지를 만들 때 필수적으로 들어가는 요소 중 하나가 '검증'이다. 그런데 검증이라는 것이 만들면 만들 수록 끝이 없고 반복되는 작업이 계속해서 생기기 마련이다. 그리고 일반적으로 코드를 직접 작성해서 검증 기능을 구현하면 컨트롤러에 구현하게 되고 그렇게 되면 컨트롤러가 꽤나 지저분해질 수 있다.
Spring에서는 이 검증기능을 지원해주는 기능이 있다. SpringBoot를 이용해서 쉽고 편하게 검증기능을 사용하는 방법을 알아보자.
사용준비
먼저 gradle에 validation관련 라이브러리를 추가해준다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
추가 후 build를 하면 준비가 완료된 것이다.
검증기능 사용하기
이제 데이터를 넣기 위한 객체를 하나 만들어야 한다.
@Getter
@Setter
public class Member {
private String username;
private String password;
private int phone;
}
getter와 setter는 롬북을 이용해서 생성했다.
이제 변수에 원하는 검증코드를 추가하면된다.
@Getter
@Setter
public class Member {
@NotBlank(message = "아이디를 입력하세요.")
private String username;
@NotBlank
@Length(max = 16)
private String password;
@NotNull
private int phone;
}
설명을 조금 하자면
- @NotBlank : 빈 값 + 공백만 있는 경우를 허용하지 않음
- @NotNull : null을 허용하지 않음
- @Length : 길이의 최소, 최대값을 지정할 수 있음
그냥 이 상태로 에러메세지를 출력하면 스프링에서 지정한 메세지가 출력되지만 @NotBlank처럼 message를 사용하면 원하는 메세지 내용을 집접 만들 수 있다.
검증코드의 종류가 정말 많은데 자세한 내용은 아래의 사이트에서 확인하면서 필요한 것을 가져다 쓰면 될 것 같다.
물론 검증코드를 작성한다고 Member객체를 사용하는 모든 코드에서 검증을 하는 것은 아니다. 컨트롤러에서 어노테이션을 하나 추가해주어야한다.
@Controller
public class ValidationController {
@GetMapping("/validation/test")
public String validation(Model model) {
model.addAttribute("member", new Member());
return "validationTest";
}
@PostMapping("/validation/test")
public String validation(@Validated @ModelAttribute Member member, BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
return "validationTest";
}
return "redirect:/validationOk";
}
}
@Validated를 추가해주면 된다. @Valid를 사용하기도 하는데 2개 중 어느것을 사용해도 정상적으로 동작하니 원하는 것을 선택해서 사용하면 된다.(@PostMapping부분이 에러를 받아서 처리하는 부분이다.)
▼ @Valid와 @Validated차이점 알아보기
2개의 차이점을 간단하게 이야기하자면
- @Valid : 자바에서 지원하는 표준 어노테이션
- @Validated는 스프링에서 지원하는 어노테이션
그렇기 때문에 만약 코드는 코드는 유지한 채 스프링이 아닌 다른 것으로 옮겨간다면 @Validated는 사용 할 수 없을 것이다.
그렇지만 이후에 배울 group이라는 기능은 @Validated에서만 지원하고 있기때문에 이 기능을 사용하기 위해서는 @Validated를 사용해야 한다.
(group기능은 포스팅 마지막부분에 있다.)
간단한 검증코드를 사용하는데에는 둘 중 어느것을 사용해도 무방하니 원하는 것을 사용하도록 하자.
이제 간단한 회원가입 페이지를 만든 뒤 테스트해보자.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<style>
.formBox {width: 500px; margin:0 auto;}
.field-error {
border-color: #dc3545;
color: #dc3545;
}
</style>
</head>
<body>
<div class="container">
<form action="/validation/test" th:object="${member}" method="post" class="formBox">
<div>
<label for="username">이름</label>
<input type="text" id="username" th:field="*{username}"
th:errorclass="field-error" class="form-control" placeholder="이름을 입력하세요">
<div class="field-error" th:errors="*{username}">
이름 오류
</div>
</div>
<div>
<label for="password">비밀번호</label>
<input type="password" id="password" th:field="*{password}"
th:errorclass="field-error" class="form-control" placeholder="비밀번호을 입력하세요">
<div class="field-error" th:errors="*{password}">
비밀번호 오류
</div>
</div>
<div>
<label for="phone">휴대폰번호</label>
<input type="text" id="phone" th:field="*{phone}"
th:errorclass="field-error" class="form-control" placeholder="휴대폰번호를 입력하세요">
<div class="field-error" th:errors="*{phone}">
휴대폰번호 오류
</div>
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit">가입하기</button>
<button>취소</button>
</div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
feild-error클래스를 추가해서 나타나는 에러메세지는 빨간색글씨로 나타나고 input박스는 빨간색으로 색칠되도록 설정했다.
사진을 보면 가입하기 버튼을 눌렀을 때 에러가 나타는 것을 알 수 있다.
이때 '이름' 부분은 객체에서 직접 입력한 메세지가 나타났고, '비밀번호'는 스프링에서 제공하는 메세지가 잘 나타났는데 '휴대폰번호'는 전혀 엉뚱해보이는 메세지가 나타난 것을 볼 수 있다.
이것은 휴대폰번호의 타입을 int로 설정했는데 문자타입이 입력이 됐기 때문이다. 이런 타입을 자동으로 잡아주는 것은 BindingResult덕분이다. 자세한 내용은 BindingResult관련 포스터를 참고하기 바란다.
여기서 한 가지 더 고민해야 할 것이 있다. 다른 페이지에서 같은 Member객체를 사용해서 검증을 하는 경우이다.
물론 검증의 조건이 두 페이지 모두 동일하면 문제가 생기지 않겠지만 만약 조금이라도 검증의 조건이 다르다면 두 페이지 모두 한 객체에서 설정한 검증조건을 바라보고있기 때문에 문제가 생길 수 있다.
이때 사용하는 것이 group기능이다. group은 검증조건을 사용하고자 하는 그룹으로 지정해서 사용할 수 있게 하는 기능이다. 코드로 살펴보자.
먼저 그룹으로 사용 할 interface를 만들어줘야 한다.
public interface SaveCheck {
}
public interface UpdateCheck {
}
특별히 코드를 넣을 필요는 없다.
그리고 Member객체의 코드를 조금 수정해주도록 하자.
@Getter
@Setter
public class Member {
@NotBlank(message = "아이디를 입력하세요.(저장)", groups = SaveCheck.class)
@NotBlank(message = "아이디를 입력해주세요.(수정)", groups = UpdateCheck.class)
private String username;
@NotBlank
@Length(max = 16)
private String password;
@NotNull
private int phone;
}
실제로 이런식으로 코드를 작성하지는 않겠지만 테스트용 코드라서 간단하게 작성했다.
코드를 살펴보면 username변수에 지정된 @NotBlank안에 message말고 group이 추가된 것을 볼 수 있다. 이 그룹에서 조금전에 만든 인터페이스명을 입력해주면 내가 지정한 그룹에서만 사용이 가능하도록 된다.
group을 따로 지정하지 않은 변수들은 에러메세지가 나타나지 않을 것이다. 마지막에 테스트하면서 확인해보도록 하자.
마지막으로 컨트롤러의 코드도 수정해주자
...
@GetMapping("/validation/update")
public String validationUpdate(Model model) {
model.addAttribute("member", new Member());
return "validationUpdateTest";
}
@PostMapping("/validation/update")
public String validationUpdate(@Validated(UpdateCheck.class) @ModelAttribute Member member, BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
log.info("errors = {}" , bindingResult);
return "validationUpdateTest";
}
return "validationOk";
}
@PostMapping("/validation/test")
public String validation(@Validated(SaveCheck.class) @ModelAttribute Member member, BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
log.info("errors = {}" , bindingResult);
return "validationTest";
}
return "validationOk";
}
@Validated옆에 SaveCheck.class가 추가 된 것을 볼 수 있다. 이렇게 추가함으로서 Member객체에서 group을 SaveCheck로 등록한 검증만 사용 할 수 있게 된다.
UpdateCheck.class를 검증 할 수 있는 코드도 함께 추가했다.
이제 확인해보자.
두 사진에서 볼 수 있듯이 '이름'에 지정한 에러메세지는 group으로 나눠서 지정한 메세지가 나타나고, group으로 지정하지 않은 부분은 메세지가 나타나지 않는 것을 알 수 있다.
'Spring > Boot' 카테고리의 다른 글
스프링 설정 환경 분리 (0) | 2022.06.18 |
---|---|
ExceptionResolver를 이용한 API예외 처리 (0) | 2022.05.25 |
댓글
이 글 공유하기
다른 글
-
스프링 설정 환경 분리
스프링 설정 환경 분리
2022.06.18 -
ExceptionResolver를 이용한 API예외 처리
ExceptionResolver를 이용한 API예외 처리
2022.05.25