스프링 2-30, ch2-31

서현우·2022년 7월 4일
0

스프링의정석

목록 보기
69/85

ch2-30, ch2-31 - 데이터의 변환과 검증

  • WebDataBinder - 데이터의 타입 변환, 데이터 검증
  • 타입 변환 - PropertyEditor, Formatter, Converter
  • @InitBinder - PropertyEditor등록, Local Validator 등록
  • Formatter - @DataTimeFormat, @NumberFormat
  • Validator - 객체 검증기, 수동, 자동, 글로벌 Validator
  • MessageSource - Validator에 에러 메세지 추가, properties파일
  • 에러 메세지 출력 - JSP에 <form:form>태그 사용

WebDataBinder

  • 타입 변환, 데이터 검증, BindingResult

타입 변환

BindingResult타입 객체를 컨트롤러 메서드의 매개변수로 선언하면,
예외가 발생했을 때 에러페이지로 가지 않고, 컨트롤러에 Binding 결과를 주고, 컨트롤러가 처리하도록 한다.
컨트롤러 메서드에 BindingResult를 선언 했을 때와 안했을 때 차이를 알자!
PropertyEditor, Converter, Formatter

PropertyEditor

양방향 타입 변환(String -> 타입, 타입 -> String).
특정 타입이나 이름의 필드에 적용 가능하다.

  • 디폴트 PropertyEditor - 스프링이 기본적으로 제공.

  • 커스텀 PropertyEditor - PropertyEditorSupport를 상속해서 직접 구현 가능.

  • 모든 컨트롤러 내에서의 변화 - WebBindingInitializer를 구현 후 등록.

  • 특정 컨트롤러 내에서의 변환 - 컨트롤러에 @InitBinder가 붙은 메서드를 작성.

Converter

단방향 타입(타입A -> 타입B)으로 변환하므로,
PropertyEditor(stateful)의 단점을 개선(iv를 사용 -> 싱글톤 불가).
Converter(stateless)(iv 사용X -> 싱글톤으로 사용 가능).

Formatter

양방향 타입 변환(String -> 타입, 타입 -> String)
바인딩할 필드에 적용 - @NumberFormat, @DateTimeFormat

RegisterController.java

@InitBinder를 붙인 메서드를 작성해서 PropertyEditor를 사용한다.
해당 컨트롤러 내에서만 사용할 수 있다.

@InitBinder
public void toDate(WebDataBinder binder) {
	SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
	//커스텀에디터로 SimpleDateFormat의 날짜 형식을 Date클래스로 변환한다.
	binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false));
	//커스텀에디터로 "#"구분자로 나눠서 String[]로 변환한다.
	binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#"));
}

User.java

날짜와 숫자는 컨트롤러에 PropertyEditor 대신 Formatter를 iv에 붙여서 타입변환을 할 수 있다.
@DateTimeFormat, @NumberFormat

@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;

데이터 검증

Validator란?
객체를 검증하기 위한 인터페이스. 객체 검증기(validator) 구현에 사용.

  • Validator 인터페이스
public interface Validator {
	//이 검증기로 검증가능한 객체인지 알려주는 메서드
	boolean supports(Class<?> clazz);
	//객체를 검증하는 메서드 - target: 검증할 객체, errors:검증시 발생한 에러저장소
	void validate(Object target, Errors errors);
}
  • 객체 검증기 UserValidator 구현
    검증이라는 관심사를 컨트롤러에서 분리.
public class UserValidator implements Validator {
	@Override
	public boolean supports(Class<?> clazz) {
		//clazz가 User타입인지 확인
		return User.class.equals(clazz);
		
		//clazz가 User또는 그 자손인지 확인
		//return User.class.isAssignableFrom(clazz);
	}
	@Override
	public void validate(Object target, Errors errors) {
		User user = (User)target;
		
		String id = user.getId();
		
		//if(id==null || "".equals(id.trim())) {
		//	errors.rejectValue("id", "required");
		//}
		
		//위 주석 3줄을 1줄로 한 것.
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
		
		if(id==null || id.length() < 5 || id.length() > 12) {
			errors.rejectValue("id", "invalidLength");
		}
	}
}

Validator를 이용한 검증 - 수동 검증

컨트롤러에 UserValidator객체를 만들어서 validator로 검증.
에러가 있으면 registerForm으로 이동.

@PostMapping("/register/add")
public String save(User user, BindingResult result, Model model) {
	UserValidator userValidator = new UserValidator();
	userValidator.validate(user, result); //validator로 검증
	
	if(result.hasErrors()) { //에러가 있으면
		return "registerForm";
	}
}

Validator를 이용한 검증 - 자동 검증

@InitBinder의 메서드에 setValidator()로 등록하고,
검증 할 객체 앞에 @Valid를 붙인다.
@Valid는 javax.validation을 사용하므로,
Maven Ropo에서 validation-api를 pom.xml에 추가한다.

@InitBinder
public void toDate(WebDataBinder binder) {
	//1 - UserValidator를 WebDataBinder에 등록한다.
	binder.setValidator(new UserValidator());
}

@PostMapping("register/add")
//2 - @Valid를 검증할 객체 앞에 붙이면 자동검증한다.
public String save(@Valid User user, BindingResult result, Model model) {
	if(result.hasErrors()) {
		return "registerForm";
	}
}

pom.xml

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

글로벌 Validator

하나의 Validator로 여러 객체를 검증할 때, 글로벌 Validator로 등록.

  • 글로벌 Validator로 등록하는 방법
  1. GlobalValidator클래스를 작성하고,
  2. servlet-context.xml에 등록.
<!-- 아래 빈을 annotation-driven에 bean의 id를 등록 -->
<annotation-driven  validator="globalValidator"/>
<!-- 빈으로 등록 -->
<beans:bean id="globalValidator" class="com.fastcampus.ch2.GlobalValidator"/>
  • 글로벌 Validator와 로컬 Validator를 동시에 적용하는 방법
    "setValidator"는 로컬 Validator만 자동검증으로 등록 할 때 사용하고,
    글로벌 Validator를 사용할 때는 "로컬을 글로벌에 등록"하는 것이므로, "addValidators"를 써야 한다.
   @InitBinder
    public void toDate(WebDataBinder binder) {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false));
//        binder.setValidator(new UserValidator()); //UserValidator를 WebDataBinder의 로컬 validator로 등록
        binder.addValidators(new UserValidator()); //글로벌Validator에 로컬Validator를 추가(add)

MessageSource

  • 다양한 리소스에서 메세지를 읽기 위한 인터페이스
public interface MessageSource {
	String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
	String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NuSuchMessageException;
}
  • 프로퍼티 파일을 메세지 소스로 하는 ResourceBundleMessageSource를 등록
  1. servlet-context.xml에 bean으로 등록한다.
<bean:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	<beans:property name="basenames">
		<beans:list>
			<beans:value>error_message</beans:value> <!-- /src/main/resources/error_message.properties -->
		</beans:list>
	</beans:property>
	<beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>
  1. error_message.properties를 만들고 key=value로 작성한다.
    작성 전에 properties파일의 인코딩을 utf-8로 설정해야 한다.
    (인텔리제이 - File-encoding(properties)을 utf-8로 변경)
    (STS - Preferences - General - Content Type - Text클릭 후 Default encoding에 utf-8 update)
required=필수 항목입니다.
required.user.pwd=사용자 비밀번호는 필수 항목입니다.
invalidLength.id=아이디의 길이는 {1}~{2}사이어야 합니다.

검증 메세지의 출력

콘솔이 아닌 브라우저에 검증 메세지를 출력하려면,
스프링이 제공하는 커스텀 태그 라이브러리를 사용해야 한다.
1. JSP페이지에 임포트 해야한다.

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
  1. <form> 대신 <form:form>을 사용하면 자동변경된다.
    2-1. user 객체를 검증한다.
<!-- user는 검증할 객체 -->
<form:form modelAttribute="user">
</form:form>

아래와 같이 변경된다.

<form id="user" action="/ch2/register/add" method="post">
</form>

2-2. form태그안에 <form:errors>로 에러를 출력 할 수 있다.
path에 에러가 발생하는 필드를 지정한다.(*는 모든 필드의 에러)
즉 user객체의 id란 필드에서 발생하는 에러의 msg를 보여준다는 의미이다.

<form:errors path="id" cssClass="msg"/>

아래와 같이 변경된다.

<span id="id.errors" class="msg">필수 입력 항목입니다.</span>

registerForm.jsp

<div id="msg" class="msg"><form:errors path="id"/></div>
profile
안녕하세요!!

0개의 댓글