커맨드 객체 값 검증과 에러 메시지

dongeranguk·2021년 9월 26일
0
post-thumbnail

값 검증은 크게 두가지 방법을 사용한다.

  • 웹 브라우저 : 자바 스크립트를 이용해서 데이터를 웹 서버에 전송하기 전에 미리 검사한다.

  • 웹 서버 : 전달받은 요청 파라미터의 값을 검사한다. 파라미터 값이 올바르지 않을 경우 에러 코드(ex.400,500)을 응답하거나 폼 화면에 재입력을 위한 폼 화면을 웹 브라우저에 전송한다.

💬 웹 브라우저에서 자바 스크립트를 이용해서 폼 값을 검사하면 사용자에게 빠르게 검사 결과를 보여주어 사용자 입장에서 중요하지만, 악의적으로 잘못된 데이터를 서버에 전송하거나, 클라이언트에서 값을 완전히 검증하지 못할 수도 있기 때문에 서버에서도 반드시 값을 검사해야 한다.

따라서, 웹 브라우저와 웹 서버에서 함께 값 검증을 하는것이 좋다.


📌 1. Validator와 Errors/BindingResult를 이용한 객체 검증

Validator 인터페이스를 사용하면, 스프링이 제공하는 객체 검증에러 메시지 지원 등의 기능을 사용할 수 있다. 컨트롤러에서 커맨드 객체의 값을 검증할 때 Validator 를 유용하게 사용한다.

Validator 인터페이스는 다음과 같이 2개의 메서드를 정의하고 있다.

public interface Validator {
    boolean supports(Class<?> clazz);
    void validate(Object target, Errors errors);
}

supports() 메서드는 Validator가 해당 타입의 객체를 지원하는지 여부를 리턴한다.

실제 값을 검증하는 코드는 validate() 메서드에 위치한다. validate() 메서드의 target 파라미터는 값을 검증할 객체이며, errors 파라미터는 값이 올바르지 않을 경우 그 내용을 저장하기 위해 사용한다.

아래는 Validator 인터페이스 구현 예시 코드이다.

  • 라인 32 : 중첩 프로퍼티에 대한 검사를 할 경우, errors에 중첩 프로퍼티 진입을 지정한다.
  • 라인 37 : 중첩 프로퍼티에 대한 검사가 끝나면, errors에 중첩 프로퍼티 끝을 지정한다.

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

위 코드는 "email" 프로퍼티의 값이 잘못되었고, 에러 코드 "required"를 사용한다는 것을 의미한다.

ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "required");

위 코드는 target의 "name" 프로퍼티의 값이 null이거나, 길이가 0인 경우 errors 객체에 "name" 프로퍼티의 에러코드로 "required"를 등록한다.


Validator를 구현했다면, 컨트롤러에서 Validator를 이용해서 커맨드 객체를 검증할 수 있다.

위 코드의 regist() 메서드는 커맨드 객체인 memRegReq 파라미터와 에러 정보를 저장할 bindingResult 파라미터를 갖고 있다.

BindingResult

Errors 인터페이스를 상속받은 타입으로
컨트롤러를 구현할 때에는 두 타입 중 하나를 사용하면 된다.

값 검증을 위해 MemberRegistValidator 객체를 생성하고 validate() 메서드를 호출하여 validate() 메서드를 호출한다.

validate() 메서드를 실행한 후에 값에 오류가 존재하면 bindingResult.hasErrors() 메서드는 true를 리턴한다. 따라서, 리턴 값(true or false)에 따라 다시 입력폼을 보여주거나, 에러 화면을 보여주는 등의 입력 오류 처리를 한다.


📌 2. Errors와 BindingResult 인터페이스의 주요 메서드

Validator는 객체 데이터에 오류가 있을 경우 Errors 객체에 오류가 등록한다.

Errors 인터페이스는 오류를 등록하기 위해 아래와 같은 메서드를 제공한다.

2.1. ValidationUtils 클래스를 이용한 값 검증

ValidationUtils 클래스는 다수의 rejectIfEmpty() 메서드와 rejectIfEmptyOrWhitespace() 메서드를 제공하고 있으며, 다음과 같이 간결하게 대체할 수 있다.

📌 3. 에러 코드와 메시지

Validator를 이용해서 오류를 확인하고, 오류 내용을 알려 주는것은 에러 코드이다.

스프링 MVC는 에러 코드로부터 메시지를 가져오는 방법을 제공하며, 이 메시지를 응답 결과에 보여주는 방법 역시 제공한다.

검증 과정에서 추가된 에러 메시지를 사용하려면 다음과 같은 코드를 작성한다.

  • 메시지를 읽어올 때 사용할 MessageSource를 스프링 설정에 등록한다.

  • MessageSource에서 메시지를 가져올 때 사용할 프로퍼티 파일을 작성한다.

  • JSP와 같은 뷰 코드에서 스프링이 제공하는 태그를 이용해서 에러 메시지를 출력한다.

아래의 코드는 MessageSource의 등록 예시이다.

<bean id="messageSource"
	class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basenames">
    	<list>
        	<value>message.error</value>
        </list>
    </property>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>

위 설정의 경우 message 패키지에 위치한 error.properties 파일로부터 메시지를 읽어오도록 설정한 것이다.

error.properties 파일은 아래와 같이 작성되었다.

required=필수항목 입니다.
minlength=최소 {1} 글자 이상 입력해야 합니다.
maxlength=최대 {1} 글자까지만 입력해야 합니다.
unsafe.password=암호는 알파벳과 숫자를 포함해야 합니다.

스프링 MVC는 에러 코드로부터 생성된 메시지 코드를 사용해서 에러 메시지를 출력하게 된다.

글로벌 에러 코드인 경우 다음의 우선 순위에 따라 메시지 코드를 생성한다.

  1. 에러코드 + "." + 커맨드객체이름
  2. 에러코드

에러 메시지를 읽어올 때, 먼저 메시지 코드가 "에러코드 + "." + 커맨드객체이름" 인 메시지를 찾는다. 존재하지 않으면, 우선 순위에 따라 "에러코드" 를 사용한다.

3.1. 에러 메시지 출력

스프링 MVC는 JSP에서 에러 메시지를 출력할 수 있도록 커스텀 태그를 제공하고 있다. 커스텀 태그는 아래와 같다.

<form:form>

<form:errors>

스프링의 <form:form> 커스텀 태그를 사용하면 스프링의 폼 관련 태그에서 커맨드 객체의 정보를 사용할 수 있다.

라인 9에서 commandName 속성 값으로 "loginCommand" 를 지정했는데, 이는 <form:> 태그에서 이름이 "loginCommand" 인 커맨드 객체를 사용한다는 것을 의미한다.

<form:errors> 태그는 <form:form> 태그에서 지정한 커맨드 객체의 에러 코드를 이용해서 에러 메시지를 출력한다.

예를 들어, 글로벌 에러 코드 "InvalidIdOrPassword"가 존재한다면, 이 경우 <form:errors> 태그는 "InvalidIdOrpassword.loginCommand" 메시지를 읽어와 출력한다.


📌 4. @Valid, @InitBinder 를 이용한 검증

@Valid 어노테이션을 이용하면, 커맨드 객체를 검사하는 코드를 직접 호출하지 않고 스프링 프레임워크가 호출하도록 설정할 수 있다.

@Valid 어노테이션을 사용하려면 먼저 의존 설정을 추가해야 한다.

<dependency>
	<groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.0.0.GA</version>
</dependency>

어떤 Validator가 커맨드 객체를 검증할 지의 여부는 @InitBinder 어노테이션이 적용된 initBinder() 메서드를 이용한다. 아래의 코드는 사용 예시이다.

@InitBinder
public void initBinder(WebDataBinder binder) {
	binder.setValidator(new LoginCommandValidator());
}

폼과 커맨드 객체 사이의 매핑을 처리해주는 WebDataBinder를 초기화할 수 있도록 하며, 이 메서드에서 WebDataBinder.setValidator() 를 통해 커맨드 객체의 유효성 여부를 검사할 때 사용할 Validator를 설정하게 된다.

그리고 스프링 MVC는 설정한 Validator 객체를 이용해서 @Valid 어노테이션이 붙은 커맨드 객체를 검증한다.


📌 5. 글로벌 Validator와 컨트롤러 Validator

글로벌 Validator를 사용하면 한 개의 Validator를 이용해서 모든 커맨드 객체를 검증할 수 있다. 글로벌 Validator를 사용하는 방법은 아래와 같다.

<mvc:annotaion-driven validator="validator"/>
<bean id="validator" class="custom.CommonValidator"/>

위 코드는 CommonValidator를 글로벌 Validator로 등록하였고, CommonValidator를 이용해서 @Valid 어노테이션이 적용된 커맨드 객체를 검사하게 된다.

@RequestMapping(method=RequestMethod.POST)
public String login(@Valid LoginCommand loginCommand, Errors errors) {
  if(errors.hasErrors()) {
  	return LOGIN_FORM;
  }
  ...
}

글로벌 Validator를 사용하지 않고 다른 Validator를 사용하려면 @InitBinder가 적용된 메서드에서 WebDataBinder의 setValidator() 메서드를 이용하면 된다.


(출처: 웹 개발자를 위한 Spring 4.0 프로그래밍, 저자 : 최범균)

0개의 댓글