728x90
반응형
글로벌 Validator 설정과 @Valid 애노테이션
글로벌 Validator 적용방법
- 설정 클래스에서 WebMvcCOnfigurer의 getValidator() 메서드가 Validator 구현 객체를 리턴하도록 구현
- 글로벌 범위 Validator가 검증할 커맨드 객체에 @Valid 애노테이션 적용
package config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import controller.RegisterRequestValidator;
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
...
// Validator 구현객체 리턴
@Override
public Validator getValidator() {
return new RegisterRequestValidator();
}
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource ms =
new ResourceBundleMessageSource();
ms.setBasenames("message.label");
ms.setDefaultEncoding("UTF-8");
return ms;
}
}
스프링 MVC는 WebMvcConfigurere 인터페이스의 getValidator() 메서드가 리턴한 객체를 글로벌 Validator로 사용
지정은 @Valid 애노테이션을 사용해서 Validator 적용
ReigsterRequestValidator
package controller;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import spring.RegisterRequest;
public class RegisterRequestValidator implements Validator {
private static final String emailRegExp = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
+ "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
private Pattern pattern;
public RegisterRequestValidator() {
pattern = Pattern.compile(emailRegExp);
}
@Override
public boolean supports(Class<?> clazz) {
return RegisterRequest.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
//검사대상 target을 실제 타입으로 변환 후 값 검사
RegisterRequest regReq = (RegisterRequest) target;
// 값 검사
if (regReq.getEmail() == null || regReq.getEmail().trim().isEmpty()) {
//email 프로퍼티 값이 존재하지 않으면 -> null or 빈 문자열
//email 프로퍼티의 에러 코드로 "required" 추가
errors.rejectValue("email", "required");
} else {
//이메일 정규표현식으로 올바른 이메일 형식인지 확인
Matcher matcher = pattern.matcher(regReq.getEmail());
if (!matcher.matches()) {
//정규 표현식이 일치하지 않으면, "email" 프로퍼티에 "bad" 추가
errors.rejectValue("email", "bad");
}
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "required");
ValidationUtils.rejectIfEmpty(errors, "password", "required");
ValidationUtils.rejectIfEmpty(errors, "confirmPassword", "required");
if (!regReq.getPassword().isEmpty()) {
if (!regReq.isPasswordEqualToConfirmPassword()) {
errors.rejectValue("confirmPassword", "nomatch");
}
}
}
}
Valid애노테이션을 이용한 ReigsterController
@Valid 애노테이션을 사용하기 위해서는 validation-api 모듈을 추가
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
package controller;
import javax.validation.Valid;
...
@Controller
public class RegisterController {
....
@PostMapping("/register/step3")
public String handleStep3(@Valid RegisterRequest regReq, Errors errors) {
if (errors.hasErrors()) {
return "register/step2";
}
try {
memberRegisterService.reigst(regReq);
return "register/step3";
} catch (DuplicateMemberException ex) {
//이메일 중복 익셉션 발생 시, "email" 프로퍼티 에러코드 "duplicate" 추가
errors.rejectValue("email", "duplicate");
return "register/step2";
}
}
}
RegisterController의 handleStep3() 메서드는 RegisterRequest 타입 커맨드 객체를 사하고 이 파라미터에 @Valid 애노테이션을 붙여서 글로벌 범위 Validator를 적용
[요청 메서드 실행 전]
커맨드 객체에 해당하는 파라미터에 @Valid 애노테이션을 붙이면 글로벌 범위 Validator가 해당 타입을 검증가능한지 확인
검증 가능하다면, 그 결과를 Errors에 저장
- handleStep3() 메서드 실행 전 @Valid 애노테이션이 붙은 regReq 파라미터를 글로벌 범위 Validator로 검증
- 글로벌 Validator가 ReigsterRequest 타입을 지원하므로 regReq 파라미터로 전달되는 커맨드 객체에 대한 검증 수행, 결과는 Errors 타입 파라미터로 받음
- 이 모든 과정은 handleStep3() 메서드가 실행 되기전에 이뤄지므로 handleStep3() 메서드가 RegisterRequest 객체를 검증하는 코드를 작성할 필요가 없음
- 파라미터로 전달 받은 Errors를 이용해서 검증 에러가 존재하는지 확인만 하면 된다
@Valid 애노테이션을 사용할 때 주의점
- Errors 타입 파라미터가 없음녀 검증 실패 시 400 에러를 응답
@PostMapping("/register/step3")
public String handleStep3(@Valid RegisterRequest regReq) {
if (errors.hasErrors()) {
return "register/step2";
}
try {
memberRegisterService.reigst(regReq);
return "register/step3";
} catch (DuplicateMemberException ex) {
//이메일 중복 익셉션 발생 시, "email" 프로퍼티 에러코드 "duplicate" 추가
errors.rejectValue("email", "duplicate");
return "register/step2";
}
}
글로벌 Validator의 범용성
- RegisterRequestValidator 클래스는 RegisterRequest 타입의 객체만 검증할 수 있으므로 모든 컨트롤러에 적용할 수 있는 글로벌 범위 Validator로 부적합
- 스프링 MVC는 자체적으로 제공하는 글로벌 Validator가 존재하고, 이 Validator를 사용하면 Bean Validation이 제공하는 애노테이션을 이용해서 값을 검증할 수 있다.
728x90
반응형
'Spring' 카테고리의 다른 글
[Spring] Bean Validation 검증 (0) | 2024.05.18 |
---|---|
[Spring] 컨트롤러 범위 Validator @InitBinder (0) | 2024.05.18 |
[Spring] 커맨드 객체의 에러 메시지 출력하기 (0) | 2024.05.14 |
[Spring] Errors 와 ValidationUtils 클래스의 주요 메서드 (0) | 2024.05.13 |
[Spring] 커맨드 객체의 값 검증과 에러 메시지 처리 1 (0) | 2024.05.11 |