일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 자바왕기초
- 오라클통계
- HTML
- 제이쿼리
- 자바왕초보
- 오라클월별데이터
- 오라클클라우드에 젠킨스 설치하기
- CSS
- 스프링
- 자바기초
- 자바 기초
- 세션
- 스프링 구글차트로 기간별 현황 조회하기
- 스프링과 스프링부트 차이점
- 스프링 구글차트
- 자바 왕기초
- 스프링 에러
- 스프링 Ioc
- 스프링 부트가 해결하려고 했던 문제
- 스프링 Ioc Container
- maven
- 스프링 제어역전
- jsp
- 오라클주별데이터
- 자바
- 오라클일별데이터
- Spring Boot가 해결하려고 했던 문제
- java
- 썸머노트
- 오라클
- Today
- Total
Just Do it
스프링 WebDataBinder의 데이터 변환과 검증 본문
0. WebDataBinder
@RequestMapping("/getYoilMVC5")
public String main(@ModelAttribute MyDate date, BindingResult result) {
- 매개변수의 참조형으로 MyDate가 들어와서 아래와 같이 쿼리스트링으로 값이 입력되었을 때, 이 입력된 값이 MyDate의 인스턴스에 저장된다.
http://localhost/ch2/getYoilMVC5?year=2021&month=10&day=8
쿼리 스트링으로 입력된 값의 반환 타입은 String 이고 MyDate의 인스턴스에 저장될 때의 반환 타입은 int 일 때, 타입 변환을 도와주는 것이 바로 WebDataBinder이다.
- WebDataBinder가 하는 일은 두 가지이다.
1) 타입 변환
2) 데이터 검증 (Validation)
- WebDataBinder는 입력된 값에 대해 먼저 타입 변환을 하고 그다음 데이터 검증을 하는데, 결과나 에러가 있으면 BindingResult에 저장(Binding)한다.
-BindingResult에 저장된 값은 Controller에 넘겨줄 수 있고, Controller는 BindingResult에 저장된 값을 가지고 필요한 처리를 할 수 있다. 이때 BindingResult는 반드시 Binding 할 객체의 바로 뒤에 와야한다.
-그리고 BindingResult는 아래처럼 예외처리 메서드 내 매개 변수로 넣어서 BindingResult에 저장된 값이 필요할 경우 사용할 수 있다.
@ExceptionHandler(Exception.class)
public String catcher(Exception ex, BindingResult result) {
// ex.printStackTrace(); 예외가 왜 발생했는지 스프링 콘솔 창에서 보고 싶으면 printStackTrace()를 쓰면 된다.
return "yoilError"; // 예외 발생 시 보여줄 view 폴더 내 파일
}
1. WebDataBinder의 타입 변환
- 스프링이 알아서 변환하지 못하는 것들은 타입 변환에서 에러가 발생한다. 그래서 이 에러를 해결하기 위해서는 변환기를 등록해야한다.
예) String으로 입력된 날짜를 Data 타입으로 바꿀 때 (날짜 형식은 굉장히 다양해 스프링에서 자동으로 변환하지 못한다.)
1.1 PropertyEditor를 이용한 타입 변환 (양방향 타입 변환)
- PropertyEditor: 양방향 타입 변환 (String -> 타입, 타입 -> String).
- PropertyEditor를 이용한 타입 변환 방법에는 변환 메서드를 이용하는 방법과 어노테이션을 이용하는 방법이 있다.
* 날짜와 숫자는 굉장히 많이 사용하기 때문에 별도의 어노테이션이 있다 (숫자: @NumberFormat, 날짜: @DateTimeFormat)
-모든 컨트롤 내에서의 변환: WebBindingInitializer를 구현 후 등록
-특정 컨트롤 내에서의 변환: 컨트롤러에 @InitBinder가 붙은 메서드를 작성
- 특정 타입이나 이름의 필드에도 적용 가능하다.
예)
@InitBinder
public void toDate(WebDataBinder binder) {
// "hobby"라는 필드에만 아래 변환을 적용한다.
binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#"));
*디폴트 PropertyEditor: 스프링이 기본적으로 제공
org.springframework.beans.propertyeditors (Spring Framework 5.3.15 API)
Class Summary Class Description ByteArrayPropertyEditor Editor for byte arrays. CharacterEditor Editor for a Character, to populate a property of type Character or char from a String value. CharArrayPropertyEditor Editor for char arrays. CharsetEditor Ed
docs.spring.io
*커스텀 PropertyEditor: 사용자가 직접 구현. PropertyEditorSupport를 상속하면 편리.
예 1-1) 변환 메서드 이용
- 변환 메서드에는 @InitBinder 애너테이션을 붙여줘야한다.
- 변환 메서드는 컨트롤러 내에서만 적용된다.
//변환메서드
@InitBinder
public void toDate(WebDataBinder binder) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// 스프링이 제공하는 CustomDateEditor를 이용해서 변환: String -> Date
//registerCustomEditor() = 변환기
binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false)); // yyyy-MM-dd 형식으로 입력되는 문자 타입의 날짜를 Date 타입으로 변환.
binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("#")); // #을 구분자로 입력되는 문자열을 배열로 나눈다.
}
예 1-2) 필드에 직접 어노테이션 이용
public class User{
private String id;
private String pwd;
private String name;
private String email;
@DateTimeFormat(pattern="yyyy/MM/dd") // 입력된 연월일을 "yyyy/MM/dd" 형식으로 바꿔준다.
private Date birth;
private String[] sns;
private String[] hobby;
}
1.2 Converter와 ConversionService를 이용한 타입 변환 (단방향 타입 변환)
- Converter: 단방향 타입 변환 (타입 A -> 타입 B). PropertyEditor의 단점을 개선 (stateful -> stateless)
- 아래 예시와 같이 Converter를 만든 후 ConversionService로 등록을 해야한다.
예)
public class StringToStringArrayConverter implements Converter<String, String[]> {
@Override
public String[] convert(String source) {
return source.split("#"); // #을 구분자로 하는 문자 String을 문자 배열 String[] 로 바꾼다.
}
}
- ConversionService: 타입 변환 서비스를 제공. 여러 Converter를 등록 가능
* WebDataBinder에 DefaultFormattingConversionService가 기본적으로 등록되어있다.
* 모든 컨트롤러 내에서 변환: ConfigurableWebBindingInitializer 를 설정해서 사용
* 특정 컨트롤러 내에서 변환: 컨트롤러에 @InitBinder가 붙은 메서드를 작성
1.3 Formatter (양방향 타입 변환)
- Formatter: 양방향 타입 변환(String -> 타입, 타입 -> String)
- 바인딩할 필드에 적용한다. (숫자: @NumberFormat, 날짜: @DateTimeFormat)
* 1.1의 PropertyEditor의 어노테이션 이용 방법과 유사하다.
@DateTimeFormat(pattern="yyyy/MM/dd") // yyyy/MM/dd 로 들어오는 String을 Date로 변환
Date birth;
@NumberFormat(pattern="###,###") // ###,### 로 들어오는 String을 BigDecimal로 변환
BigDecimal salary;
** WebDataBinder에 타입 변환기를 등록할 수 있는 방법은 아래와 같고, 처리 우선 순위도 아래 순서와 같다. (1->2->3)
1) 커스텀 PropertyEditor
2) 디폴트 PropertyEditor
3) ConversionService
2. WebDataBinder의 데이터 검증
2.1 Validator
- 객체를 검증하기 위한 인터페이스. 객체 검증기 (Validator) 구현에 사용한다.
public interface Validator {
// 이 검증기로 검증 가능한 객체인지 알려주는 메서드
boolean supports(Class<?> clazz>;
// 객체를 검증하는 메서드 - target: 검증할 객체, errors: 검증시 발생한 에러 저장소
void validate(@Nullable Object target, Errors errors);
}
Validator 구현 예)
public class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz) // clazz가 User 또는 그 자손인지 확인. isAssignableFrom 대신 equals를 사용해도 괜찮다.
}
@Override
public void validate(Object target, Errors errors) {
User user = (User)target;
// Object타입의 target을 User로 형변환. 위에서 이미 User 또는 그 자손인지 확인했으므로 instanceof를 쓸 필요가 없다.
String id = user.getId();
// id를 검사하기 위해 id값을 가져온다.
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
// id가 비어있거나 공백이면 id 필드에 required라는 에러 코드를 저장한다.
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
// pwd가 비어있거나 공백이면 pwd 필드에 required라는 에러 코드를 저장한다.
if (id==null || id.length()<5 || id.length() > 12) {
errors.rejectValue("id", "invalidLength");
//id가 비어있거나 id의 길이가 5보다 작거나 12보다 길면 invalidLength라는 에러 코드를 저장한다.
} // 먼저 에러코드를 저장하고 나중에 에러코드에 맞는 메세지를 작성하여 출력한다.
}
}
Validator를 이용한 검증 - 수동 예)
//컨트롤러에서 객체검증기를 불러온다.
@PostMapping("/register/add")
public String save(Model model, User user, BindingResult result) {
UserValidator userValidator = new UserValidator();
userValidator.validate(user,result);
// validator로 user 객체를 검사하여 에러를 result로 반환한다.
// 여기서 result는 위의 save 메서드의 매개변수 안에 있는 result이다. BindingResult는 Errors의 자손이다.
if(result.hasErrors()) {
return "registerForm"; // 에러가 있으면 registerForm내의 <form:errors> 코드를 통해 화면에 보여준다.
}
}
Validator를 이용한 검증 - 자동 예)
*검증할 객체 앞에 @Valid 애너테이션을 붙일 때 빨간 밑줄이 생기는 이유:
@Valid 애너테이션은 스프링이 아닌 자바 표준 애너테이션이라서 import 되지 않는다. Maven Repo에서 가져와서 써야한다.
1) 아래 사이트에 가서 Maven 칸에 있는 코드 복사
https://mvnrepository.com/artifact/javax.validation/validation-api/2.0.1.Final
2. pom.xml에 복사해온 코드를 복사해 넣고 저장한다.
3. pom.xml이 바뀌었으니 프로젝트파일 우클릭->Maven->Update Project 클릭해서 업데이트 해준다.
4. 작업하던 컨트롤러 파일로가서 import 하면 @Valid를 쓸 수 있다.
//1. Validator 등록
@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()); // validator를 WebDataBinder에 등록
List<Validator> validators = binder.getValidators();
System.out.println("validators=" + validators);
}
@PostMapping("/register/add")
//2. 검증할 객체 앞에 @Valid를 붙인다.
public String save(Model model, @Valid User user, BindingResult result) {
if(result.hasErrors()) {
return "registerForm"; // 에러가 있으면 registerForm내의 <form:errors> 코드를 통해 화면에 보여준다.
}
}
- 하나의 Validator로 여러 객체를 검증할 때, 글로벌 Validator로 등록한다.
*글로벌 Validator로 등록하는 방법: servlet-context.xml 파일 내에 아래 bean을 등록한다. 그럼 검증할 객체 앞에 @Valid를 붙여 사용할 수 있다.
<annotation-driven validator="globalValidator"/>
<beans:bean id="globalValidator" class="com.xxx.xxxx.ch2.GlobalValidator"/>
// class="패키지명.클래스이름(glovalValidator로 사용하기 위해 만든 클래스)" 를 id="globalValidator"로 등록한다.
*글로벌 Validator와 로컬 Validator를 동시에 적용하는 방법
// 1.컨트롤러 안에 @InitBinder가 붙은 메서드를 만든다.
@InitBinder
public void toDate(WebDataBinder binder) {
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false));
binder.addValidators(new UserValidator()); // 2. 로컬 Validator를 글로벌 Validator로 등록한다.
2.2 MessageSource
- 다양한 리소스(파일, 배열 등)에서 메시지를 읽기 위한 인터페이스.
- 이 인터페이스를 이용하여 사용자 화면에 에러 코드에 의한 에러 메세지를 띄울 수 있다.
- 프로퍼티 파일을 메시지 소스로 하는 ResourceBundleMessageSource를 servlet-context.xml에 등록
<beans: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 -->
//위에 erro_message 이 이름은 프로퍼티파일의 이름과 동일해야한다.
</beans:list>
</beans:property>
<beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>
- 에러 코드에 대한 메세지가 있는 프로퍼티 파일(.properties)을 만들어줘야하고 이 프로퍼티 파일이 화면에 출력된다.
2.2 검증메세지의 출력
- 스프링이 제공하는 커스텀 태그 라이브러리를 사용한다.
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
- <form> 대신 <form:form>을 사용한다.
<form:form modelAttribute="user"> // 큰 따옴표("")사이에는 검증할 객체가 온다.
- <form>태그 안에 <form:errors>로 에러를 출력한다. path에 에러 발생 필드를 지정한다. (*은 모든 필드의 에러를 의미한다.)
<form:errors path="id" cssClass="msg"/>
//user 객체의 id 필드에 해당하는 메세지를 보여준다.
'신입 개발자가 되기 위해 공부했던 독학 자료들 > Spring' 카테고리의 다른 글
스프링의 의존성 주입 (0) | 2022.02.14 |
---|---|
Spring DI 활용하기: 이론 (0) | 2022.02.14 |
@RequestParam과 @ModelAttribute (0) | 2022.02.10 |
스프링 웹 출력시 한글 깨짐 방지를 위해 web.xml 파일에 입력하면 되는 <filter> 태그 (0) | 2022.02.10 |
스프링 MVC의 Controller (0) | 2022.02.08 |