안녕하세요 개발자 문문입니다.
오늘은 Spring MVC의 메시지, 커맨드 객체 검증에 대해 공부하겠습니다.
- <spring:message> 태그
- 만약 어느 사이트의 언어를 다국어 처리해야 한다고 가정했을때, 사이트에 적혀있는 모든 글자를 다국어 처리하는건 쉽지 않습니다. (페이지를 여러개 만들거나, 다국어 로직을 만들어야 함)
- 위와 같은 상황이 왔을때 각 나라별 메시지를 미리 파일로 만들어 놓고 그 메시지를 불러오기만 한다면 한 페이지에서 간단하게 처리가 가능할 것 입니다.
- 스프링에서는 아래와 같은 작업을 진행하면 이런 기능을 구현할 수 있습니다.
1. 문자열을 담은 메시지 파일 생성
- src/main/resourece/message에 label.properties 생성
member.register = 회원 가입
term = 약관
term.agree = 약관동의
next.btn = 다음단계
2. MessageSource 빈 설정
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer{
...
@Bean
public MessageSource messageSource(){
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasenames("message.label");
ms.setDefaultEncoding("UTF-8");
return ms;
}
- ResourceBundleMessageSource : Spring에서 국제화 메시지(properties 파일 기반)를 처리하기 위해 사용하는 메시지 소스
3. <spring:message> 태그를 사용해서 메시지 출력
~
<%@ taglib prefix="spring" url="http://www.springframework.org/tagss" %>
<!DOCTYPE html>
~
<head>
<title><spring:message code"member.register"/><title>
</head>
~
- 이렇게 사용하면 title에 회원 가입이라고 출력됩니다.
- 커맨드 객체의 값 검증과 에러 메시지 처리
- 스프링에서 커맨드 객체의 값이 올바른지 검사하려면 다음의 두 인터페이스를 사용합니다.
- org.springframework.validation.Validator
- org.springframework.validation.Errors
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) {
System.out.println("RegisterRequestValidator#validate(): " + this);
RegisterRequest regReq = (RegisterRequest) target;
if (regReq.getEmail() == null || regReq.getEmail().trim().isEmpty()) {
errors.rejectValue("email", "required");
} else {
Matcher matcher = pattern.matcher(regReq.getEmail());
if (!matcher.matches()) {
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");
}
}
}
}
- supports (): 파라미터로 전달 받은 clazz 객체가 RegisterRequest 클래스로 타입 변환이 가능한지 확인하는 역할을 합니다.
- validate(Object target, Errors errors)
- target : 검사 대상 객체
- errors : 검사 결과 에러 코드 설정 객체 (errors.rejectValue()로 에러코드 저장)
- errors.rejectValue("프로퍼티명","에러코드") : 첫 번째 파라미터의 이름을 가진 값이 존재하지 않으면 해당 프로퍼티의 에러코를 "에러코드"로 지정합니다.
- ValidateUtills.rejectIfEmptyOrWhitespace(Error errors,String field, String errorCode) : field에 해당하는 프로퍼티 값이 null이거나 빈 문자열이면 에러코드로 errorCode를 추가합니다.
- 에러코드 추가 메서드에서 target을 사용하지 않고 프로퍼티 값을 검사할 수 있는 이유 : 커맨드 객체 파라미터 뒤에 Errors 타입 파라미터가 위치하면, 스프링 MVC는 메서드 호출시 커맨드 객체와 연결된 Errors 객체를 생성하기 때문입니다. 그렇기 때문에 Errors의 위치는 반드시 커맨드 객체 뒤에 있어야 합니다.
@PostMapping("/register/step3")
public String handleStep3(RegisterRequest regReq, Errors errors){
new RegisterRequestValidator().validate(regReq, errors);
if(errors.hasErrors())
return "register/step2";
try{
memberRegisterService.regist(regReq);
return "register/step2";
} catch(DuplicationMemberException ex) {
errors.rejectValue("email","duplicate");
return "register/step2";
}
}
- errors.hasErrors() : validate()를 실행하는 과정에서 유효하지 않은 값이 존재하면 Errors의 rejectValue() 메서드를 실행하고 hasErrors() 메서드는 true를 반환합니다.
- 만약 프로퍼티 값이 아니라 커맨드 객체 자체가 잘못 되면 rejectValue() 대신 reject()를 사용하는데 이때 추가된 에러를 글로벌 에러라고 합니다.
errors.reject("notMatchingPassword");
- 에러 메시지 출력
<form:errors path="name"/>
- path : 에러 메시지를 출력할 프로퍼티명을 지정합니다.
- name 프로퍼티에 에러 코드가 존재하면, 에러 코드에 해당하는 메시지를 출력합니다.
- 에러 코드에 맞는 메시지 지정 (label.properties)
member.register = 회원 가입
term = 약관
term.agree = 약관동의
next.btn = 다음단계
required=필수항목입니다.
bad.email=이메일이 올바르지 않습니다.
duplicate.email=중복된 이메일입니다.
nomatch.confirmPassword=비밀번호와 확이닝 일치하지 않습니다.
- 글로벌 범위, 컨트롤러 범위 Validator
- 글로벌 범위 Validator 설정
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer{
@Override
public Validator getValidator() {
return new RegisterRequestValidator();
}
}
@Controller
public class RegisterController {
...
@PostMapping("/register/step3")
public String handleStep3(@Valid RegisterRequest regReq, Errors errors) {
if(errors.hasErrors())
...
}
}
- @Valid 애노테이션을 붙이면 메서드 실행 전 글로벌 범위의 Validator가 해당 타입을 검증할 수 있는지 확인하고, 검증 수행 결과를 Errors에 저장합니다.
- 그렇기 때문에 위에서 사용했던 검증 메서드 validate()는 사용하지 않아도 됩니다.
- 컨트롤러 범위 Validator 설정
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new RegisterRequestValidator());
}
- @InitBinder 애노테이션을 적용한 메서드는 WebDataBinder 타입 파라미터를 갖는데, setValidator() 메서드를 통해 컨트롤러 범위에 적용할 Validator를 설정할 수 있습니다.
- Bean Validation을 이용한 값 검증 처리
- @Size , @NotBlank, @Email, @NotEmpty와 같은 Bean Validation을 사용하려면 OptionalValidatorFactoryBean클래스를 빈으로 등록해야합니다.
- @EnableWebMvc가 OptionalValidatorFactoryBean을 글로벌 범위 Validator로 등록해주기 때문에 @EnableWebMvc만 설정 클래스에 붙여주면 됩니다.
- 단, 설정 클래스에 별도로 설정한 Validator가 없어야 사용가능합니다.
public class RegisterRequest{
@NotBlank
@Email
private String email;
...
- label.properties
NotBlank=필수 항목입니다. 공백 문자는 허용하지 않습니다.
NotEmpty=필수 항목입니다.
Size.password=암호 길이는 6자 이상이어야 합니다.
Email=올바른 이메일 주소를 입력해야 합니다.'Spring 개념정리' 카테고리의 다른 글
| [Spring] MVC : 날짜 값 변환, @PathVariable, 익셉션 처리 (1) | 2025.07.08 |
|---|---|
| [Spring] MVC : 세션, 인터셉터, 쿠키 (1) | 2025.07.07 |
| [Spring] MVC : 요청 매핑, 커맨드 객체, 리다이렉트, 모델 (2) | 2025.07.06 |
| [Spring] 스프링 MVC 프레임워크 동작 방식 (0) | 2025.07.02 |
| [Spring] AOP(Aspect Oriented Programming) (0) | 2025.07.01 |