반응형
1. WebDataBinder
쿼리스트링이 담긴 URL을 요청하면, 파라미터 Map으로 쿼리시트링에 담긴 값들이 담긴다.
Map은 다시 컨트롤러의 매개변수로 전달받은 객체에 담기게 된다.
WebDataBinder
: Map의 Value는 모두 String이기 때문에, 객체의 자료형에 맞춰서 타입 변환과 데이터 검증을 수행하고, BindingResult에 타입 변환과 데이터 검증의 결과를 담아서 응답한다.
- 회원 가입 예제
- 타입 변환
- String -> Date 변환 (변환 메서드를 정의한 변환기 이용해 변환)
- String[] -> String 변환 (여러 개 문자열로 이루어진 문자열 배열을 문자열로 스프링이 자동변환)
- 데이터 검증
- BindingResult타입의 매개변수로 성공한 결과 혹은 실패시 에러가 담긴 결과 반환해 해당 결과를 컨트롤러에서 사용
- 타입 변환
- Date
- 기본 형식인 "yyyy/MM/dd"으로 String 문자열로 요청된 경우, 자동 변환 수행
- 하지만 나머지 형식은 변환기에서 변환할 형식 지정 필요 : @DataTimeFormat 어노테이션으로 필드에 직접 형식 지정 가능
- 해당 요청을 담는 객체 바로 다음에 BindindResult를 함께 매개변수로 전달하면, 예외가 발생해도 컨트롤러에 결과를 담아서 컨트롤러가 처리하도록 전달한다.
- 예외가 발생했을 경우, 컨트롤러가 처리하는 방식
- msg를 문자열로 지정해, URLEncoder로 변환 후 모델에 담아서 사용자에게 에러 결과를 URL재작성해 전달
- 스프링 Validator 인터페이스 구현하는 방법으로 해당 인스턴스에서만 활용되는 Validator로 인터페이스에는 두 개의 메서드가 존재한다.
- supoorts : 동작할 조건을 정의하는데, 일반적으로 클래스의 타입을 비교한다.
- validate : 원하는 검증을 수행한다.
- Java Bean Validation 이용하는 방법으로, 자바 빈 객체 내에서 어노테이션으로 검증방법 명시
: 요청 DTO에 어노테이션으로 명시하고, @RequestBody 앞에 @Valid 어노테이션을 붙여서 검증
만약 자바 빈 Validation으로 검증 실패시 MethodArgumentNotValidException이 발생한다.- @NotBlank
- @NotNull
- @Size
- @Min
- sns을 문자열 배열 바꿔도 스프링이 자동 변환
2. 데이터 변환 방법
: 인스턴스 변수를 사용하는 PropertyEditor와 싱글톤에서 사용하는 Converter
- 문자열을 날짜 형식으로 변환기 추가
- Data Binding 방식 : 빈으로 등록시 ConversionService에 등록돼 자동으로 동작한다.
- PropertyEditor를 상속받아 양방향으로 변환을 수행하는 @InitBinder을 이용해 해당 클래스에서 변환수행하는데 인스턴스 변수를 변경하는 방식으로 싱글톤에선 사용이 불가능하다.
- 단방향으로 변환을 수행하기 위해서 Conveter를 구현한 클래스를 이용
- Formatter는 특정 객체와 문자열 간의 변환을 담당 인터페이스
- print : API 요청에 대한 응답에 대해서, 전달 받은 데이터를 원하는 형태의 문자열로 변환
- parse : API 요청을 받아올 때, 문자열로 된 형식의 데이터를 원하는 형식으로 변환
- Data Binding 방식 : 빈으로 등록시 ConversionService에 등록돼 자동으로 동작한다.
- @InitBinder 이용해 문자열을 날짜로 변환형식 전달해 변환에러 해결
- WebDataBinder를 매개변수로 받는 메서드 정의
- 원하는 날짜형식을 전달하는 SimpleDateFormat 객체 생성
- 전달받은 WebDataBinder 매개변수의 registerCustomEditor 메서드 호출하는데 스프링이 제공하는 CustomDataEditor 객체를 생성해서 만들어둔 형식과 빈값 허용여부를 인자로 전달
- 커스텀변환기로 형식 확인 후, 디폴트 변환기로 형식 확인해 변환
- 취미 필드 추가해 문자열을 구분자로 #지정해 문자열 배열로 들어가도록 변환 [PropertyEditor 이용]
- registerCustomEditor의 원하는 필드와 StringPropertyEditor 객체 생성해 구분자를 인자로 전달
3. Converter와 ConversionService
- Converter
- 싱글톤에서 사용가능한 단방향 타입 변환기
- ConversionService
- 여러 컨버터 등록 가능
- 타입 변환 서비스 제공
- WebDataBinder에 DefaultFormattingConversionService 자동 등록
- 모든 컨트롤러에서 변환 - ConfigurableWebBindingInitializer
- 특정 컨트롤러에서 변환 - @InitBinder가 붙은 메서드 작성
4. Fomater
- Printer<T>와 Parser<T> 인터페이스를 상속하는 Formatter
- 양방향 타입 변환
- 바인딩할 필드에 어노테이션으로 직접 적용 - @NumberFomart, @DataTimeFormat
5. Validator [컨트롤러 내의 관심사 분리]
- 객체를 검증하기 위한 인터페이스로, 객체 검증기 구현에 사용
- supports : 검증 가능한 객체 여부를 판단하기 위한 메서드로 일반적으로 클래스 타입을 지정
[클래스타입.class.isAssginableFrom(clazz)로 clazz가 해당 클래스 타입 또는 그 자손인지 확인] - validate : 객체를 검증하는 메서드로 검증할 객체와 검증시 발생한 에러 저장소를 인자로 전달
- supports : 검증 가능한 객체 여부를 판단하기 위한 메서드로 일반적으로 클래스 타입을 지정
- BindingResult의 조상클래스인 Errors 인터페이스
- 메서드
- 객체 전체에 대한 에러를 전달하는 reject 메서드
- 필드에 대한 에러코드를 전달하는 rejectValue 메서드로 세번째 인자에 디폴트 메시지 전달 가능
- Errors 클래스의 rejectValue 메서드 호출해 <key, value> 형식으로
필드와 필드에 해당하는 에러 코드를 인자로 전달, 에러코드에 해당하는 메시지를 전달하는데 사용한다. - ValidationUtils 클래스의 rejectIfEmptyOrWhitespace 메서드를 이용해 간단하게 공백과 빈 문자열 확인 가능
- 메서드
- 뷰에서는 <form:errors>태그를 이용해 전달받은 에러코드에 해당하는 에러 메시지를 전달하는데 사용 가능
6. 검증기를 작성한 컨트롤러안에서만 사용가능한 로컬 Validator
- Validator 수동 검증 : 작성한 Validator 클래스를 호출해 직접 검증
- Validator 자동 검증 : 작성한 Validator를 WebDataBinder에 등록하고 검증할 객체에 @Valid 선언해 검증
@Valid 어노테이션 : 자바 표준 어노테이션으로, Bean Validation API 의존성 추가 필요
7. 하나의 Validator로 여러 객체 검증가능한 글로벌 Validator
- 클래스 정의해 서블릿 컨텍스트에 빈으로 등록
- 서블릿 컨텍스트에 등록한 빈을 <annotation-driven>에 Validator 속성으로 등록
- 글로벌 검증기에 로컬 검증기를 추가하는 방식으로 글로벌 검증기와 로컬 검증기 동시에 적용
- 회원가입 예제에 적용
1. 회원가입 컨트롤러에 수동 검증
- 객체의 필드에 대한 지정한 에러코드로 규칙을 이용해 여러개의 에러코드를 만들거나, 빠진 필드가 존재할 경우에는 디폴트 메시지를 이용해 에러 메시지를 보여준다.
- 필드에 대한 에러코드를 만들면, 여러 개의 에러코드를 만들어 해당하는 메시지를 찾는다.
- 세 번째 인자에 디폴트 메시지를 전달할 수도 있는데 적지 않을 경우 null로 전달 [일반적으로 직접 작성하지 않고 별도의 파일로 관리한다.]
2. 회원가입 컨트롤러에 자동검증 추가
3. 전역 검증기 적용
- 전역 검증기 작성
- 서블릿 컨텍스트에 빈 등록
- 컨트롤러의 @InitBinder에 addValidator로 추가해 글로벌 검증기와 로컬 검증기 동시 적용
8. MessageSource 인터페이스
- MessageSource 인터페이스의 Locale 매개변수로 지역의 국가코드별로 원하는 에러메시지 작성 가능
- 프로퍼티 파일에 <에러코드=에러메시지>으로 에러코드에 해당하는 메시지 코드 작성
- 프로퍼티 파일을 메시지 소스로하는 ResourceBundleMessageSource 등록
- 해당 에러코드의 여러 변형형태순으로 에러메시지 검색 [복잡한 형태 -> 단순한 형태]
: required.user.id -> required.id -> required.java.lang.String -> required
- 에러 메시지 설정
- text의 디폴트 인코딩을 UTF-8으로 설정
- 에러메시지를 properties로 작성
- 서블릿 컨텍스트에 작성한 프로퍼티 이름을 빈으로 등록
required=필수 항목입니다.
required.user.pwd=사용자 비밀번호는 필수 항목입니다.
invalidLength.id=아이디의 길이는 {0}~{1}사이어야 합니다.
<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 -->
</beans:list>
</beans:property>
<beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>
- 검증 메시지 출력
- 스프링이 제공하는 커스텀 태그 라이브러리 추가
- <form> 대신 <form:form>으로 변경
- <form: errors>로 에러 출력하는데 , path에 에러 발생 필드 지정 (*은 모든 필드의 에러) : 자바스크립트 이용
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<form:form modelAttribute="user">
<div class="title">Register</div>
<div id="msg" class="msg">
<c:if test="${not empty param.msg}">
<i class="fa fa-exclamation-circle"> ${URLDecoder.decode(param.msg)}</i>
</c:if>
</div>
<label for="">아이디</label>
<input class="input-field" type="text" name="id" placeholder="8~12자리의 영대소문자와 숫자 조합">
<label for="">비밀번호</label>
<input class="input-field" type="text" name="pwd" placeholder="8~12자리의 영대소문자와 숫자 조합">
<label for="">이름</label>
<input class="input-field" type="text" name="name" placeholder="홍길동">
<div class="sex-chk">
<label><input type="radio" name="sex" value="true" checked="checked"/>남</label>
<label><input type="radio" name="sex" value="false" />여</label>
</div>
<label for="">연락처</label>
<input class="input-field" type="text" name="tel" placeholder="010-1234-1234">
<label for="">이메일</label>
<input class="input-field" type="text" name="email" placeholder="example@fastcampus.co.kr">
<label for="">생년월일</label>
<input class="input-field" type="text" name="birth" placeholder="2020-12-31">
<button>회원 가입</button>
</form:form>
<form: errors> 기본 구조
<form : errors path="id" cssClass="msg"/>
css 적용해 <form: errors> 삽입
<div id="msg" class="msg"><form : errors path="id">
자바스크립트 적용시
<div id="msg" class="msg">
<c:if test="${not empty param.msg}">
<i class="fa fa-exclamation-circle"> ${URLDecoder.decode(param.msg)}</i>
</c:if>
</div>
<script>
function formCheck(frm) {
var msg ='';
if(frm.id.value.length<3) {
setMessage('id의 길이는 3이상이어야 합니다.', frm.id);
return false;
}
return true;
}
function setMessage(msg, element){
document.getElementById("msg").innerHTML = `<i class="fa fa-exclamation-circle"> ${'${msg}'}</i>`;
if(element) {
element.select();
}
}
</script>
package com.fastcampus.comicbookrental.validator;
import com.fastcampus.comicbookrental.dto.UserDTO;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
public class UserDTOValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
// return UserDTO.class.equals(clazz); // 검증하려는 객체가 UserDTO타입인지 확인
return UserDTO.class.isAssignableFrom(clazz); // clazz가 UserDTO 또는 그 자손인지 확인
}
@Override
public void validate(Object target, Errors errors) {
System.out.println("UserDTOValidator.validate() is called");
UserDTO user = (UserDTO)target;
String id = user.getId();
// if(id==null || "".equals(id.trim())) {
// errors.rejectValue("id", "required");
// }
//앞뒤 공백을 잘라서 빈문자열인지 확인하는 클래스와 static메서드 (유틸리티클래스)
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
if(id==null || id.length() < 5 || id.length() > 12) {
errors.rejectValue("id", "invalidLength", new String[] {"", "5", "12"}, null);
//errors.rejectValue("id", "invalidLength", new String[] {"5", "12"}, null);
}
}
}
반응형
'Server Programming > BackEnd Project' 카테고리의 다른 글
50일차 - TIL (0) | 2023.02.01 |
---|---|
49일차 - TIL (0) | 2023.01.31 |
48일차 - TIL (0) | 2023.01.29 |
45일차 - TIL (0) | 2023.01.26 |
44일차 - TIL (0) | 2023.01.25 |