유효성 검사
- 폼 데이터가 적합한지 체크하는 기능은 스프링이 제공해주는 Validator 인터페이스를 구현하거나, JSR 303 Validation을 사용하는 방법이 있다.
- 백기선님의 스프링 강좌 내용을 가져와 예시로 적용하였다.
회원가입 폼 검증 - JSR 303 어노테이션 검증
값의 길이나 필수값 등을 검증한다.
AccountController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class AccountController {
private final SignUpFormValidator signUpFormValidator;
"signUpForm") // signUpForm 이라는 데이터를 받을 때 바인더를 설정 (
public void initBinder(WebDataBinder webDataBinder){
// Validator를 추가
// SignUpForm 의 타입과 매핑이되어 Validator가 사용됨.
webDataBinder.addValidators(signUpFormValidator);
}
"/sign-up") (
public String signUpForm(Model model){
model.addAttribute("signUpForm", new SignUpForm());
return "account/sign-up";
}
"/sign-up") // 파라미터에서는 @ModelAttribute 생략 가능 (
public String signUpSubmit(@Valid @ModelAttribute SignUpForm signUpForm, Errors errors) {
// @Valid : jsr 303 어노테이션들의 조건을 만족하는지 확인
if (errors.hasErrors()) {
return "account/sign-up";
}
// @InitBinder로 대체
/* signUpFormValidator.validate(signUpForm, errors);
if (errors.hasErrors()) {
return "account/sign-up";
}*/
// TODO 회원 가입 처리
return "redirect:/";
}
}@ModelAttribute
매개변수로 선언하는 경우
- 파라미터로 넘겨 준 타입의 오브젝트를 자동으로 생성 (이때,
@ModelAttribute
가 지정되는 클래스는 getter와 setter가 명명 규칙에 맞게 만들어져 있어야 한다.) - 생성된 오브젝트에 HTTP로 넘어온 값들을 자동으로 바인딩한다. 위의 코드에서는 SignUpForm에 있는 nickname, email, password 속성 값들이 해당 변수의 setter를 통해 해당 멤버 변수에게로 binding 된다.
@ModelAttribute
어노테이션이 붙은 객체가 자동으로 Model객체에 추가된다.- 위와 같이 파라미터에 붙이는 경우에는 생략이 가능하다.
- 파라미터로 넘겨 준 타입의 오브젝트를 자동으로 생성 (이때,
메소드에 선언하는 경우
- View에서 사용할 데이터를 설정하는 용도로 사용
@ModelAttribute
가 설정된 메소드는@RequestMapping
어노테이션이 적용된 메소드보다 먼저 호출@ModelAttribute
메소드 실행 결과로 리턴되는 객체는 자동으로 Model에 저장@ModelAttribute
메소드 실행 결과로 리턴된 객체를 View 페이지에서 사용 가능
@Valid
: 요청 데이터를 검증하는 어노테이션@Valid
를 이용한 자동 검증- 컨트롤러 메소드의
@ModelAttribute
파라미터에@Valid
애노테이션을 추가한다. 그러면 validate() 메소드를 실행하는 대신 바인딩 과정에서 자동으로 검증이 진행된다. - Validation 과정에서 실패하거나 에러가 발생하면
Errors
에 에러들이 담기게 된다.errors.hasErrors()
를 이용하여 에러 발생 시 예외처리를 할 수 있다.
- 컨트롤러 메소드의
SignUpForm.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SignUpForm {
// 비어있는 값이면 안됨.
3, max = 20) // 문자열의 길이 지정 3 ~ 20 (min =
"^[ㄱ-ㅎ가-힣a-z0-9_-]{3,20}$") // 정규표현식으로 사용 가능한 패턴 지정 (regexp =
private String nickname;
// email 형식의 문자열
private String email;
8, max = 50) (min =
private String password;
}@Data
: 롬복이 제공하는 어노테이션,@getter
,@setter
,@RequiredArgsConstructor
,@equalsAndHashcord
,@ToString
을 한번에 설정해 주는 간축형 어노테이션
- 유효성 검사 어노테이션
@NotBlank
: 문자열이나 배열의 경우 null이 아니고 길이가 0이 아닌지 검사@NotNull
: 변수 값이 null인지 아닌지 검사@Pattern(regexp = )
: 변수 값이 정규표현식을 만족하는지 검사@Email
: 이메일 형식을 만족하는지 검사@Size(min=, max=)
: 문자열, 배열 등의 크기가 지정된 크기를 만족하는지 검사@Length(min=, max=)
: 문자열의 길이가 지정된 크기를 만족하는지 검사(Hibernate 제공)@Past
: 해당 시간이 과거의 시간인지 검사@Future
: 해당 시간이 미래의 시간인지 검사@AssertTrue
: 변수 값이 true인지 검사@AssertFalse
: 변수 값이 flase인지 검사
커스텀 검증 - Validator 인터페이스 구현
Spring은 도메인 객체를 검증할 수 있도록 Validator 인터페이스를 도입했다. Validator 인터페이스는 객체를 검증하는데 실패하면 Errors 객체에 에러를 등록함으로써 동작한다.
이메일, 닉네임 중복 확인을 위해
Validator
인터페이스를 구현한다.SignUpFormValidator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
// lombok이 제공하는 어노테이션, private final 타입의 맴버 변수의 생성자를 만들어준다.
public class SignUpFormValidator implements Validator {
private final AccountRepository accountRepository;
public boolean supports(Class<?> clazz) {
// SignUpForm 타입의 인스턴스를 검사
return clazz.isAssignableFrom(SignUpForm.class);
}
public void validate(Object target, Errors errors) {
// 이메일, 닉네임 중복 검사
SignUpForm signUpForm = (SignUpForm)target;
if (accountRepository.existsByEmail(signUpForm.getEmail())){
errors.rejectValue("email", "invalid.email", new Object[]{signUpForm.getEmail()}, "이미 사용중인 이메일입니다.");
}
if (accountRepository.existsByNickname(signUpForm.getNickname())){
errors.rejectValue("nickname", "invalid.nickname", new Object[]{signUpForm.getNickname()}, "이미 사용중인 닉네임입니다.");
}
}
}Validator
인터페이스는 두 가지 메서드를 가지고 있다.supports(Class)
: 매개변수로 전달된 클래스를 검증할 수 있는지 여부를 반환validate(Object, Errors)
: 매개변수로 전달된 객체를 검증하고 실패하면 Errors객체에 에러를 등록org.springframework.validation.Errors
임을 주의
@RequiredArgsConstructor
: lombok이 제공하는 어노테이션, private final 타입의 맴버 변수의 생성자를 만들어준다.- 다음과 같은 의미
1
2
3public SignUpFormValidator(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
- 다음과 같은 의미
(스프링 4.2 이후 어떤 빈이 생성자가 하나이고 그 생성자가 받는 파라미터들이 빈으로 등록이 되어있다면 자동으로 빈을 주입해주기 때문에 @Autowired @Inject 없이도 의존성 주입이 됨.)
AccountRepository.java
1
2
3
4
5
6true) (readOnly =
public interface AccountRepository extends JpaRepository<Account, Long> {
boolean existsByEmail(String email);
boolean existsByNickname(String nickname);
}@Transactional(readOnly = true)
: 트랜잭션을 읽기 전용으로 설정 (해당 nickname, mail의 존재 여부만 확인)
참조
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1/dashboard
https://webcoding.tistory.com/entry/Spring-JSR-303-%EC%9C%BC%EB%A1%9C-%EA%B0%9D%EC%B2%B4-%EA%B0%92-%EA%B2%80%EC%A6%9D%ED%95%98%EA%B8%B0
https://velog.io/@junwoo4690/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8%EC%97%90%EC%84%9C-request-Validation-%EC%9A%94%EC%B2%AD%EA%B0%92-%EA%B2%80%EC%A6%9D%ED%95%98%EA%B8%B0
https://velog.io/@junwoo4690/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8%EC%97%90%EC%84%9C-request-Validation-%EC%9A%94%EC%B2%AD%EA%B0%92-%EA%B2%80%EC%A6%9D%ED%95%98%EA%B8%B0
https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte2:ptl:jsr303