Closed wooyounggggg closed 3 years ago
애너테이션 기반 검증
@NotNull
, @Size
등의 애너테이션 적용 자바 구성 클래스에 LocalValidatorFactoryBean
선언
SingerValidationService
클래스 생성
사용자 정의 검증
(1) @interface
, @Retention(RetentionPolicy.RUNTIME)
, @Target(ElementType.TYPE)
등을 사용하여, 사용자 정의 어노테이션 생성
@Target
: 어노테이션이 적용할 위치를 결정
@Retention
: 어떤 시점까지 어노테이션이 영향을 미치는지 결정
(2) ConstraintValidator 인터페이스를 구현한 CountrySingerValidator
클래스의 isValid() 메서드 안에, 사용자 정의 검증 로직 구현
(3) 도메인 클래스에 (사용자가 정의한) '클래스 레벨' 어노테이션 적용
사용자 정의 검증을 @AssertTrue
로 구현하는 방법
도메인 클래스인 Singer
내부에 검증 로직을 구현할 수도 있음
Singer 엔터티의 birthDate 필드의 타입은 org.joda.time.DateTime
입니다.
그런데 XML 구성이나 프로퍼티 파일에 "yyyy-mm-dd" 형식을 지키는 String을 사용할 겁니다.
따라서 문자열을 bithDate 필드에 요다 타임 타입으로 바인딩 해줄 도구가 필요합니다.
(PropertyEditor는 문자열을 특정 타입으로 변환하는 데 적합)
"문자열 -> 특정 타입" 매핑 로직은 PropertyEditor.setAsText(text: String)
에서 구현함.
PropertyEditorRegistrar
(프로퍼티 에디터 등록자)는 매핑 로직을 구현한 PropertyEditor
들을 PropertyEditorRegistry
(프로퍼티 에디터 저장소)에 등록함.
CustomEditorConfigurer
는 PropertyEditorRegistrar
를 리스트로 보유함.
메서드 인자로 프로퍼티 저장소 객체를 전달받습니다. 아마 CustomEditorConfigurer 쪽에서 넣어주는 것 같죠? 해당 저장소에 커스텀 프로퍼티 에티터들을 등록하도록 설계되어있습니다.
public interface PropertyEditorRegistrar {
void registerCustomEditors(PropertyEditorRegistry registry);
}
프로퍼티 에디터 구현체 PropertyEditorSupport
입니다.
setAsText(text: String)
: 문자열을 특정 타입으로 변환하는 역할을 하고 있습니다.setValue(value: Object)
: 문자열이 표상한 특정 타입 객체를 프로퍼티 에디터에 바인딩합니다.public class PropertyEditorSupport implements PropertyEditor {
public void setAsText(String text) throws java.lang.IllegalArgumentException {
if (value instanceof String) {
setValue(text);
return;
}
throw new java.lang.IllegalArgumentException(text);
}
public void setValue(Object value) {
this.value = value;
firePropertyChange();
}
public void getValue(Object value) {
return this.value;
}
책에서 "문자열->요다 타임" 변환이 setAsText(text: String)
에서 구현됩니다.
public class DateTimeEditorRegistrar implements PropertyEditorRegistrar {
private DateTimeFormatter dateTimeFormatter;
public DateTimeEditorRegistrar(String dateFormatPattern) {
dateTimeFormatter = DateTimeFormat.forPattern(dateFormatPattern);
}
// 커스텀 프로퍼티 에디터를 저장소에 등록
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(DateTime.class, new DateTimeEditor(dateTimeFormatter));
}
// 문자열 -> 요다 타입 매핑을 구현하는 커스텀 프로퍼티 에디터
private static class DateTimeEditor extends PropertyEditorSupport {
private DateTimeFormatter dateTimeFormatter;
public DateTimeEditor(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
// 문자열 -> 요다 타입 매핑 로직을 구현
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(DateTime.parse(text, dateTimeFormatter));
}
}
}
PropertyEditor
는 stateful
(→ thread-unsafe
) 하고 String과 자바 객체 사이의 변환만 가능하다는 명확한 한계가 존재함Converter
와 Formatter
를 사용한다.Converter
그리고 후술할 Formatter
의 경우 ConversionService
인터페이스를 사용하여 데이터 바인딩이 이뤄진다.implements Converter<S, T>
로 변환할 두 클래스를 정의한다public class EventConverter {
public static class StringToEventConverter implements Converter<String, Event> {
@Override
public Event convert(String s) {
return new Event(Integer.parseInt(s));
}
}
public static class EventToStringConvert implements Converter<Event, String> {
@Override
public String convert(Event event) {
return event.getId().toString();
}
}
}
ConversionService
인터페이스의 인스턴스를 구성해야한다ConversionServiceFactoryBean
클래스를 사용하여 정의한다@Configuration
public class AppConfig {
@Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
Set<Converter> convs = new HashSet<>();
convs.add(StringToEventConverter());
convs.add(EventToStringConvert());
conversionServiceFactoryBean.setConverters(convs);
conversionServiceFactoryBean.afterPropertiesSet();
return conversionServiceFactoryBean;
}
@Bean
StringToDateTimeConverter converter() {
return new StringToDateTimeConverter();
}
//
@Bean
SingerToAnotherSingerConverter singerConverter() {
return new SingerToAnotherSingerConverter();
}
}
implements Formatter<S>
로 변환할 클래스를 정의public class DateTimeFormatter implements Formatter<DateTime> {
private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
private String datePattern = DEFAULT_DATE_PATTERN;
private DateTimeFormatter dateFormat;
@PostConstruct
public void init() {
dateFormat = DateTimeFormat.forPattern(datePattern);
}
@Override
public DateTime parse(String dateTimeString, Locale locale) throws ParseException {
return dateFormat.parseDateTime(dateTimeString);
}
@Override
public String print(DateTime dateTime, Locale locale) {
return dateFormat.print(dateTime);
}
}
ConversionService
인터페이스의 인스턴스를 구성해야한다FormattingConversionServiceFactoryBean
클래스를 사용하여 정의한다@Configuration
public class AppConfig {
@Bean
public FormattingConversionServiceFactoryBean conversionService() {
FormattingConversionServiceFactoryBean formattingConversionServiceFactoryBean
= new FormattingConversionServiceFactoryBean();
Set<Formatter> formatters = new HashSet<>();
formatters.add(dateTimeFormatter());
formattingConversionServiceFactoryBean.setFormatters(formatters);
formattingConversionServiceFactoryBean.afterPropertiesSet();
return formattingConversionServiceFactoryBean;
}
}
FormattingConversionServiceFactoryBean
을 extends
하여 사용자 정의 포매터를 사용하고 있음@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToEventConverter());
registry.addFormatter(new EventFormatter());
}
}
public class EventConverter {
@Component
public static class StringToEventConverter implements Converter<String, Event> {
@Override
public Event convert(String s) {
return new Event(Integer.parseInt(s));
}
}
@Component
public static class EventToStringConvert implements Converter<Event, String> {
@Override
public String convert(Event event) {
return event.getId().toString();
}
}
}
주제
10장 타입 변환과 포매팅을 사용해 유효성 검증하기를 읽고 중요✨ 하다고 생각하는 키워드와 선택한 이유에 대해서 코멘트로 달아주세요.
연관챕터
73