데이터 검증
- 필수 데이터 존재 유무
- 문자열 길이나 숫자형 데이터 값의 범위
- 이메일/신용카드 형식이 맞는지
비즈니스 검증
- 서비스 정책에 따라 데이터를 확인하여 검증
- 예)배달앱인 경우 배달 요청을 할때 해당 주문건이 결제 완료 상태인지 확인
- 예) 사용자가 금액을 보내야 할때 보내는 금액이 계좌에 남아있는 금액보다 작은지 여부
- 경우에 따라 외부 API를 호출하거나 DB의 데이터까지 조회하여 검증하는 경우도 존재
Java Bean Validation
- 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; // the usual getters and setters... }
- 검증 방식에 따라 각각의 다른 어노테이션을 적용한다.
- NotBlank(빈칸 금지),Size(크기) ,Min(최소) , Email(이메일형식)
- 어노테이션을 붙인다고 단순하게 바로 검증이 되는것은 아니다.
@PostMapping(value = "/member") public MemeberCreationResponse createMember( @Valid @RequestBody final MemeberCreationRequest memeberCreationRequest) { // member creation logics here... }
- 작성한 어노테이션을 @Valid와 @RequestBody를 이용하여 적용시키고 문제가 없을때 정상적으로 서비스를 실행 시킨다.
- 검증중 실패가 발생하면 MethodNotArgumentNotValidException이 발생한다.
Spring Validator를 인터페이스 구현을 통한 Validation
public class Person { private String name; private int age; // the usual getters and setters... }
- 위처럼 Person이라는 클래스에 name과 age라는 javaBean 객체가 있을때
이를 활용하여 Implements를 시킨다면public class PersonValidator implements Validator { /** * This Validator validates only Person instances */ public boolean supports(Class clazz) { return Person.class.equals(clazz); } public void validate(Object obj, Errors 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"); } } }
- supports: Validator가 동작할 조건을 정의,주로 class타입을 비교
- validate : 원하는 검증을 진행한다.
Convert<S,T> Interface
- S(Source)라는 타입을 받아서 T(Target)이라는 타입을 변환해주는 interface이다.
package org.springframework.core.convert.converter; public interface Converter<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; // the usual getters and setters... } @GetMapping("/user-info") public UserInfoResponse getUserInfo( @RequestHeader("x-auth-user") XAuthUser xAuthUser){ // get User Info logic here... }
- 파라미터에 json형식이 문자열이 담겨오는 경우 해당 문자열을 특정 Dto에 담고 싶은 경우
- json 형식의 문자열을 XAuthUser객체에 바로 담고 싶은 경우 ConverterBean으로 등록하면 된다.
@Component public class XAuthUserConverter implements Converter<String, XAuthUser> { @Override public XAuthUser convert(String source) { return objectMapper.readValue(source, XAuthUser.class); } }
- 즉 Json 형식으로 받은 데이터는 문자열이기 때문에 이를 형식에 맞게 바꾸기 위해서 Converter가 필요하다.
- Converter<String , XAuthUser>는 String부분은 바뀌기전 데이터 형식
Json 문자열 형식을 변환하기 때문에 String 자료형을 명시하고 이후에 XAuthUser는 내가 데이터를 변환하면 어떤 클래스 형식의 멤버변수로 변경 할 것인지를 명시하는 것이다.
package org.springframework.format.datetime;
public final class DateFormatter implements Formatter<Date> {
public String print(Date date, Locale locale) {
return getDateFormat(locale).format(date);
}
public Date parse(String formatted, Locale locale) throws ParseException {
return getDateFormat(locale).parse(formatted);
}
// getDateFormat 등 일부 구현은 핵심에 집중하기 위해 생략...
}