데이터를 믿고 쓰기 위해 필요한 것 : Validation
그 데이터를 원하는 형식으로 받고 싶은 경우 : Data Binding
유효성검증
사용자 또는 타 서버의 요청(http request) 내용에서 잘못된 내용이 있는지 확인하는 행위를 뜻함
개발자들이 주로 알아야할 검증은 크게 두종류로 나뉜다.
스프링은 웹 레이어에 종속적이지 않은 방법으로 밸리데이션을 하려고 의도하고 있으며 주로 아래 두가지 방법을 활용하여 밸리데이션 진행(둘다 데이터 검증에 가까움)
JavaBean 기반으로 간편하게 개별 데이터 검증
요즘에 가장 많이 활용되는 방법 중 하나이며, 아래 코드처럼 JavaBean 내에 어노테이션으로 검증방법을 명시함
public class MemberCreationRequest{
@NotBlank(message="이름을 입력해주세요.")
@Size(max=64, message="이름의 최대 길이는 64자 입니다.")
private String name;
@Min(0, "나이는 0보다 커야 합니다.")
private int age;
@Email("이메일 형식이 잘못되었습니다.")
private int email;
}
요청 dto에 어노테이션으로 명시 후 아래처럼 @Valid 어노테이션을 해당 @RequestBody에 달게 되면, Java Bean Validation을 수행한 후 문제가 없을 때만 메서드 내부로 진입이 된다.
@PostMapping(value="/member")
public MemberCreationResponse createMember(
@Valid @RequestBody final MemberCreationRequest memberCreationRequest){
}
)
public class Person{
private String name;
private int age;
}
Person이라는 javaBean 객체가 있을 때, 아래는 해당 인스턴스에서만 활용되는 validator이다. 인터페이스에 있는 두개의 메서드는 아래와 같은 역할을 한다.
public class PersonValidation implements Validator {
public boolean supports(Class clazz){
return Person.class.equals(clazz);
}
pubilc void validate(Object obj, Erros e){
ValidationUtils.rejectIfEmpty(e,"name","name.empty");
Person p = (Person) obj;
if(p.getAge() < 0){
e.rejectValue("age","negativevalue");
} else if(p.getAge() > 110){
e.rejectValue("age", "too.darn.old");
}
}
}
Validation이 너무 여러 군데에 흩어져있으면 테스트 및 유지보수성이 떨어짐
Spring validator의 장단점
사용자나 외부 서버의 요청 데이터를 특정 도메인 객체에 저장해서 우리 프로그램에 Request에 담아주는 것을 뜻한다.
S(Source)라는 타입을 받아서 T(Target)이라는 타입으로 변환해주는 Interface 인터페이스의 모양은 아래와 같다.
package org.springframework.core.convert.converter;
public interface Converters<S,T>{
T convert(S source);
}
// 요청
GET /user-info
x-auth-user : {"id":123, "name":"Paul"}
// 유저 객체
public class XAuthUser{
private int id;
private String name;
}
@GetMapping("/user-info")
pubilc UserInfoResponse getUserInfo(
@RequestHeader("x-auth-user") XAuthUser xAuthUser){
}
헤더에 담긴 json 형식 문자열을 XAuthUser에 바로 담고 싶은 경우 아래와 같이 Converter를 Bean으로 등록
@Component
public class XAuthUserConverter implements Converter<String, XAuthUser>{
@Override
public XAuthUser convert(String source){
return objectMapper.readValue(source,XAuthUser.class);
}
}
이와 비슷하게 PathParameter나 기타 특수한 경우의 데이터를 특정 객체에 담고 싶은 경우
특정 객체와 String간의 변환을 담당
package org.springframework.format.datetime;
public final class DateFormatter implements Formatter<Date>{
public String pring(Date date, Locale locale){
return getDateFormat(locale).format(date);
}
public Date parse(String formatted, Locale locale) throws ParseException{
return getDataForma(locale).parse(formatted);
}
}
Formatter도 Converter와 마찬가지로 Spring Bean으로 등록하면 자동으로 ConversionService에 등록시켜주기 때문에 필요(요청/응답 시 해당 데이터 타입이 있는 경우)에 따라 자동으로 동작하게 된다.