본문 바로가기

스프링

[spring] jaxb_자기참조빈_4

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

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

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

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

5.     Com.springsource.junit : junit

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

7.     Mail : java-mail

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

9.     Mysql-connector : Mysql JDBC

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

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

12.   Spring-bean : 스프링 빈을 활용하는 경우 필요. 스프링의 XML 설정파일과 자바 애노테이션을 파싱 하는데 필요한 클래스 포함

13.   Spring-context : 스프링 코어를 확장한 많은 클래스가 들어 있는데 모든 클래스는 EJB, JNDI(Java Naming Directory Interface), JMX용 클래스와 연동하는데 applicationcontext기능을 사용해야 하며 스프링 리모팅 클래스, 동적 스크립팅 언어(제이루비, 그루비등)와 연동하는 클래스, 빈 유효성검증(JSR-303) API, 스케줄링을 하는 클래스도 포함되어 있다.

14.   Spring-core : 모든 스프링 모듈에서 필요한 모듈. 다른 스프링 모듈에서 사용하는 공통 클래스가 포함됨.

15.   Spring-expression : 스프링 표현언어(SpEL) 지원 클래스 포함.

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

17.   Spring-test.jar

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

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

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

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

 

 

레벨의 역할(다음레벨 셋팅 및 전달)은 레벨에게 위임

 

 

User의 역할은 User에게 User 레벨셋팅

 

 

JAXB sql 엘리먼트 domain

 

 

Jaxb Sqlmap 엘리먼트 domain

 

JPA, Hibernate, JDBC, JDO 와 같이 변할 수 있으므로 인터페이스

 

JDBC 구현

 

쿼리 DAO와 분리 DBA에 의한 SQL 리뷰나 튜닝 필요시 해당 파일만 전달. SQL 내용을 변경하더라도 어플리케이션 코드나 DI 설정은 전혀 수정할 필요가 없어짐.

독립적인 파일 XML이 편리한 포맷

 

46. 스프링은 프로퍼티 ref 항목에 자기 자신을 넣는 것을 허용한다. 이를 통해 sqlService를 구현한 메소드와 초기화 메소드는 외부에서 DI된 오브젝트라고 생각하고 결국 자신의 메소드에 접근한다. (자기참조빈)

* 자기참조빈

- 사실 흔히 쓰이는 방법은 아니다. 책임이 다르다면 클래스를 구분하고 각기 다른 오브젝트로 만들어지는 것이 자연스럽다. 다만 자기 참조빈을 만들어보는 것은, 책임과 관심사가 복잡하게 얽혀 있어서 확장이 힘들고 변경에 취약한 구조의 클래스를 유연한 구조로 만들려고 할 때 처음 시도해볼 수 있는 방법이다.

- 당장 확장 구조를 이용해 구현을 바꿔 사용하지 않더라도 확장구조를 만들어두는게 좋다고 생각될 때 가장 간단히 접근할 수 있는 방법.

- 실제 스프링이 제공하는 클래스 중 비슷하게 자신의 의존 오브젝트 인터페이스를 스스로 구현하고 자신을 참조하거나 DI하는 코드가 제법 있다.

- 이렇게 책임과 역할을 구분해두고 DI를 통해 연결해줬다면 다음 단계부턴 쉽다.

 

 

 

* XmlSqlService 관심사의 구분

1) SQL정보를 외부의 리소스로부터 읽어오는 것(SqlReader)

2) 읽어온 SQL을 보관해두고 있다가 필요할 때 제공 및 SQL등록/수정(재기동X)(SqlRegistry)

 

당연히 Interface를 이용하여, 기능변경에 자유로울 수 있도록 DI를 통해 제공받아야한다.

* SQL정보를 외부의 리소스로부터 읽어오는 것(SqlReader)

- 읽어오는 SQL 정보는 다시 SqlRegistry에 전달해서 등록되게 해야한다. 리턴타입은 무엇으로 해야할까? SqlReader는 범용적인 인터페이스로 임의의 리소스로부터 가져올 수 있기 때문에  특정 구현에 의존하도록 JAXB로 생성된 Sql 클래스를 사용하는건 곤란하다. 맵을 사용해보자.

 

SqlReader가 리소스로부터 읽어온 SQL 정보를 맵으로 돌려준다고 정의하면 SqlService 구현 클래스에는 위와 같은 코드가 만들어 질 것이다.

* Map 타입으로 형식으로 갖도록 만드는 것은 불편하다.

- JAXB를 사용한 SqlReader라면 SqlmapSql 타입의 오브젝트로 SQL 정보를 읽어오는데 이를 다시 Map으로 옮겨 담아서 리턴해줘야 하기 때문이다.

- Map타입으로 SqlRegistry에 전달했는데 SqlRegistry 내부에서는 Map이 아닌 다른 배열로 저장해두는 방식을 사용한다면? 이렇게 전달 과정 중에 일정한 포맷으로 변환하도록 강제하는 것은 피할 수 없을까?

> SqlService가 일단 SqlReader에게서 정보를 전달받은 뒤, SqlRegistry에 다시 전달해줘야 할 필요는 없다. SqlServiceSqlReader에게 데이터를 달라고 요청하고, 다시 SqlRegistry에게 이 데이터를 사용하라고 하는 것 보다는 SqlReader에게 SqlRegistry 전략을 제공해주면서 이를 이용해 SQL 정보를 SqlRegistry에 저장하라고 요청하는 편이 낫다.

 

 

당연히 Interface를 이용하여, 기능변경에 자유로울 수 있도록 DI를 통해 제공받아야한다.

5. registerSql : SqlRegistry가 구현한 인터에이스에 등록을 위한 메소드 제공해두면 SqlService코드를 통해 특정 포맷으로 변환한 SQL정보를 주고받을 필요 없이 SqlReader가 직접 SqlRegistrySQL 정보를 등록할 수 있다.

7. 검색의 경우 실패시 예외를 던지게 했다. 코드에 버그가 있거나 설정에 문제가 있기 때문에 발생할 수 있으니 복구할 가능성이 적다고 판단해서 런타임 예외로 만들었다. 하지만 여러 개의 레지스트리를 둬서 사용한다면 한 레지스트리에서 검색이 실패할 경우 다른 레지스트리에 검색을 시도할 수 있다. 재시도를 통해 예외를 복구할 수 있는 여지가 있는 셈이다. 따라서 이 메소드를 사용해서 만드는 코드가 그 가능성을 확인할 수 있도록 런타임 예외이지만 명시적으로 메소드가 던지는 예외를 선언해두는 편이 좋다.

 

 

인터페이스가 총 3개 이므로 위와 같이 각 인터페이스를 구현한 클래스 3개를 만들어야한다.

XmlSqlService는 인터페이스만 알면되고 사용하는 오브젝트가 무엇인지는 관심도 없고, 알 필요도 없다. 그저 DI 받은 오브젝트를 사용하면된다. 그렇다면 이 세 개의 인터페이스를 하나의 클래스가 모두 구현한다면??

* 인터페이스 구현은 해당 타입을 상속하는 것

* 하나의 클래스에서 여러 개의 인터페이스를 상속해서 여러 종류의 타입으로서 존재 가능.

 

* loadSql(), getSql() 안에 SqlReader, SqlRegistry 두 가지 전략을 이용하도록 재구성.

* loadSql() 로서는 초기화 작업 때 이런 일을 한다는 걸 보여주는 코드만 있으면 된다. 그 것을 어떻게 할지는 DI받는 SqlRegistrySqlReader 오브젝트의 몫이다.

18~19. XmlSqlServiceSqlService만을 구현한 독립적인 클래스라고 생각했을 때 SqlReaderSqlRegistry 두 개의 인터페이스 타입 오브젝트에 의존하는 구조가 되어야 한다.

29. loadSql을 통해 초기화시 SqlReader를 활용하여 SqlRegisterMap에 저장. 이 때 SqlReader를 독립적인 오브젝트라고 생각하고(SqlRegistry도 동일) 구현 메소드를 사용해야한다. 직접 메소드를 통한 접근은 안된다. 서로 독립되어 의존하는 관계(객체지향)

33. SqlRegistry 오브젝트를 제공해주는건 SqlService

35. SQL Map에 저장은 SqlRegistry. , SqlRegistry는 콜백 오브젝트처럼 사용됨. 동시에 SqlService에게 등록된 SQL을 검색해서 돌려주는 기능을 제공하고 있기도 하므로 SqlService의 의존 오브젝트이기도 하다.

42. sqlMap 오브젝트는 SqlRegistry 타입 오브젝트를 통해서만 사용한다. 물론 XmlSqlService의 일부이므로 직접 접근이 가능하지만 다른 오브젝트라고 생각하고 인터페이스를 통해서만 사용.

43. 기존에는 XmlSqlServiceXML 파일을 읽어오는 방법과 저장 방법을 모두 알고 있었다. 이제는 SqlReader 메소드 뒤로 숨기고, 어떻게 저장해둘지는 SqlRegistry 타입 오브젝트가 알아서 처리하도록 변경

 

 

69. 이렇게 등록을하면 불필요하게 Map등의 특정 오브젝트로 변환할 필요 없이 SqlRegistry에 등록된다. > 자바의 오브젝트는 데이터를 가질 수 있다. 이렇게 자신의 데이터를 이용해 어떻게 작업해야할 지도 자신이 가장 잘 안다. 그러므로 자신의 오브젝트를 외부에 노출시킬 필요는 없는 것이다. SqlRegistry에서 Map으로 작업을 하면 된다.

* SqlReader는 내부에 갖고 있는 SQL 정보를 돌려주는 대신 SQL을 등록하는 SqlRegistry에게 필요에 따라 read 메소드를 통해 읽어 등록해 주면 된다.(자신의 일은 자신이 남의 일은 위임)

* SqlReader의 입장에서는 SqlRegistry 인터페이스를 구현한 오브젝트를 런타임 시에 메소드 파라미터로 제공받아 사용하는 구조이니 일종의 수동 DI라고 볼 수 있다.

 

 

 

 

 

 

 

데코레이터패턴, 프록시, 트랜잭션을 위한 인터페이스.

트랜잭션  경계설정의 일원화(하나로 만듬)

비즈니스 로직을 담고 있는 서비스 계층 오브젝트에 트랜잭션 경계를 부여하기에 가장 적절한 대상이다.

예를 들어 UserService가 아니라면 UserDao를 직접 사용하지 않고, UserService를 사용하는 것이 바람직하다. 단순 조회나 간단한 수정이라면 직접 UserService외의 서비스 계층 오브젝트에서 UserDao를 사용해도 상관없다. 하지만 등록이나 수정, 삭제가 포함된 작업이라면 다른 모듈의 DAO를 직접 이용할 때 신중을 기해야 한다. 안전하게 사용하려면 다른 모듈의 서비스 계층을 통해 접근하는 방법이 좋다.(트랜잭션 모두 취소 혹은 모두 성공)

-  단순히 레코드 개수를 리턴하는 getCount()를 제외하면 나머지는 독자적인 트랜잭션을 가지고 사용될 가능성이 높다. 따라서 이 4개의 메소드를 추가함.

 

 

타겟인터페이스 구현

 

 

메일서버 과부하를 막기위한 테스트 스텁

 

 

DB 접속불가를 위한 목오브젝트를 이용한 테스트

 

 

목 오브젝트 생성과 add 테스트

 

트랜잭션 테스트

 

번거롭게 목, 스텁 오브젝트를 생성하지 않고, 목 프레임웍을 활용한 테스트