Java/JavaSpring

Chap 12. MVC 2 : 메시지, 커맨드 객체 검증

펭킹 2022. 7. 13. 14:40

<spring:message> 태그로 메시지 출력

메시지 파일을 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}님</strong>, 회원 가입을 완료했습니다.
go.main=메인으로 이동

 

MessageSource 타입의 빈을 MvcConfig에 추가한다. 이때 messageSource 이외 다른 이름을 사용할 경우 정상적으로 작동하지 않는다.

 

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

 

label.properties에서 설정한 properties를 다음과 같이 설정하여 사용 할 수 있다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix = "spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<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>

 

<spring:message> 태그를 이용하여 메시지를 출력한다. label.properties 파일의 설정과 일치하는 메시지를 읽어와 해당 부분으로 출력한다.

다국어 메시지를 지원하려면 해당 파일이름에 로케일 문자를 추가한다

한국 : label_ko.properties, 영어 : label_en.properties

특정 언어에 해당하는 메시지 파일이 존재하지 않으면 label.properties 파일의 메시지를 사용한다.

 

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

입력한 값에 대한 검증처리(deligation 처리)가 되지 않은 경우 폼 값 검증과 에러 메시지 처리를 해야된다.

스프링 MVC에서 커맨드 객체 값을 검증하기 위해 두개의 인터페이스를 사용한다.

- org.springframework.validation.Validator 

- org.springframework.validation.Errors

 

객체 검증 시 사용하는 Validator 인터페이스는 다음과 같다.

 

package org.springframework.validation;

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

 

supports() 메서드는 Validator가 검증할 수 있는 타입인지 검사한다. validate() 메서드는 첫 번째 파라미터로 전달받은 객체를 검증하고 오류 결과를 Errors에 담는 기능을 정의한다.

 

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);
		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.rejectIfEmpty(errors, "name", "required");
		ValidationUtils.rejectIfEmpty(errors, "password", "required");
		ValidationUtils.rejectIfEmpty(errors, "confirmPassword", "required");
		if(!regReq.getPassword().isEmpty()) {
			if(!regReq.isPasswordEqualToConfirmPassword()) {
				errors.rejectValue("confirmPassword", "nomatch");
			}
		}
	}
}

 

clazz 객체가 RegisterRequest 클래스로 타입 변환이 가능한지 확인한다. target 파라미터는 검사 대상 객체, error 파라미턴는 검사 결과 에러 코드를 설정하기 위한 객체이다. 값 검사를 위해 프로퍼티 에러 코드로 "required"를 추가한다.

 

커맨드 객체 자체에 에러를 추가할 때, reject() 메서드를 사용한다.

try{
	//인증 처리 코드
}catch(WrongIdPasswordException ex){
	//특정 프로퍼티가 아닌 커맨드 객체 자체에 에러 코드 추가
    errors.reject("notMatchingIdPassword");
    return "login/loginForm";
}

 

Errors 타입 파라미터는 반드시 커맨드 객체를 위한 파라미터 다음에 위치해야 한다. 그렇지 않으면 익셉션이 발생한다.

 

reject(String errorCode, Object[] errorArgs, String defaultMessage)

Object 배열 타입의 errorArgs 파라미터를 이용하여 변수에 삽입될 값을 전달한다. 익셉션 발생시키는 대신 defaultMessage를 출력한다.

 

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

 

글로벌 범위 Validator와 컨트롤러 범위 Validator

스프링 MVC는 모든 컨트롤러(@Configration)에 적용할 수 있는 전역 Validator와 지역 Validator를 설정하는 방법을 제공한다.

 

글로벌 Validator의 경우 검증할 커맨드 객체에 @Valid 애노테이션을 적용한다. 대표적으로 Dto가 있다.

 

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import controller.RegisterRequestValidator;
import org.springframework.validation.Validator;

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {


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

 

getValidate() 메서드가 리턴한 객체를 글로벌 범위 Validator로 사용한다. 글로벌 범위 Validator인 RegisterRequestValidator는 RegisterRequest(Commad) 타입에 대한 검증을 지원한다.

 

import javax.validation.Valid;

@Controller
@RequestMapping("/register")
public class RegisterController {
	
    ...
	
	@PostMapping("/step3")
	public String handleStep3(@Valid RegisterRequest regReq, Errors errors)//RegisterRequest가 검증대상이다.
	{

 

글로벌 Validator 가 RegisterRequest 타입을 지원하므로 regReq 파라미터로 전달 되는 커맨드 객체에 대한 검증을 수행한다. 검증 결과를 Errors타입 파라미터로 받아 Errors에 저장한다. 

글로벌 범위 Validator는 Command(Submit)과 궁합이 좋다.

 

지역 Validator는 binder를 통해 컨트롤러 범위를 적용 시킬 수 있다.

	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		binder.setValidator(new RegisterRequestValidator());
	}

 

Bean Validation을 이용한 값 검증 처리

pom.xml에 API 정의 모듈과 API를 구현할 프로바이더의 의존 처리를 한다.

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

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

import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;

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

	public String getEmail() {
		return email;
	}
    
    	//getter, setter
 }

Bean Validation 애노테이션을 적용한 커맨드 객체를 검증할 수 있는 OptionalValidatorFactoryBean 클래스를 빈으로 등록한다. @EnableWebMvc 애노테이션 사용 시 OptionalValidatorFactoryBean을 글로벌 범위 Validator로 등록하면 자동으로 연결된다.