데이터 바인딩 추상화(1) : PropertyEditor

de_sj_awa·2021년 6월 25일
0
post-custom-banner

1. PropertyEditor

DataBinder

org.springframework.validation.DataBinder

기술적인 관점: 프로퍼티 값을 타겟 객체에 설정하는 기능
사용자 관점: 사용자 입력값을 애플리케이션 도메인 모델에 동적으로 변환해 넣어주는 기능. 해석하자면: 입력값은 대부분 “문자열”인데, 그 값을 객체가 가지고 있는 int, long, Boolean, Date 등 심지어 Event, Book 같은 도메인 타입으로도 변환해서 넣어주는 기능.

PropertyEditor

  • 스프링 3.0 이전까지 DataBinder가 변환 작업 사용하던 인터페이스
  • 쓰레드-세이프 하지 않음 (상태 정보 저장 하고 있음, 따라서 싱글톤 빈으로 등록해서 쓰다가는...)
  • Object와 String 간의 변환만 할 수 있어, 사용 범위가 제한적 임. (그래도 그런 경우가 대부분이기 때문에 잘 사용해 왔음. 조심해서..)

도메인 클래스

package com.example.spring9;

public class Event {
    private Integer id;
    private String title;

    public Event(Integer id){
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Event{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}
@RestController
public class EventController {
   
    @GetMapping("/event/{event}")
    public String getEvent(@PathVariable Event event){ // PathVariable에 해당하는 부분을 Event 도메인으로 받음
        System.out.println(event);
        return event.getId().toString();
    }
}

테스트 코드

@RunWith(SpringRunner.class)
@WebMvcTest
public class EventControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void getTest() throws Exception{
        mockMvc.perform(get("/event/1"))
                .andExpect(status().isOk())
                .andExpect(content().string("1"));
    }
}

그러나 이 테스트는 실패한다. 1이라는 숫자를 Event 타입으로 변경할 수 없기 때문이다. 그 다음에 PropertyEditor를 만든다.

public class EventEditor extends PropertyEditorSupport {
    @Override
    public String getAsText() {
        Event event = (Event) getValue();
        return event.getId().toString();
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        setValue(new Event(Integer.parseInt(text)));
    }
}

그러나 이 PropertyEditor가 공유하고 있는 "value"는 상태를 공유하며 Stateful하므로, 쓰레드 세이프하지 않다. 따라서 PropertyEditor는 절대 다음과 같이 빈(Singleton)으로 등록하면 안된다.

@Component
public class EventEditor extends PropertyEditorSupport {
    @Override
    public String getAsText() {
        Event event = (Event) getValue();
        return event.getId().toString();
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        setValue(new Event(Integer.parseInt(text)));
    }
}

그래서 PropertyEditor는 빈으로 등록하지 않고 다음과 같이 컨트롤러에 등록하여 사용한다.

@RestController
public class EventController {
    
    @InitBinder
    public void init(WebDataBinder webDataBinder){
        webDataBinder.registerCustomEditor(Event.class, new EventEditor());
    }

    @GetMapping("/event/{event}")
    public String getEvent(@PathVariable Event event){
        System.out.println(event);
        return event.getId().toString();
    }
}

PropertyEditor는 Object-String 간의 변환만 지원하며 구현도 번거롭고 스레드 세이프 하지 않기 때문에 빈으로 등록해서 사용할 수도 없다. 따라서 스프링 3.0 이후부터는 Converter와 Formatter를 주로 사용하게 된다.

참고

profile
이것저것 관심많은 개발자.
post-custom-banner

0개의 댓글