[Spring] 스프링 MVC 2 : 메시지, 커맨드 객체 검증
2023. 12. 8.
반응형

 

 

이번 포스팅은 메시지를 출력하는 방법과 커맨드 객체의 값을 검증하는 방법에 대해 살펴보겠습니다.

 

사용자 화면에 보이는 문자열을 JSP에 직접 하드 코딩하면 화면을 수정해야 할 때 큰 공수가 듭니다.

예를 들어 '이메일' -> '이메일 주소' 로 바꾸기로 했다면 텍스트를 전부 찾아 수정해 줘야 합니다.

 

또한 다국어를 지원하는 경우에도 언어 설정에 따라 텍스트를 바꿔줘야 할 때도 마찬가지로 번거로워집니다.

이를 해결하기 위해 뷰 코드에서 사용할 문자열을 언어별로 파일에 보관하고,

뷰 코드는 언어에 따라 알맞은 파일에서 문자열을 읽어와 출력할 수 있습니다.

 

스프링은 자체적으로 이 기능을 제공하고 있기 때문에 각 상황에 맞게 문자열을 JSP 코드를 출력할 수 있습니다.

 

 

 

 

반응형

 

 

 

 

 

이를 위해 문자열을 담을 메시지 파일을 작성해 보겠습니다.

src/main/resources/message 폴더에 label.properties 파일을 만들고 안에 다음과 같이 작성합니다.

 

member.register=회원가입

term=약관
term.agree=약관동의
next.btn=다음단계

member.info=회원정보
email=이메일
name=이름
password=비밀번호
password.confirm=비밀번호 확인
register.btn=가입 완료

register.done=<strong>{0}님 ({1})</strong>, 회원 가입을 완료했습니다.

go.main=메인으로 이동

required=필수항목입니다.
bad.email=이메일이 올바르지 않습니다.
duplicate.email=중복된 이메일입니다.
nomatch.confirmPassword=비밀번호와 확인이 일치하지 않습니다.

 

 

그럼 다음으로 MvcConfig에 MessageSource 타입의 빈을 추가하겠습니다.

 

MvcConfig.java

 

@Bean
public MessageSource messageSource() {
	ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
	ms.setBasenames("message.label");
	ms.setDefaultEncoding("UTF-8");
	return ms;
}

 

 

"message.label"은 message 패키지에 속한 label 프로퍼티 파일로부터 메시지를 읽어온다고 설정한 것입니다.

아까 만든 message폴더에 lable.properties 파일에서 메시지를 읽을 수 있게 설정한 것입니다.

setBasenames() 메소드는 가변 인자이므로 사용할 메시지 프로퍼티 목록을 전달할 수 있습니다.

또한 defaultEncoding 속성으로 UTF-8로 인코딩을 설정해 줬습니다.

주의할 점은 빈의 아이디를 messageSource로 지정해야 한다는 것입니다.

(다른 이름으로 할 경우 정상작동 하지 않음)

 

 

 

 

반응형

 

 

 

 

 

이제 MessageSource를 사용해 메시지를 출력할 JSP 코드를 수정해 보겠습니다.

회원가입에서 사용했던 step1,2,3 페이지를 다음과 같이 수정하겠습니다.

 

 

step1.jsp

 

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="member.register" /></title>
</head>
<body>
    <h2><spring:message code="term" /></h2>
    <p>약관 내용</p>
    <form action="step2" method="post">
    <label>
        <input type="checkbox" name="agree" value="true"> 
        <spring:message code="term.agree" />
    </label>
    <input type="submit" value="<spring:message code="next.btn" />" />
    </form>
</body>
</html>

 

 

step2.jsp

 

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="member.register" /></title>
</head>
<body>
    <h2><spring:message code="member.info" /></h2>
    <form:form action="step3" modelAttribute="registerRequest">
    <p>
        <label><spring:message code="email" />:<br>
        <form:input path="email" />
        <form:errors path="email"/>
        </label>
    </p>
    <p>
        <label><spring:message code="name" />:<br>
        <form:input path="name" />
        <form:errors path="name"/>
        </label>
    </p>
    <p>
        <label><spring:message code="password" />:<br>
        <form:password path="password" />
        <form:errors path="password"/>
        </label>
    </p>
    <p>
        <label><spring:message code="password.confirm" />:<br>
        <form:password path="confirmPassword" />
        <form:errors path="confirmPassword"/>
        </label>
    </p>
    <input type="submit" value="<spring:message code="register.btn" />">
    </form:form>
</body>
</html>

 

 

step3.jsp

 

 

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="member.register" /></title>
</head>
<body>
    <p>
        <spring:message code="register.done">
        	<spring:argument value="${registerRequest.name}" />
        	<spring:argument value="${registerRequest.email}" />
        </spring:message>
    </p>
    <p>
        <a href="<c:url value='/main'/>">
            [<spring:message code="go.main" />]
        </a>
    </p>
</body>
</html>

 

 

<spring:message> 태그를 이용해서 메시지를 출력해 줬고,

이를 사용하기 위해 <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>를 넣어 태그 라이브러리 설정을 추가해 줬습니다.

 

<spring:message> 태그의 code 값은 앞서 작성한 프로퍼티 파일의 프로퍼티 이름과 일치합니다.

code와 일치하는 값을 가진 프로퍼티 값을 출력합니다.

 

예를 들어 label.properties 파일에

"member.register=회원가입"이면

step1.jsp 파일에

"<title><spring:message code="member.register" /></title>"코드는 

"회원가입"이라고 보이게 됩니다.

 

이런 식으로 설정을 완료하고 step1을 주소에 입력하면

 

 

 

이렇게 화면이 보이게 됩니다.

 

스프링은 이처럼 언어에 상관없이 일관된 방법으로 문자열을 관리할 수 있는 MessageSource 인터페이스를 정의하고 있습니다.

getMessage() 메소드의 code 파라미터는 메시지를 구분하기 위한 코드이고, locale 파라미터는 지역을 구분하기 위한 Locale입니다.

이 기능을 사용하면 국내에서 접근하면 한국어로 메시지를 보여주고, 해외에서 접근하면 영어로 메시지를 보여주는 처리를 할 수 있습니다.

 

MessageSource의 구현체로 자바 프로퍼티 파일로부터 메시지를 읽어오는 ResourceBundleMessageSource 클래스를 사용했습니다.

이 클래스는 메시지코드와 일치하는 프로퍼티의 값을 메시지로 제공합니다.

ResourceBundleMessageSource 클래스는 자바의 리소스번들을 사용하기에 해당 프로퍼티 파일이 클래스 패스에 위치해야 합니다.

 

그래서 src/main/resources에 프로퍼티 파일을 위치시켰습니다. 관리의 편의성을 위해 그 안에 message라는 패키지를 만들어 파일을 작성했습니다.

 

앞서 작성한 label.properties 파일을 보면 다음과 같은 프로퍼티를 포함하고 있습니다.

 

register.done=<strong>{0}님 ({1})</strong>, 회원 가입을 완료했습니다.

 

 

괄호의 적힌 것은 인덱스 기반 변수의 값으로 대치되는 부분을 표현한 것입니다.

MessageSource의 getMessage() 메소드는 Object 배열 타입의 파라미터를 사용합니다.

 

ex)

 

Object[] args = new Object[1];
args[0] = "자바";
messageSource.getMessage("register.done", args, Locale.KOREA);

 

 

이처럼 Object 배열을 생성해서 인덱스 기반 변숫값을 전달할 수 있습니다.

<spring:message> 태그를 사용할 때는 arguments 속성을 사용해서 인덱스 기반 변수값을 전달합니다.

 

 

1.
<spring:message code="register.done">
        <spring:argument value="${registerRequest.name}" />
        <spring:argument value="${registerRequest.email}" />
</spring:message>

2.
<spring:message code="register.done" arguments="${registerRequest.name}", "${registerRequest.email}" />

 

 

두 개의 인자를 전달해야 할 때는 위와 같은 방법으로 사용이 가능합니다.

이처럼 코드를 작성하면 "${registerRequest.name}"이 0번 "${registerRequest.email}"이 1번에 인자로 들어가게 됩니다.

 

 

 

 

 

반응형

 

 

 

 

 

 

그럼 이제 커맨드 객체의 값을 검증하고 에러 메시지 처리하는 방법에 대해 알아보겠습니다.

이를 위해서는 커맨드 객체를 검증하고 결과를 에러 코드로 저장해야 합니다.

 

스프링 MVC 커맨드 객체의 값이 올바른지 검사하려면 

 

org.springframework.validation.Errors;
org.springframework.validation.Validator;

 

위의 두 인터페이스를 사용해야 합니다. RegisterRequest 객체를 검증하기 위해 Validator 구현 클래스를 작성해보겠습니다.

 

RegisterRequestValidator.java

 

 

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);
		System.out.println("RegisterRequestValidator#new(): " + this);
	}

	@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 클래스로 타입 변환이 가능한지 확인합니다. 이 예제에서는 supports() 메소드를 직접 실행하지는 않지만 스프링 mvc가 자동으로 검증 기능을 수행하도록 설정하려면 supports() 메소드를 올바르게 구현해야 합니다.

 

validate() 메소드의 target 파라미터는 검사 대상 객체이고, error 파라미터는 검사 결과 에러 코드를 설정하기 위한 객체입니다.

검사 대상 객체의 특정 프로퍼티나 상태가 올바른지 검사하고,

올바르지 않다면 Errors의 rejectValue() 메소드를 이용해서 에러 코드를 저장합니다.

 

위의 코드는 이메일이 빈 문자열이거나 null일 경우 에러코드를 required로, 그 외에 이메일이 정규 표현식으로 확인했을 때 올바르지 않으면 bad라고 설정합니다.

 

 

errors.rejectValue("email", "required");

 

 

이처럼 첫 번째 파라미터로는 프로퍼티의 이름을 전달받고, 두 번째 파라미터로는 에러 코드를 전달합니다.

JSP 코드에서는 여기서 지정한 에러 코드를 이용해서 에러 메시지를 출력합니다.

 

ValidationUtils 클래스는 객체의 값 검증 코드를 간결하게 작성할 수 있도록 도와주는 역할을 합니다.

 

// errors 객체의 getFieldValue("name") 메소드를 실행해서
// 커맨드 객체의 name 프로퍼티 값을 구함
// 따라서 커맨드 객체를 직접 전달하지 않아도 값 검증을 할 수 있음 
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "required");

 

 

검사 대상 객체 name의 프로퍼티가 null, 공백문자로만 되어 있는 경우, name 프로퍼티의 에러코드로 required를 설정하는 코드입니다.

이 Validator를 실행하기 위해 Controller 코드를 수정하겠습니다.

 

RegisterController.java

 

 

@Controller
public class RegisterController {

    private MemberRegisterService memberRegisterService;

    public void setMemberRegisterService(
        MemberRegisterService memberRegisterService) {
        this.memberRegisterService = memberRegisterService;
    }

    @RequestMapping("/register/step1")
    public String handleStep1() {
        return "register/step1";
    }

    @PostMapping("/register/step2")
    public String handleStep2(
        @RequestParam(value = "agree", defaultValue = "false") Boolean agree,
        Model model) {
        if (!agree) {
            return "register/step1";
        }
        model.addAttribute("registerRequest", new RegisterRequest());
        return "register/step2";
    }

    @GetMapping("/register/step2")
    public String handleStep2Get() {
        return "redirect:/register/step1";
    }

    @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/step3";
        } catch (DuplicateMemberException ex) {
            errors.rejectValue("email", "duplicate");
            return "register/step2";
        }
    }

}

 

 

RegisterRequestValidator 객체를 생성하고 validate() 메소드를 실행합니다. 이를 통해 RegisterRequest 커맨드 객체의 값이 올바른지 검사하고, 그 결과를 Erorrs 객체에 담습니다.

이를 통해 erorrs 객체에 에러가 있는지 검사하기 위해 hasErrors() 메소드를 수행합니다.

validate()를 실행하는 과정에서 유효하진 않은 값이 존재하면 catch 블록에서 rejectValue()를 통해 에러코드를 설정해 줍니다.

 

 

 

 

 

반응형

 

 

 

 

 

 

위의 코드에서는 회원가입 시 이메일 중복을 막기 위해 DuplicateMemberException이 발생한 경우 에러 코드를 duplicate로 지정했습니다.

 

커맨드 객체의 특정 프로퍼티가 아닌 커맨드 객체 자체가 잘못될 수도 있습니다. 이런 경우에는 rejectValue() 메소드 대신에 reject() 메소드를 사용합니다.

 

ex) 로그인 아이디와 비밀번호를 잘못 입력한 경우

 

 

try {
	...
} catch (DuplicateMemberException ex) {
    errors.reject("notMatchingIdPassword");
    return "login/loginForm";
}

 

 

reject() 메소드는 개별 프로퍼티가 아닌 객체 자체에 에러 코드를 추가하므로 이 에러를 글로벌 에러라고 부릅니다.

 

Errors 타입 파라미터는 반드시 커맨드 객체를 위한 파라미터 다음에 위치해야 합니다. 

커맨드 객체 앞에 Errors 객체가 위치하게 되면 익셉션이 발생합니다.

 

ex)

 

 

@PostMapping("/register/step3")
public String handleStep3(Errors errors, RegisterRequest regReq) {
	...
}

 

 

 

Errors 인터페이스가 제공하는 에러 코드 추가 메소드는 다음과 같습니다.

 

public void reject(java.lang.String errorCode,
                   java.lang.Object[] errorArgs,
                   java.lang.String defaultMessage)

public void rejectValue(java.lang.String field,
                        java.lang.String errorCode,
                        java.lang.Object[] errorArgs,
                        java.lang.String defaultMessage)

 

에러 코드에 해당하는 메시지가 {0}이나 {1}과 같이 인덱스 기반 변수를 포함하고 있는 경우 Object 배열 타입의 errorArgs 파라미터를 이용해서 변수에 삽입될 값을 전달합니다.

defaultMessage 파라미터를 가진 메소드를 사용하면, 에러 코드에 해당하는 메시지가 존재하지 않을 때 익셉션을 발생시키는 대신 defaultMessage를 출력합니다.

 

 

ValidationUtils 클래스는 rejectIfEmtpy() 메소드와 rejectIfEmptyOrWhitespace() 메소드를 제공합니다.

 

public static void rejectIfEmpty(Errors errors,
                                 java.lang.String field,
                                 java.lang.String errorCode,
                                 java.lang.String defaultMessage)
                                 
rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode)
rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode, Object[] errorArgs)
rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode, Object[] errorArgs, String defaultMessage)
rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode, String defaultMessage)

 

rejectEmpty() 메소드는 field에 해당하는 프로퍼티 값이 null이거나 빈 문자열인 경우 에러 코드로 errorCode를 추가합니다.

rejectIfEmptyOrWhitespace() 메소드는 null이거나 빈 문자열 혹은 공백문자로만 값이 구성된 경우 에러 코드를 추가합니다.

에러 코드에 해당하는 메시지가 {0}과 같이 인덱스 기반 플레이스홀더를 포함하고 있으면 errorArgs를 이용해 메시지의 플레이스홀더에 삽입할 값을 전달합니다.

 

이처럼 에러 코드를 지정하는 이유는 에러 상황에 따른 에러 메시지를 출력하기 위함입니다.

Errors에 에러 코드를 추가하면 JSP는 스프링이 제공하는 <form:errors> 태그를 사용해서 에러에 해당하는 메시지를 출력할 수 있습니다.

 

 

<p>
    <label><spring:message code="email" />:<br>
      <form:input path="email" />
      <form:errors path="email"/>
    </label>
  </p>

 

 

 

path 속성은 에러 메시지를 출력할 프로퍼티 이름을 지정합니다.

에러 코드가 두 개 이상 존재하면 각 에러 코드는 해당하는 메시지가 출력됩니다.

 

에러 코드에 해당하는 메시지 코드를 찾는 규칙

 

  1. 에러코드 + "." + 커맨드객체 이름 + "." + 필드명
  2. 에러코드 + "." + 필드명
  3. 에러코드 +  "." + 필드타입
  4. 에러코드

 

프로퍼티 타입이 list나 목록인 경우 메시지 코드 생성 규칙

 

  1. 에러코드 + "." + 커맨드객체 이름 + "." + 필드명[인덱스].중첩필드명
  2. 에러코드 + "." + 커맨드객체 이름 + "." + 필드명.중첩필드명
  3. 에러코드 + "."  + 필드명[인덱스].중첩필드명
  4. 에러코드 + "." + 필드명.중첩필드명
  5. 에러코드 + "." + 중첩필드명
  6. 에러코드 + "." + 필드타입
  7. 에러코드

 

ex) errors.rejectValue("email", "required")

=> email 프로퍼티에 required 에러 코드 추가, 커맨드 객체 이름 "registerRequest"

 

  1. required.registerRequest.email
  2. required.email
  3. required.String
  4. required

 

이 중에서 먼저 검색되는 메시지 코드를 사용합니다. (우선순위 높은 순)

 

 

 

 

 

반응형

 

 

 

 

메시지를 찾을 때는 MessageSource를 사용하므로 에러 코드에 해당하는 메시지를 메시지 프로퍼티파일에 추가해주어야 합니다.

RegisterRequestValidator 클래스와 RegisterController 클래스에서 사용한 에러 코드에 맞는 메시지를 추가해 주겠습니다.

 

label.properties

 

required=필수항목입니다.
bad.email=이메일이 올바르지 않습니다.
duplicate.email=중복된 이메일입니다.
nomatch.confirmPassword=비밀번호와 확인이 일치하지 않습니다.

 

 

 

에러 코드가 잘 출력되는 모습입니다.

 

<form:errors> 커스텀 태그는 프로퍼티에 추가한 에러 코드 개수만큼 에러 메시지를 출력합니다.

 

<form:errors path="userId" element="div" delimeter="" />

 

 

element: 각 에러 메시지를 출력할 때 사용할 HTML태그. (기본 값은 span)

delimiter: 각 에러 메시지를 구분할 때 사용할 HTML태그. (기본 값은 <br/>)

 

path 속성을 지정하지 않으면 글로벌 에러에 대한 메시지를 출력합니다.

 

스프링 MVC는 모든 컨트롤러에 적용할 수 있는 글로벌 Validator와 단일 컨트롤러에 적용할 수 있는 Validator를 설정하는 방법을 제공합니다.

이를 사용하면 @Valid 어노테이션을 사용해서 커맨드 객체에 검증 기능을 적용할 수 있습니다.

 

글로벌 Validator를 적용하기 위해 설정을 해보겠습니다.

 

MvcConfig.java

 

@Override
public Validator getValidator() {
	return new RegisterRequestValidator();
}

 

 

WebMvcConfigurer 인터페이스의 getValidator() 메소드가 리턴한 객체를 글로벌 범위 Validator로 사용합니다.

이를 통해 @Valid 어노테이션을 사용해서 Validator를 적용할 수 았습니다.

글로벌 범위 Validator인 RegisterRequestValidator는 RegisterReqest 타입에 대한 검증을 지원합니다.

 

@Valid 어노테이션을 사용하기 위해 pom.xml에 모듈을 추가해 줍니다.

 

<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>1.1.0.Final</version>
</dependency>

 

 

RegisterController.java

 

	@PostMapping("/register/step3")
	public String handleStep3(@Valid RegisterRequest regReq, Errors errors) {
		if (errors.hasErrors())
			return "register/step2";

		try {
			memberRegisterService.regist(regReq);
			return "register/step3";
		} catch (DuplicateMemberException ex) {
			errors.rejectValue("email", "duplicate");
			return "register/step2";
		}
	}

 

 

커맨드 객체에 해당하는 파라미터에 @Valid 어노테이션을 붙이면 글로벌 범위 Validator가 해당 타입을 검증할 수 있는지 확인합니다.

검증이 가능하다면 실제 검증을 수행하고 그 결과를 Errors에 저장합니다. 이는 요청 처리 메소드 실행 전에 적용됩니다.

 

위의 예는 handleStep3() 메소드를 실행하기 전에 @Valid 어노테이션이 붙은 regReq 파라미터를 글로벌 범위로 검증합니다.

RegisterRequest 타입을 지원하므로 regReq 파라미터로 전달되는 커맨드 객체에 대한 검증을 수행합니다.

검증 수행 결과는 Errors 타입 파라미터로 받습니다.

이 과정은 메소드가 실행 되기 전에 이루어지므로 RegisterRequest 객체를 검증하는 코드를 작성할 필요가 없습니다.

파라미터로 전달받은 Errors 이용해서 검증 에러가 존재하는지 확인하면 됩니다.

=> 이 때문에 Errors 타입의 파라미터가 없으면 검증을 실패하는 400 에러로 응답하기 때문에 주의가 필요합니다.

 

 

 

반응형

 

 

 

 

@InitBinder 어노테이션을 설정하면 컨트롤러의 범위 Validator를 설정할 수 있습니다.

 

RegisterController.java

 

	@PostMapping("/register/step3")
	public String handleStep3(@Valid RegisterRequest regReq, Errors errors) {
		if (errors.hasErrors())
			return "register/step2";

		try {
			memberRegisterService.regist(regReq);
			return "register/step3";
		} catch (DuplicateMemberException ex) {
			errors.rejectValue("email", "duplicate");
			return "register/step2";
		}
	}
    
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
    	binder.setValidator(new RegisterRequestValidator());
    }

 

 

handleStep3() 메소드는 Validator 객체의 validate() 메소드를 호출하는 코드가 없습니다.

어떤 Validator가 커맨드 객체를 검증할지는 initBinder() 메소드가 정합니다.

@InitBinder를 적용한 메소드는 WebDataBinder 타입 파라미터를 갖는데 setValidator() 메소드를 이용해서 컨트롤러 범위에 적용할 Validator를 설정할 수 있습니다.

 

예시는 RegisterRequest 타입을 지원하는 RegisterRequestValidator를 컨트롤러 범위 Validator로 설정했으므로 @Valid로 검증 시 이를 사용합니다.

 

@InitBinder가 붙은 메소드는 컨트롤러의 요청 처리 메소드를 실행하기 전에 실행됩니다.

즉, 요청처리 메소드인 handlerStep3()를 실행하기 전에 initBinder()를 매번 호출해서 WebDataBinder를 초기화합니다.

 

@Valid 어노테이션은 Bean Validation 스펙에 정의되어 있습니다. 이 스펙은 @NotNull, @DIgits, @Size 등의 어노테이션을 정의하고 있습니다.

이를 사용하면 Validator의 작성 없이 어노테이션만으로 커맨드 객체의 값을 검증할 수 있습니다.

 

이를 사용하기 위해 pom.xml에 모듈을 추가해야 합니다.

 

 

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.2.Final</version>
</dependency>

 

 

 

RegisterRequest.java

 

 

public class RegisterRequest {
    @NotBlank
    @Email
    private String email;
    @Size(min = 6)
    private String password;
    @NotEmpty
    private String confirmPassword;
    @NotEmpty
    private String name;
    ...
}

 

 

이와 같이 어노테이션의 이름을 보면 어떤 검사를 하는지 유추할 수 있습니다.

이를 적용했다면 Bean Validation 어노테이션을 적용한 커맨드 객체를 검증할 수 있는 OptionalValidatorFactoryBean 클래스를 빈으로 등록하는 것입니다.

이는 @EnableWebMvc 어노테이션을 사용하면 자동으로 설정됩니다.

이제 @Valid를 붙여서 글로벌 범위 Validator를 검증하면 됩니다.

 

하지만 아까처럼 글로벌 범위의 Validator를 설정하면 OptionalValidatorFactoryBean이 사용되지 않기 때문에 코드를 지워줘야 합니다.

 

//글로벌 범위 설정 Validator
@Override
public Validator getValidator() {
	return new RegisterRequestValidator();
}

 

 

이에 맞춰 Message를 출력하려면 label.properties에 메시지를 입력해야 합니다.

 

NotBlank=필수 항목입니다. 공백 문자는 허용하지 않습니다.
NotEmpty=필수 항목입니다.
Size.password=암호 길이는 6자 이상이어야 합니다.
Email=올바른 이메일 주소를 입력해야 합니다.

 

 

검증을 통해 상황에 맞춘 메시지를 출력하게 됩니다.

 

이외에도 Bean Validation의 주요 어노테이션을 알고 싶다면 아래의 링크를 방문해 보시길....

 

https://docs.oracle.com/javaee%2F7%2Fapi%2F%2F/javax/validation/constraints/package-summary.html

 

javax.validation.constraints (Java(TM) EE 7 Specification APIs)

Enum Summary  Enum Description Pattern.Flag Possible Regexp flags. Annotation Types Summary  Annotation Type Description AssertFalse The annotated element must be false. AssertFalse.List Defines several AssertFalse annotations on the same element. Assert

docs.oracle.com

 

 

 

출처: 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문

반응형
myoskin