본문 바로가기

스프링

091.WEB_모델과바인딩검증_프로퍼티에디터_싱글톤

@ModelAttribute의 역할 3가지

1) 파라미터 타입의 오브젝트를 만든다.

- User user라는 파라미터 선언이 있다면 User 타입의 오브젝트를 생성하므로 디폴트 생성자가 반드시 필요하다.

- @SessionAttribute에 의해 세션에 저장된 모델 오브젝트가 있다면, 새로운 오브젝트를 생성하는 대신 세션에 저장되어 있는 오브젝트를 가져온다.

2) 준비된 모델 오브젝트의 프로퍼티에 웹 파라미터를 바인딩 해준다.

- 프로퍼티가 스트링이 아니라면 적절한 변환이 필요함.

- 스트링 전환이 불가능한 경우라면, BindingResult 오브젝트 안에 바인딩 오류를 저장해서 컨트롤러로 넘겨주거나 예외를 발생시킨다. (088번 참조)

3) 모델의 값을 검증

- 필수프로퍼티인데 값이 없거나, 숫자의 경우 지정된 범위를 넘었거나 등의 검증작업을 진행한다.

 > 스프링에선 모델 검증 분리가 가능하다.

 > 이번 절에선 어떻게 바인딩 검증이 가능한지를 살펴본다.

 

* 스프링에서 바인딩이란 : 오브젝트의 프로퍼티에 값을 넣는 것.

1. 위처럼 com.mysql.jdbc.Driver 문자열을 Class타입으로 프로퍼티에 넣을 수 있을까?

> 스프링 프로퍼티 바인딩 기능을 적용했기 때문이다.

2. HTTP를 통해 전달되는 클라이언트의 요청을 모델 오브젝트로 변환할 경우 문자열을 바인딩 과정 중에 적절한 변환을 해준다.

* @Controller에서의 바인딩 기능은 @ModelAttribute 뿐만 아닌 @RequestParam, @PathVariable 같은 단일 파라미터에 대한 바인딩에도 적용된다.

 

스프링의 내장 PropertyEditor

 

* PropertyEditor

> 스프링의 API가 아닌 자바빈 표준에 정의된 인터페이스.

> 자바빈은 원래 GUI 컴포넌트를 만들 때 활용하도록 설계됐다. 위처럼 문자열을 입력하는 것만으로 다양한 타입으로 변환 기능을 하게했다. GUI컴포넌트로 자바빈은 성공하지 못해 getter, setter 역할의 자바빈 형태가 남았지만, 스프링은 스프링 PropertyEditor를 통해 XML <property value=””>를 통해 프로퍼티 값을 실제 오브젝트로 활용될 수 있게 했다.

 

프로퍼티 에디터는 XMLvalue 애트리뷰트뿐 아니라 @Controller의 파라미터에도 동일하게 적용된다. URL/hello?charset=UTF-8이라면, 컨트롤러의 메소드의 charset 파라미터는 UTF-8로 설정된 Charset 타입의 오브젝트를 받게 될 것이다.

* 호출받은 파라미터의 타입으로 PropertyEditor의 구현체를 찾아 해당 타입으로 셋팅

 

PropertyEditor 흉내내보기

 

1.     Com.springsource.javax.activation : Spring java-mail

2.     Com.springsource.javax.mail : Spring java-mail

3.     Com.springsource.javax.servlet.jsp.jstl : JstlView

4.     Com.springsource.org.aopalliance : Spring ProxyFactoryBean

5.     Com.springsource.org.aspectj.tools : AspectJExpressionPointcut 포인트컷 표현식 지원

6.     Com.springsource.org.cator.core : 자동으로 자바 오브젝트를 XML로 변환 지원.

7.     Com.springsource.org.cator.xml : 자동으로 자바 오브젝트를 XML로 변환 지원.

8.     Com.springsource.junit : junit

9.     Commons-logging : Spring-context가 사용

10.   Jackson-annotation : MappingJackson2JsonView

11.   Jackson-core : MappingJackson2JsonView

12.   Jackson-databind : MappingJackson2JsonView

13.   Mail : java-mail

14.   Mockito : 목 프레임워크 중 Mockito

15.   Mysql-connector : Mysql JDBC

16.   Org.springframework.context.support : Spring java-mail

17.   Org.springframework.oxm : Object-XML Mapping. 마샬러 빈을 지정해 모델에서 변환에 사용할 오브젝트를 지정해주면, OXM마샬러를 통해 모델 오브젝트를 XML로 변환해서 뷰의 결과로 사용할 수 있음.

18.   Spring-aop : 스프링 기능 자체의 aop, Spring ProxyFactoryBean

19.   Spring-bean : 스프링 코어와 함께 의존성 주입 제공 (Core Container)

20.   Spring-context : 스프링 코어, BeanFactory를 확장한 어플리케이션 컨텍스트 구현, 리소스 로드 및 국제화 지원(Core Container)

21.   Spring-core : 다른 스프링 모듈이 사용하는 유틸리티(Core Container)

22.   Spring-expression : EL 확장 Bean속성(배열, 컬렉션 포함).(Core Container)

23.   Spring-jdbc : 스프링이 지원하는 jdbc.

24.   Spring-test.jar

-       @RunWith : Junit 프레임워크의 테스트 실행방법을 확장시 사용.

-       SpringJUnit4ClassRunner : 어플리케이션컨텍스트를 만들고 관리하는 확장 클래스

-       @ContextConfiguration(경로) : 자동으로 만들어줄 어플리케이션 컨텍스트 설정파일

25.   Spring-tx : DuplicateKeyException.class 파일 존재 및 스프링 트랜잭션

26.   Spring-web : ContextLoaderListener 내장 루트 컨텍스트(서비스,DAO)

27.   Spring-webmvc : SpringMVC를 위한 리졸버, 모델앤뷰 등 지원

 

테스트 디스패처 서블릿을 위한 인터페이스

 

* 번거로운 XML 설정 대신 AbstractDispatcherServletTest를 사용

25. DispatcherServlet 초기화시 ServletConfig 오브젝트를 만들어 초기화해야함.

50. ContextRoot 값이 있을 때 추가해줌

54. setServletPath : 컨텍스트 root를 넣을 수 있도록 셋팅

58. requesturi 정보와 get,post mothod 셋팅 및 리스판스 객체 생성

 

81. 테스트 DispatcherServlet Init

 

 

15. 바인딩 과정에서 변환할 파라미터 또는 모델 프로퍼티의 타입에 맞는 프로퍼티 에디터가 자동으로 선정돼서 사용된다. 만약 스프링이 지원하지 않는 타입을 파라미터로 사용한다면, 이때는 직접 프로퍼티 에디터를 만들어서 적용할 수도 있다.

 

내장 프로퍼티 에디터를 통해 charset으로 변환됨.

 

스프링이 디폴트로 적용해주는 프로퍼티 에디터는 자바의 기본적인 타입 20여 가지에 불과하다. 직접 정의한 타입으로 직접 바인딩을 하고 싶다면, 프로퍼티 에디터를 직접 작성하면 된다.

> 위 이늄 타입은 스프링이 당연히 알지 못한다. 따라서 파라미터를 직접 매핑할 수 있는 방법은 없다.

18. valueOf : 이 값에 대응되는 Level 오브젝트를 가져오는 스태틱 메소드(인스턴스 변수나 인스턴스 메서드를 사용하지 않는 메서드).

12. Level 오브젝트의 값을 읽어오는 메소드

 

1. PropertyEditor에서 변환을 위해 사용되는 메소드는 총 4개다. HTTP 요청 파라미터와 같은 문자열은 스트링 타입으로 서블릿에서 가져온다.

1) setAsText() 메소드를 이용해 스트링 타입의 문자열을 넣고,

2) getValue()로 변환된 오브젝트를 가져온다

3) 반대로 오브젝트를 다시 문자열로 바꿀 때는 setValue()로 오브젝트를 넣고

4) getAsText()를 통해 메소드로 변환된 문자열을 가져온다.

2. 여기서 getValue()setValue()는 오브젝트를 저장하고 가져올 때 사용하는 것이므로 손댈 것 없고, 구현해줘야 할 메소드는 바로 setAsText()getAsText() 두가지다.

3. PropertyEditor를 만들 땐 직접 인터페이스를 구현하기보다는 기본구현이 되어있는 PropertyEditorSupport 클래스를 상속해서 필요한 메소드만 오버라이드 해주는 편이 낫다.

4. setAsText()에서는 스트링 타입 파라미터를 오브젝트로 변환해서 넣어주고, getAsText()에서는 그 반대로 변환해주는 코드를 작성하면 된다.

 

프로퍼티 에디터를 잘 활용하면 반복적으로 등장하는 변환 코드를 제거할 수 있고, 특히 클라이언트와 요청 파라미터를 적절한 타입으로 가져올 수 있게 해주면 컨트롤러 코드와 뷰 모두 깔끔하게 만들 수 있다.

15. setValue : 인자의 Level오브젝트를 ProertyEditor 객체로 변환

16. getAsText : 21라인에 구현한대로 인트 밸류를 스트링형으로 받는다.

23. getValue : Level 타입의 외브젝트를 가져옴

25. setAsText : 스트링 타입의 문자열로 오브젝를 생성한다.

27. setValue : 인자의 오브젝트를 PropertyEditor 객체로 변환

 

* @MVC에는 디폴트 프로퍼티 에디터만 등록되어 있다. 어떻게 Level이 바인딩될까?

22. @Controller, @RequestParam 등의 호출해줄 책임이 있는 AnnotationMethod~AdapterHTTP 요청을 파라미터 변수에 바인딩해주는 작업이 필요한 애노테이션을 만나면 먼저 WebDataBinder를 만든다. 따라서 @RequestParam과 같은 메소드 파라미터 바인딩에 적용하려면 바로 WebDataBinder에 프로퍼티를 직접 등록해줘야한다. 스프링은 WebDataBinder 초기화를 위해 @InitBinder라는 애노테이션을 제공한다.

23. @InitBinder가 붙은 initBinder(WebDataBinder) 메소드가 메소드 파라미터를 바인딩하기 전에 자동으로 호출되어, WebDataBinder에 커스텀 프로퍼티 에디터를 추가할 수 있는 기회를 제공해준다.

23~31. WebDataBinder 테스트. 바인더 오브젝트를 만들고 추가로 프로퍼티 에디터를 등록해주면 타입변환 메소드를 이용할 수 있다. 물론 코드에서 직접 만들어 수동으로 바인딩할 필요는 없다. @InitBinder가 붙은 메소드를 적절히 이용해서 추가해주는 것은 개발자의 몫이다. 메소드 애노테이션타입 변환을 위해 유연한 사고가 필요하다.

37,38. 문자열을 입력받아 setValue를 통해 PropertyEditor타입으로 변환.

32. registerCustomerEditor : 적용 타입과 프로퍼티 에디터 두 개를 파라미터로 등록하면 해당 타입을 가진 바인딩 대상이 나오면 항상 프로퍼티 에디터가 적용된다.(방식1. 기본방식)

* 하지만 @InitBinder는 하나의 컨트롤러에만 동작한다. 모든 컨트롤러에서 사용하려면 WebBinderInitializer 인터페이스를 구현해서 작성하는 편이 좋다.(쭉 내려가면 예시 참조)

 

23. registerCustomEditor : “age” 특정 이름의 프로퍼티에만 적용되는 프로퍼티에디터. @RequestParam과 같은 단일 파라미터 바인딩에는 적용되지 않고, @ModelAttribute로 지정된 모델 오브젝트의 프로퍼티 바인딩에 사용할 수 있다.

47. setAsText : 스트링 타입의 문자열로 PropertyEditor타입 오브젝트를 생성한다.

51. setValue : PropertyEditorSupport메서드. 인자의 오브젝트를 ProertyEditor 객체로 변환

44. getAsText : ProertyEditor 객체를 원하는 형태로 변환하여 전달.

* 커스텀 프로퍼티 에디터를 먼저 적용한 후(우선순위) 디폴트 프로퍼티 에디터를 사용한다.

 

45. WebBindingInitializer : 하나의 컨트롤러가 아닌 모든 컨트롤러에 PropertyEditor를 등록가능. 인터페이스를 구현하여 커스텀 프로퍼티 에디터를 추가한다.

36~44. WebBindingInitializer를 구현해서 만든 클래스를 빈으로 등록.(XML일경우 아래스샷)

30~35. @Controller를 담당하는 어댑터 핸들러의 프로퍼티에 DI

이렇게 하면 모든 컨트롤러에 적용되어 필요없는 컨트롤러에도 적용되어 낭비가 발생하긴 하지만, 프로퍼티 에디터 자체가 워낙 간단한 클래스이므로 자주 생성되도 별문제가 되지는 않는다. 적절한 기준을 정해 등록하는 방법을 검토해보자.

* 위처럼 프로퍼티 에디터는 매번 새로만든다. 싱글톤 빈으로 등록될 수 없다. 프로퍼티 에디터에 setValue()를 이용해 오브젝트를 넣은 후 getAsText()로 변환된 문자열을 가져오는 식이다. 프로퍼티 에디터에 의해 타입이 변경되는 오브젝트는 상태를 갖고 있기 때문에 싱글톤은 안된다.