데이터의 변환과 검증

뫄뫄(ahk)·2022년 9월 24일
0

Spring

목록 보기
14/18
post-thumbnail
post-custom-banner

WebDataBinder는 무엇일까?

  • 요청으로 넘어온 값들을 실제 객체에 binding하는 과정의 중간 역할을 한다.
    구체적으로는
  1. 타입변환
  2. 데이터검증
    이 두가지를 수행하며, 수행하는 도중 에러가 없어야지 binding된다.

만약 에러가 발행한다면, BindingResult 객체를 사용하면 해당 에러를 controller가 처리할 수 있다.

BindingResult

  • 타입 변환, 데이터 검증의 결과와 에러를 저장하여 에러가 발생하더라도 Controller에서 처리하도록 함.
  • 매핑된 메서드의 파라미터 중, binding한 객체 바로 뒤에 선언해야 한다.
  • 에러 발생 시 에러가 발생한(=변환실패한) 파라미터의 값은 null. 에러는 발생하지만, 에러페이지에 넘어가지 않았을 뿐이다.

잠깐! @InitBinder가 뭘까?

  • WebDataBinder를 초기화하기 위한 메서드에 붇는 애너테이션으로 @RequestMapping과 같은 에너테이션이 붙은 요청 처리 메서드에 명령어나 form으로 넘어온 인자들을 채우기 위해 사용된다. == binding되는 값들을 적절히 처리하기 위해 사용되는 메서드에 붙음
  • 보안이슈를 조심해야한다
  • @RequestMapping 메서드의 파라미터로 들어갈 인자들 모두는 @InitBinder 메서드로 다룰 수 있다. command/form Object와 이와 연관된 validation result 객체는 제외.

1. 변환

RegisterController에 변환기능 추가하기

  • 타입변환시 사용하는 변환기 등록(타입 변환시 패턴을 입력해야 한다)
  • @InitBinder는 컨트롤러 내에서만 적용된다.
    전역 변환은 WebBindingInitializer을 적용해야.

PropertyEditor

@InitBinder
	public void toDate(WebDataBinder binder) { //메서드 이름은 아무거나 파라미터만 WebDataBinder로
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		binder.registerCustomEditor(Date.class, "birth", new CustomDateEditor(df, false));
		//binder에 커스텀 변환기 등록, Date클래스로 변환, CustomDateEditor에 형식만 지정하여 등록
		//false: 빈 값을 허용할 것이냐?
	}
  • 양방향 타입 변환(String->타입, 타입-> String) (String인이유는 질의'문자열' 이기 때문)
  • 적용할 클래스 타입이나 필드 지정가능
  • 수십개의 기본 propertyEditor가 있다.
  • java beans(=특별한 객체) 기술을 사용.
    이 객체의 iv를 property라 하고, propertyEditor는 iv를 변환하는 기술
    iv를 사용하기 때문에 싱글톤 패턴을 사용할 수 없다.(stateful)

Converter와 ConversionService

  1. Converter
    • 단방향 타입 변환
    • PropertyEditor의 단점을 개선(stateful → stateless)
  2. ConversionService
    • custom converter를 ConversionService에 등록하고, ConversionService에서 자동으로 타입변환해줌
    • 이미 많은 converter들이 등록되어있다.(WebDataBinder에 DefaultFormattingConversionService이 기본 등록되어 있어서 자동으로 타입변환이 이루어지고 있다)

Formatter

  • 양방향
  • 바인딩할 필드에 annotion으로 적용(@NumberFormat, @DateTimeFormat) (printer/Object → String , parser/String → parser)
1. Converter
    - **단방향 타입 변환**
    - **PropertyEditor의 단점을 개선**(stateful → stateless)
2. ConversionService
    - custom converter를 ConversionService에 등록하고, ConversionService에서 자동으로 타입변환해줌
    - 이미 많은 converter들이 등록되어있다.(WebDataBinder에 DefaultFormattingConversionService이 기본 등록되어 있어서 자동으로 타입변환이 이루어지고 있다)

타입 변환 우선순위(WebDataBinder에 등록 가능하거나 이미 등록된 것 중)

  1. 커스텀PE
  2. ConversionService에 등록된 Converter
  3. 디폴트PE

2. 검증

Validator란?

  • 객체를 검증하기 위한 인터페이스
  • Controller에서 ‘검증’이라는 관심사를 분리해 Validator를 구현한 객체 생성
public interface Validator {
	boolean supports(Class<?> clazz);
	//객체가 검증 가능한지 알려주는 메서드
	//결과가 false이면 검증 불가

	
	void validate(@Nullable Object target, Errors errors);
	//객체를 검증하는 메서드 
	//target-검증할 객체, errors-검증시 발생한 에러 저장소, BindingResult(interface)의 부모
}
  • Validator를 구현하여 클래스를 작성하면 검증기 완성!
  • Errors
    발생하지 않으면 그냥 변환된 값이 binding된다.
    validate()로 검증 중 에러 발생 → errors에 에러코드 저장

Validator를 이용한 검증 - 수동

//직접 validator 생성, 호출
UserValidator userValidator = new UserValidator();
userValidator.validate(user, result);

if(result.hasErrors()){
	return "registerForm"; //<form:errors>로 jsp페이지에서 에러 출력가능
}

Validator를 이용한 검증 - 자동

  • WebDataBinder에 검증기를 등록
@InitBinder
public void toDate(WebDataBinder binder){
	...
	binder.setValidator(new UserValidator());
}

@PostMapping("/register/add")
public String save(Model m, @Valid User user, BindingResult result){
	if(result.hasErrors()){
		return "registerForm";	
	}
}
  • 등록한 Validator는 @InitBinder를 작성한 클래스 안에서만 사용가능
  • @Valid로 검증할 대상을 지정 (@Valid는 Spring이 아닌 자바 표준 annotation)

global Validator

<annotation-driven validator="globalValidator"/>
<beans:bean id="globalValidator" class="com.fastcampus.ch22.GlobalValidator"/>
//servlet-context.xml

@PostMapping("/register/add")
public String save(Model m, @Valid User user, BindingResult result){
	if(result.hasErrors()){
		return "registerForm";	
	}
}
  • 글로벌&로컬 Validator를 동시에 적용하는 방법
<annotation-driven validator="globalValidator"/>
<beans:bean id="globalValidator" class="com.fastcampus.ch22.GlobalValidator"/>
//servlet-context.xml

@InitBinder
public void toDate(WebDataBinder binder){
	...
	binder.addValidators(new UserValidator()); 
	//set이 아닌 add!!
	//local Validator를 추가
}
  • addValidators() 메서드를 사용한다.
  • setValidator를 쓰면 global Validator를 안씀

검증 메시지의 출력

  • spring이 제공하는 form tag library를 사용하여 검증 메시지를 출력
<form:form action="submit" method="post" modelAttribute="welcome">
        <table>
            <tr>
                <td><form:label path="name">Full Name: </form:label></td>
                <td><form:input path="name" />
                <form:errors path="name" cssStyle="color:red"/></td>
            </tr>
...
<form id="welcome" action="submit" method="post">
        <table>
            <tr>
                <td><label for="name">Full Name: </label></td>
                <td><input id="name" name="name" type="text" value=""/>
                <span id="name.errors" style="color:red">Please enter your name.</span></td>
            </tr>
  • form tag
    • html ‘form’ tag를 생성
    • modelAttribute : 넘겨줄 (검증한)객체의 이름을 지정
    • action (default = 자기 페이지 URL)
    • method(default = POST)
    • inner tags가 Controller에서 넘어온 model객체에 저장된 값을 자동으로 바인딩할 수 있게한다(바인딩 경로 제공)
  • input tag
    • default type = text
  • error tag
    • error 필드를 span tag로 생성한다
    • Controller나 validator에서 발생한 예외에 접근할 수 있고, 예외와 관련된 메시지를 UI에 출력할 수 있게한다.
    • 특정 필드에 해당하는 (MessageSource로 가져온)에러 메시지를 span tag에 넣어 출력한다.
<form:errors path="name" cssStyle="color:red"/>

<span id="name.errors" style="color:red">Please enter your name.</span>
//실행시 위에서 아래로 변환됨
  • 흐름
    요청 → Validator에서 검증 오류 발생 → Controller에서 view로 오류에 대한 정보와 model에 담은 요청 시 입력한 정보를 보냄 → 요청 화면에서 spring의 form tag가 정보를 받아 오류에 대한 정보를 자동 출력한다.

이 게시글은 남궁성 강사님의 스프링의 정석 강의를 요약, 정리 및 보충한 글입니다.

profile
NONONONONONOYes!
post-custom-banner

0개의 댓글