Stay Hungry. Stay Foolish. Don't Be Satisfied.

[Spring 레퍼런스] 부록 C. XML 스키마에 기반을 둔 구성

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.



부록 C. XML 스키마에 기반을 둔 구성

C.1 소개

이번 부록에서는 스프링 2.0에서 도입되고 스프링 2.5와 3.0에서 강화되고 확장된 XML 스키마에 기반을 둔 구성을 살펴본다.

XML 스키마에 기반을 둔 구성파일로 바꾸는 주요 이유는 스프링 XML 구성을 더 쉽게 작성하는 것이다. '고전적인' <bean/>기반의 접근이 좋지만, 그 특성에 따라 구성에 오버헤드가 발생한다.

스프링 IoC 컨테이너의 관점에서는 모든 것이 빈이다. 모든 것이 빈이라면 같은 방식으로 이를 모두 다룰 수 있으므로 스프링 IoC 컨테이너의 입장에서는 좋은 점이지만 개발자 입장서는 그다지 좋지 않다. 스프링 XML 구성파일에서 정의한 객체는 모두 일반적이고 평범한(generic, vanilla) 빈이 아니다. 보통 각 빈은 어느 정도의 특별한 구성이 필요하다.

스프링 2.0에서 스키마에 기반을 둔 새로운 구성은 이 문제를 해결한다. <bean/> 요소는 계속 사용할 수 있고 원한다면 <bean/> 요소만 사용하면서 완전히 같은 방식의 스프링 XML 구성을 작성할 수 있다. 하지만 새로운 XML 스키마 기반의 구성은 스프링 XML 구성파일을 상당히 읽기 쉽게 만들 수 있고 빈 정의의 의도를 표현할 수 있다.

기존에 존재하는 빈(bean) 태그가 DAO, 서비스계층 객체, 밸리데이터 등의 애플리케이션에 특화된 빈에 적합하다면 새로운 커스텀 태그는 인프라스트럭처나 통합 빈(예를 들어 AOP, 컬렉션, 트랜잭션이나 Mule 등의 서드파티 프레임워크와의 통합)에 사용하기에 최적이라는 점은 꼭 기억해 두어야 한다.

다음의 예시로 스프링 2.0의 XML 스키마 지원이 추가된 것은 좋은 생각이었다는 점을 알 수 있기를 바란다. 개발자들이 이를 도입하기를 권장하고 이 새로운 구성 메커니즘이 완전히 커스터마이징할 수 있고 확장 가능하다는 점을 명심해라. 즉, 애플리케이션의 도메인을 더 잘 나타낼 수 있도록 자신의 도메인에 특화된 구성 태그를 작성할 수 있다. 자신만의 구성태그를 작성하는 방법은 부록 Appendix D, Extensible XML authoring에서 다룬다.

DTD 지원?
지금도 예전의 DTD 방식을 사용하는 스프링 구성 파일을 완전히 지원한다.

스프링 XML 구성파일을 작성할 때 새로운 XML 스키마 방식의 접근을 사용하더라도 문제는 전혀 없고 더 간결하고 깔끔한 구성을 사용할 기회를 놓칠 뿐이다. XML 구성이 DTD 기반이냐 스키마 기반이냐에 상관없이 결국은 컨테이너에서 같은 객체 모델이 된다.(보통 하나 이상의 BeanDefinition 인스턴스)


C.2 XML 스키마에 기반을 둔 구성

C.2.1 스키마 참조

기존의 DTD 방식에서 새로운 XML 스키마 방식으로 변경하려면 다음과 같이 바꾸어야 한다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
  <!-- <bean/> 정의는 여기에 작성한다 -->
</beans>

XML 스키마 방식에서 같은 파일은 다음과 같이 작성한다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
  <!-- <bean/> 정의는 여기에 작성한다 -->
</beans>


Note
'xsi:schemaLocation' 부분은 실제로는 필요 없지만 스키마의 로컬 복사본을 참조하려고(개발단계에서 유용할 수 있다.) 포함할 수 있다.


위의 스프링 XML 구성의 일부분은 복사 후 붙여넣기를 해서 사용할 수 있는 상용구문으로 항상 하듯이 <bean/>를 추가해서 사용할 수 있다. 하지만 XML 스키마 방식으로 바꾸는 것은 구성을 더 쉽게 만드는 새로운 스프링 2.0 XML 태그의 장점을 얻으려는 것이다. Section C.2.2, “util 스키마” 섹션에서 일반적인 유틸리티 태그를 사용해서 바로 시작하는 방법을 설명한다.

이번 장의 나머지 부분에서는 새로운 스프링 XML 스키마에 기반을 둔 구성의 예제를 보여준다. (새로운 태그마다 최소한 하나의 예시를 제공한다.) 형식은 before, after 형식을 사용해서 before 부분에서는 이전 방식의 XML을 보여주고(하지만 지금도 완전히 사용 가능한) 바로 이어서 나오는 after 부분에서는 같은 설정을 XML 스키마 방식을 보여준다.

C.2.2 util 스키마

처음 다룰 부분은 util 태그의 범위인데 이름에서 알 수 있듯이 util 태그는 컬렉션을 구성하거나 상수를 참조하는 등의 일반적인 유틸리티성 구성 이슈를 다룬다.

util 스키마의 태그를 사용하려면 스프링 XML 구성파일 상단에 다음 코드를 넣어야 한다. util 네임스페이스로 태그를 사용할 수 있도록 다음 예시의 텍스트는 올바른 스키마를 참조하고 있다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!-- 여기에 <bean/> 정의를 작성한다 -->
</beans>


C.2.2.1 <util:constant/>

Before...

<bean id="..." class="...">
  <property name="isolation">
    <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
    class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
  </property>
</bean>

위 구성은 빈의 'isolation' 프로퍼티 값을 'java.sql.Connection.TRANSACTION_SERIALIZABLE' 상수 값으로 설정하려고 스프링의 FactoryBean 구현체인 FieldRetrievingFactoryBean를 사용한다. 이 구성은 문제없이 잘 동작하지만 약간 장황하고 (불필요하게) 스프링의 내부 구조를 최종 사용자에게 노출한다.

다음 XML 스키마 방식은 더 간단하면서도 개발자의 의도('이 상수값을 주입해라')를 명확하게 드러내서 더 읽기가 좋다.

<bean id="..." class="...">
  <property name="isolation">
    <util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
  </property>
</bean>

필드 값으로 빈(bean) 프로퍼티나 생성자 인자 설정하기
FieldRetrievingFactoryBean은 static 필드 값이나 정적이 아닌 필드 값을 가져오는 FactoryBean이다. 이는 보통 public static final 상수를 가져오는 데 사용한다.(가져와서 또 다른 빈에 프로퍼티 값이나 생성자 인자를 설정하는 데 사용할 수 있다.)

staticField를 사용해서 static 필드를 노출하는 방법을 다음 예제에서 보여준다.

<bean id="myField" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
  <property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</bean>

빈 이름으로 static 필드를 지정하는 것도 편리한 사용방식이다.

<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
    class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>

이는 빈 id에 대한 선택권이 없다는 것을 의미하지만(그래서 이를 참조하는 다른 모든 빈도 이 긴 이름을 사용해야 한다) 이 형식은 매우 간단하게 정의할 수 있고 빈 참조에 id를 지정하지 않아도 되므로 이너 빈으로 사용할 때 아주 편리하다.

<bean id="..." class="...">
  <property name="isolation">
    <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
      class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
  </property>
</bean>

FieldRetrievingFactoryBean 클래스의 API 문서에 나와 있듯이 다른 빈의 비정적(인스턴스) 필드에 접근하는 것도 가능하다. class.

스프링에서 빈에 프로프니나 생성사 인자로 enum 값을 주입하기는 아주 쉬워서 실제로 아무것도 안해도 되고 스프링 내부에 대해( FieldRetrievingFactoryBean같은 클래스) 알아야 하는 것도 없다. enum 값을 주입하기가 얼마나 쉬운지 예제를 통해서 보자. 다음과 같은 JDK 6 enum을 생각해 보자.

package javax.persistence;

public enum PersistenceContextType {
    TRANSACTION,
    EXTENDED
}

PersistenceContextType타입의 setter를 보자.

package example;

public class Client {
  private PersistenceContextType persistenceContextType;
  public void setPersistenceContextType(PersistenceContextType type) {
    this.persistenceContextType = type;
  }
}

이에 대한 빈 정의는 다음과 같다.

<bean class="example.Client">
  <property name="persistenceContextType" value="TRANSACTION" />
</bean>

이는 (JDK 1.4와 JDK 1.3의) 전통적이면서 타입 세이프 하게 만들어진 enum에서도 잘 동작한다. 스프링은 enum 클래스의 상수와 일치하는 문자열 프로퍼티 값을 자동으로 찾을 것이다.

C.2.2.2 <util:property-path/>

Before...


<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
  <property name="age" value="10"/>
  <property name="spouse">
    <bean class="org.springframework.beans.TestBean">
      <property name="age" value="11"/>
    </bean>
  </property>
</bean>


<bean id="testBean.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>

위 구성은 스프링의 FactoryBean 구현체 PropertyPathFactoryBean을 사용해서 'testBean' 빈의 'age' 프로퍼티와 같은 값을 가진 (int 타입의) 'testBean.age'라는 빈을 생성한다.

After...


<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
  <property name="age" value="10"/>
  <property name="spouse">
    <bean class="org.springframework.beans.TestBean">
      <property name="age" value="11"/>
    </bean>
  </property>
</bean>


<util:property-path id="name" path="testBean.age"/>

<property-path/> 태그의 'path' 속성의 값은 'beanName.beanProperty' 형식을 따른다.

빈 프로퍼티나 생성자 인자를 설정할 때 <util:property-path/> 사용하기

PropertyPathFactoryBean는 FactoryBean이고 주어진 대상 객체의 프로퍼티 경로를 평가한다. 대상 객체는 직접 지정할 수도 있고 빈 이름으로 지정할 수도 있다. 지정한 값은 다른 빈 정의에서 프로퍼티 값이나 생성자 인자로 사용할 수 있다.

다음은 name으로 다른 빈을 기준으로 path를 사용하는 예제이다.

// 대상 빈은 name으로 참조된다
<bean id="person" class="org.springframework.beans.TestBean" scope="prototype">
  <property name="age" value="10"/>
  <property name="spouse">
    <bean class="org.springframework.beans.TestBean">
      <property name="age" value="11"/>
    </bean>
  </property>
</bean>

// 이는 'person' 빈의 'spouse.age' 프로퍼티의 값인 11이 된다
<bean id="theAge"
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
  <property name="targetBeanName" value="person"/>
  <property name="propertyPath" value="spouse.age"/>
</bean>

이 예제에서 path는 내부 빈을 기준으로 평가된다.

<!-- 이는 내부 빈의 'age' 프로퍼티의 값인 12가 된다 -->
<bean id="theAge" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
  <property name="targetObject">
    <bean class="org.springframework.beans.TestBean">
      <property name="age" value="12"/>
    </bean>
  </property>
  <property name="propertyPath" value="age"/>
</bean>

빈 네임이 프로퍼티 경로인 단축형태도 존재한다.

<!-- 이는 'person' 빈의 'age' 프로퍼티의 값인 10이 된다 -->
<bean id="person.age"
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>

이 형식에서는 빈 이름을 선택할 수 없어서 이에 대한 모든 참조도 같은 id(경로)를 사용해야 한다. 물론 내부 빈으로 사용한다면 참조할 필요조차 없다.

<bean id="..." class="...">
  <property name="age">
    <bean id="person.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
  </property>
</bean>

실제 정의에서 결과 타입을 구체적으로 지정할 수도 있다. 대부분 경우 이는 필요 없지만 일부의 경우에는 사용할 수 있다. 이 기능에 대한 자세한 내용은 Javadoc을 참조해라.

C.2.2.3 <util:properties/>

Before..

<!-- 제공한 위치에서 로드한 값으로 java.util.Properties 인스턴스를 생성한다 -->
<bean id="jdbcConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="location" value="classpath:com/foo/jdbc-production.properties"/>
</bean>

위 구성은 스프링의 FactoryBean 구현체인 PropertiesFactoryBean를 사용해서 제공된 Resource 위치에서 로드한 값으로 java.util.Properties 인스턴스를 인스턴스화 한다.

After...

<!-- 제공한 위치에서 로드한 값으로 java.util.Properties 인스턴스를 생성한다 -->
<util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/>


C.2.2.4 <util:list/>

Before...

<!-- 제공한 'sourceList'에서 로드한 값으로 java.util.List 인스턴스를 생성한다 -->
<bean id="emails" class="org.springframework.beans.factory.config.ListFactoryBean">
  <property name="sourceList">
    <list>
      <value>pechorin@hero.org</value>
      <value>raskolnikov@slums.org</value>
      <value>stavrogin@gov.org</value>
      <value>porfiry@gov.org</value>
    </list>
  </property>
</bean>

위 구성은 스프링의 FactoryBean 구현체 ListFactoryBean을 사용해서 제공한 'sourceList'에서 가져온 값으로 초기화한 java.util.List 인스턴스를 생성한다.

After...

<!-- 제공한 값으로 java.util.List 인스턴스를 생성한다 -->
<util:list id="emails">
  <value>pechorin@hero.org</value>
  <value>raskolnikov@slums.org</value>
  <value>stavrogin@gov.org</value>
  <value>porfiry@gov.org</value>
</util:list>

명시적으로 정확히 List 타입을 제어할 수도 있는데 이는 <util:list/> 요소의 'list-class' 속성을 사용해서 인스턴스화한다. 예를 들어 java.util.LinkedList를 인스턴스화 하기를 원한다면 다음의 구성을 사용할 수 있다.

<util:list id="emails" list-class="java.util.LinkedList">
  <value>jackshaftoe@vagabond.org</value>
  <value>eliza@thinkingmanscrumpet.org</value>
  <value>vanhoek@pirate.org</value>
  <value>d'Arcachon@nemesis.org</value>
</util:list>

'list-class' 속성을 지정하지 않으면 컨테이너가 List 구현체를 선택할 것이다.

C.2.2.5 <util:map/>

Before...

<!-- 제공한 'sourceMap'에서 로드한 값으로 java.util.Map 인스턴스를 생성한다 -->
<bean id="emails" class="org.springframework.beans.factory.config.MapFactoryBean">
  <property name="sourceMap">
    <map>
      <entry key="pechorin" value="pechorin@hero.org"/>
      <entry key="raskolnikov" value="raskolnikov@slums.org"/>
      <entry key="stavrogin" value="stavrogin@gov.org"/>
      <entry key="porfiry" value="porfiry@gov.org"/>
    </map>
  </property>
</bean>

위 구성은 스프링의 FactoryBean 구현체 MapFactoryBean를 사용해서 제공한 'sourceMap'에서 가져온 키-값 쌍으로 초기화한 java.util.Map 인스턴스를 생성한다.

After...

<!-- 제공한 키-값 쌍으로 java.util.Map 인스턴스를 생성한다 -->
<util:map id="emails">
  <entry key="pechorin" value="pechorin@hero.org"/>
  <entry key="raskolnikov" value="raskolnikov@slums.org"/>
  <entry key="stavrogin" value="stavrogin@gov.org"/>
  <entry key="porfiry" value="porfiry@gov.org"/>
</util:map>

명시적으로 정확히 Map 타입을 제어할 수도 있는데 이는 <util:map/> 요소의 'map-class' 속성을 사용해서 인스턴스화 한다. 예를 들어 java.util.TreeMap를 인스턴스화 하기를 원한다면 다음 구성을 사용할 수 있다.

<util:map id="emails" map-class="java.util.TreeMap">
  <entry key="pechorin" value="pechorin@hero.org"/>
  <entry key="raskolnikov" value="raskolnikov@slums.org"/>
  <entry key="stavrogin" value="stavrogin@gov.org"/>
  <entry key="porfiry" value="porfiry@gov.org"/>
</util:map>

'map-class' 속성을 지정하지 않는다면 컨테이너가 Map 구현체를 선택할 것이다.

C.2.2.6 <util:set/>

Before...

<!-- 제공한 'sourceSet'에서 로드한 값으로 java.util.Set 인스턴스를 생성한다 -->
<bean id="emails" class="org.springframework.beans.factory.config.SetFactoryBean">
  <property name="sourceSet">
    <set>
      <value>pechorin@hero.org</value>
      <value>raskolnikov@slums.org</value>
      <value>stavrogin@gov.org</value>
      <value>porfiry@gov.org</value>
    </set>
  </property>
</bean>

위 구성은 스프링의 FactoryBean 구현체 SetFactoryBean를 사용해서 제공된 'sourceSet'에서 가져온 값으로 초기화한 java.util.Set 인스턴스를 생성한다.

After...

<!-- 제공한 값으로 java.util.Set 인스턴스를 생성한다 -->
<util:set id="emails">
  <value>pechorin@hero.org</value>
  <value>raskolnikov@slums.org</value>
  <value>stavrogin@gov.org</value>
  <value>porfiry@gov.org</value>
</util:set>

명시적으로 정확히 Set 타입을 제어할 수도 있는데 이는 <util:set/> 요소의 'set-class' 속성을 사용해서 인스턴스화한다. 예를 들어 java.util.TreeSet를 인스턴스화 하기 원한다면 다음 구성을 사용할 수 있다.

<util:set id="emails" set-class="java.util.TreeSet">
  <value>pechorin@hero.org</value>
  <value>raskolnikov@slums.org</value>
  <value>stavrogin@gov.org</value>
  <value>porfiry@gov.org</value>
</util:set>

'set-class' 속성을 지정하지 않으면 컨테이너가 Set 구현체를 선택할 것이다.

C.2.3 jee 스키마

jee 태그는 JNDI 객체를 찾고 EJB 참조를 정의하는 등의 Java EE (Java Enterprise Edition)와 관련된 구성 이슈를 다룬다.

jee 스키마의 태그를 사용하려면 스프링 XML 구성 파일 상단에 다음 부분을 추가해야 한다. 다음 예시의 내용은 올바른 스키마를 참조해서 jee 네임스페이스의 태그를 사용할 수 있다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
<!-- 여기에 <bean/> 정의를 작성한다 -->
</beans>


C.2.3.1 <jee:jndi-lookup/> (간단버전)

Before...

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/MyDataSource"/>
</bean>

<bean id="userDao" class="com.foo.JdbcUserDao">
  
  <property name="dataSource" ref="dataSource"/>
</bean>

After...

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource"/>

<bean id="userDao" class="com.foo.JdbcUserDao">
  
  <property name="dataSource" ref="dataSource"/>
</bean>


C.2.3.2 <jee:jndi-lookup/> (단일 JNDI 환경설정을 이용)

Before...

<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/MyDataSource"/>
  <property name="jndiEnvironment">
    <props>
      <prop key="foo">bar</prop>
    </props>
  </property>
</bean>

After...

<jee:jndi-lookup id="simple" jndi-name="jdbc/MyDataSource">
  <jee:environment>foo=bar</jee:environment>
</jee:jndi-lookup>


C.2.3.3 <jee:jndi-lookup/> (다중 JNDI 환경 설정을 이용)

Before...

<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/MyDataSource"/>
  <property name="jndiEnvironment">
    <props>
      <prop key="foo">bar</prop>
      <prop key="ping">pong</prop>
    </props>
  </property>
</bean>

After...

<jee:jndi-lookup id="simple" jndi-name="jdbc/MyDataSource">
  <!-- 라인별로 구분되는 환경의 키-값 쌍 (표준 Properties 형식) -->
  <jee:environment>
    foo=bar
    ping=pong
  </jee:environment>
</jee:jndi-lookup>


C.2.3.4 <jee:jndi-lookup/> (복합 버전)

Before...

<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/MyDataSource"/>
  <property name="cache" value="true"/>
  <property name="resourceRef" value="true"/>
  <property name="lookupOnStartup" value="false"/>
  <property name="expectedType" value="com.myapp.DefaultFoo"/>
  <property name="proxyInterface" value="com.myapp.Foo"/>
</bean>

After...

<jee:jndi-lookup id="simple"
    jndi-name="jdbc/MyDataSource"
    cache="true"
    resource-ref="true"
    lookup-on-startup="false"
    expected-type="com.myapp.DefaultFoo"
    proxy-interface="com.myapp.Foo"/>


C.2.3.5 <jee:local-slsb/> (간단 버전)

<jee:local-slsb/> 태그는 EJB Stateless SessionBean애 대한 참조를 구성한다.

Before...

<bean id="simple"
      class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
  <property name="jndiName" value="ejb/RentalServiceBean"/>
  <property name="businessInterface" value="com.foo.service.RentalService"/>
</bean>

After...

<jee:local-slsb id="simpleSlsb" jndi-name="ejb/RentalServiceBean"
    business-interface="com.foo.service.RentalService"/>


C.2.3.6 <jee:local-slsb/> (복합 버전)b

<bean id="complexLocalEjb"
      class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
  <property name="jndiName" value="ejb/RentalServiceBean"/>
  <property name="businessInterface" value="com.foo.service.RentalService"/>
  <property name="cacheHome" value="true"/>
  <property name="lookupHomeOnStartup" value="true"/>
  <property name="resourceRef" value="true"/>
</bean>

After...

<jee:local-slsb id="complexLocalEjb"
    jndi-name="ejb/RentalServiceBean"
    business-interface="com.foo.service.RentalService"
    cache-home="true"
    lookup-home-on-startup="true"
    resource-ref="true">


C.2.3.7 <jee:remote-slsb/>

<jee:remote-slsb/> 태그는 remote EJB Stateless SessionBean에 대한 참조를 구성한다.

Before...

<bean id="complexRemoteEjb"
      class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean">
  <property name="jndiName" value="ejb/MyRemoteBean"/>
  <property name="businessInterface" value="com.foo.service.RentalService"/>
  <property name="cacheHome" value="true"/>
  <property name="lookupHomeOnStartup" value="true"/>
  <property name="resourceRef" value="true"/>
  <property name="homeInterface" value="com.foo.service.RentalService"/>
  <property name="refreshHomeOnConnectFailure" value="true"/>
</bean>

After...

<jee:remote-slsb id="complexRemoteEjb"
    jndi-name="ejb/MyRemoteBean"
    business-interface="com.foo.service.RentalService"
    cache-home="true"
    lookup-home-on-startup="true"
    resource-ref="true"
    home-interface="com.foo.service.RentalService"
    refresh-home-on-connect-failure="true">


C.2.4 lang 스키마

lang 태그는 스프링 컨테이너의 빈을 JRuby나 Groovy 같은 동적 언어로 작성한 경우 노출 객체를 다룬다.

이러한 태그(와 동적 언어 지원)은 Chapter 27, 동적 언어 지원 챕터에서 자세하게 다루었다. 동적 언어 지원과 lang 태그에 대한 자세한 내용은 이 챕터를 참고해라.

lang 스키마의 태그를 사용하려면 스프링 XML 구성파일의 상단에 다음 코드를 넣어야 한다. 다음 예시의 코드는 올바른 스키마를 참조해서 lang 네임스페이스의 태그를 사용할 수 있게 한다.


<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:lang="http://www.springframework.org/schema/lang"
     xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd">


</beans>


C.2.5 jms 스키마

jms 태그는 스프링의 MessageListenerContainers처럼 JMS와 관련된 빈의 구성을 다룬다. 이러한 태그는 Section 22.6, “JMS 네임스페이스 지원”라는 제목의 JMS 챕터에서 자세히 설명했다. JMS 지원과 jms 태그에 대한 자세한 내용은 해당 챕터를 참고하기 바란다.

jms 스키마의 태그를 사용하려면 스프링 XML 구성파일 상단에 다음 코드를 넣어야 한다. 다음 예시의 코드는 올바른 스키마를 참조해서 jms 네임스페이스의 태그를 사용할 수 있게 한다.


<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:jms="http://www.springframework.org/schema/jms"
     xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">


</beans>


C.2.6 tx (트랜잭션) 스키마

tx 태그는 스프링의 광범위한 트랜잭션 지원의 모든 빈의 구성을 다룬다. 이 태그는 Chapter 11, 트랜잭션 관리 장에서 다루었다.

Tip
스프링 배포판에 포함된 'spring-tx-3.0.xsd' 파일을 보기를 권장한다. (물론) 이 파일은 스프링 트랜잭션 구성의 XML 스키마로 기본 속성 등을 포함해서 tx 네임스페이스의 다양한 태그를 모두 다룬다. 이 파일은 문서에 포함되어 있으므로 DRY (Don't Repeat Yourself) 원리를 지키기 위해 여기서 다시 얘기하지는 않는다.


tx 스키마의 태그를 사용하려면 스프링 XML 구성파일 상단에 다른 코드를 넣어야 한다. tx 네임스페이스를 사용할 수 있도록 다음 예시의 텍스트는 올바른 스키마를 참조하고 있다.


<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


</beans>
Note
tx 네임스페이스로 태그를 사용하는 경우 aop 네임스페이스의 태그도 사용할 수 있다. (스프링의 선언적인 트랜잭션 지원이 AOP로 구현되었으므로) 위 XML에 aop 스키마를 참조하기에 필요한 코드가 포함되어 있으므로 aop 네임스페이스의 태그를 사용할 수 있다.


C.2.7 aop 스키마

aop 태그는 스프링의 AOP와 관련된 모든 것을 다룬다. 이 태그는 스프링의 프락시 기반 AOP 프레임워크와 AspectJ AOP 프레임워크와의 스프링 통합을 포함한다. 이 태그는 Chapter 8, 스프링의 관점 지향 프로그래밍 장에서 자세히 다루었다.

aop 스키마의 태그를 사용하려면 스프링 XML 구성 파일 상단에 다음 코드를 넣어야 한다. 다음 코드에서 올바른 스키마를 참조하고 있으므로 aop 네임스페이스의 태그를 사용할 수 있다.


<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


</beans>


C.2.8 context 스키마

context 태그는 최종 사용자에게 중요한 빈이 아니라 일반적인 빈이 아니라 BeanfactoryPostProcessors 처럼 스프링에서 동작에 문제가 있는 빈과 관련된 plumbing과 관련된 ApplicationContext 구성을 다룬다. 다음 코드가 올바른 스키마를 참조하므로 context 네임스페이스의 태그를 사용할 수 있다.


<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">


</beans>
Note
context 스키마는 스프링 2.5에서만 도입되었다.


C.2.8.1 <property-placeholder/>

이 요소는 ${...} 플레이스홀더를 지정한 프로퍼티 파일(Spring resource location처럼)로 처리해서 교체하게 한다. 이 요소는 PropertyPlaceholderConfigurer를 설정하는 편리한 방법이다. PropertyPlaceholderConfigurer를 더 세세하게 제어해야 한다면 명시적으로 직접 정의해라.

C.2.8.2 <annotation-config/>

(사용할 수 있는 경우) JSR 250의 @PostConstruct, @PreDestroy, @Resource와 (사용할 수 있는 경우) JPA의 @PersistenceContext, @PersistenceUnit와 마찬가지로 스프링의 @Required와 @Autowired같은 빈 클래스가 탐지한 다양한 어노테이션을 스프링 인프라가 활성화한다. 아니면 사용할 어노테이션에 대한 개별 BeanPostProcessors를 명시적으로 활성화할 수 있다.

Note
이 요소는 스프링의 @Transactional 어노테이션을 활성화 하지 않는다. @Transactional 어노테이션을 활성화하려면 요소를 사용해라.


C.2.8.3 <component-scan/>

이 요소는 Section 4.9, “어노테이션기반의 컨테이너 설정”에 잘 나와 있다.

C.2.8.4 <load-time-weaver/>

이 요소는 Section 8.8.4, “스프링 프레임워크에서 AspectJ를 사용한 로드타임 위빙(Load-time weaving)”에 잘 나와 있다.

C.2.8.5 <spring-configured/>

이 요소는 Section 8.8.1, “스프링에 도메인 객체를 의존성 주입시 AspectJ 사용하기”에 잘 나와 있다.

C.2.8.6 <mbean-export/>

이 요소는 Section 23.4.3, “<context:mbean-export/> 요소”에 잘 나와 있다.

C.2.9 tool 스키마

tool 태그는 커스텀 설정 요소에 대한 도구에 국한된 메타데이터를 추가하고자 할 때 사용한다. 그 후 이 메타데이터를 이해할 수 있는 도구가 이 데이터를 사용해서 원하는 무엇이든 할 수 있다.(유효성 검사 등)

tool 태그는 현재 리뷰를 진행하고 있으므로 이번 스프링 릴리즈에는 문서로 만들어 져 있지 않다. 당신이 서드파티 벤더라서 이 리뷰 과정에 참여하고 싶다면 스프링 메일링 리스트에 메일을 보내라. 현재 지원하는 tool 태그는 스프링 소스 배포판의 'src/org/springframework/beans/factory/xml' 디렉토리에서 'spring-tool-3.0.xsd' 파일에 나와 있다.

C.2.10 beans 스키마

마지막으로 얘기하지만 무시할 수 없는 태그가 beans 스키마의 태그이다. 스프링 프레임워크의 아주 초창기부터 같은 태그가 존재하고 있다. beans 스키마의 태그는 Section 4.4.2, “의존성과 설정에 대한 자세한 내용”에서 아주 광범위하게 다루었으므로(전체 장에 걸쳐서 다루었다.) 여기서 beans 스키마의 다양한 태그의 예시를 보여주진 않는다.

스프링 2.0에서 beans 태그 자체에 새로 추가된 것 중 하나는 임의의 빈 메타데이터에 대한 아이디어이다. 스프링 2.0에서는 <bean/> XML 정의에 다수의 키/값 쌍을 추가하거나 추가하지 않을 수 있다. 이 여분의 메타데이터로 할 수 있는 일은 완전히 작성한 커스텀 로직에 달려 있다.(그리고 Appendix D, Extensible XML authoring 부록에서 설명한 대로 보통 커스텀 태그를 작성한 경우에만 사용한다.

상위의 <bean/> 컨텍스트에서 <meta/> 태그를 사용한 예제가 아래 나와 있다.(메타데이터를 해석하는 로직 없이는 아무런 효과가 없다는 점을 명심해라.)


<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="foo" class="x.y.Foo">
    <meta key="cacheName" value="foo"/>
    <property name="name" value="Rick"/>
  </bean>
</beans>

위 예제에서 빈 정의를 사용하고 제공한 메타데이터로 캐시 인프라스트럭처를 구성할 어떤 로직이 있다고 가정할 수 있다.

2015/05/25 04:27 2015/05/25 04:27

Github LFS 테스트

지난 4월 초에 Github에서 Large File Storage (LFS)발표했다. 형상관리도구에서 바이너리 파일을 다루는 게 편하지는 않지만, 저장소가 프로젝트의 소스를 다 포함하고 있는 경우가 많으므로 이미지 파일 등을 포함하는 경우가 많다. 간단한 이미지 파일은 큰 문제가 안 되더라도 원본 소스가 되는 PSD 파일이나 오디오 파일 등의 대용량 파일은 저장소 더욱 다루기가 어렵다. 현재는 베타상태라 early access를 신청해야 사용할 수 있는데 초기에 신청했다가 얼마 전에 사용할 수 있게 되었다.

LFS의 동작 흐름도


설치

LFS를 사용하려면 클라이언트를 먼저 설치해야 하는데 LFS 페이지에서 다운로드 받을 수 있다. LFS는 Windows, Linux, Mac을 모두 지원하고 있고 현재 버전은 v0.5.1이다. Mac의 경우 다운로드받으면 다음과 같이 2개의 파일이 들어 있다.

├── git-lfs
└── install.sh

install.sh을 실행하면 자동으로 /usr/local아래 설치를 하고 아니면 git-lfs파일을 $PATH아래에 복사하면 된다.

설치가 정상적으로 되면 커맨드라인에서 git lfs 명령어를 사용할 수 있다.

$ git lfs
git-lfs/0.5.1 (GitHub; darwin amd64; git 2.2.1; go 1.3)

Usage:
  git-lfs [flags]
  git-lfs [command]

Available Commands:
  clean                     Implements the Git clean filter
  env                       Show the current environment
  init                      Initialize the default Git LFS configuration
  logs                      View error logs
  ls-files                  Show information about Git LFS files
  pointer                   Build and compare pointers between different Git LFS implementations
  push                      Push files to the Git LFS server
  smudge                    Implements the Git smudge filter
  status                    Show information about Git LFS objects that would be pushed
  track                     Manipulate .gitattributes
  untrack                   Remove an entry from .gitattributes
  update                    Update local Git LFS configuration
  version                   Show the version number
  pre-push                  Implements the Git pre-push hook
  help [command]            Help about any command

 Available Flags:
      --help=false: help for git-lfs

Use "git-lfs help [command]" for more information about that command.

install.sh을 사용하지 않고 git-lfs를 복사했다면 다음과 같이 초기화를 한번 해주어야 한다.

$ git lfs init
git lfs initialized


저장소에서 LFS 사용하기

아직 프로젝트에서 사용하는 건 아니지만, 사용방법을 알기 위해서 간단히 테스트를 해보았다.(그냥 저장소를 만들어서 무료 PSD 파일을 추가한 정도이다.)

사용하는 저장소에서 LFS에 저장할 파일을 다음과 같이 지정하면 알아서 LFS를 사용하게 된다. 그래서 사용하는 대용량 파일을 따로 설정하면 실제 Git을 사용할 때 별도의 과정은 필요 없다.

$ git lfs track "*.psd"
Tracking *.psd

트래킹을 추가하면 .gitattributes파일에 다음과 같은 내용이 추가되고 이 파일을 LFS가 처리하게 된다.

*.psd filter=lfs diff=lfs merge=lfs -crlf

이제 139MB짜리 PSD 파일을 저장소에 커밋하고 Github에 푸시를 했다. 다음에서 보듯이 파일을 git에 커밋할 때 달라지는 점은 전혀 없고 사용할 때는 딱히 LFS를 사용한다는 느낌도 전혀 없다.(큰 파일을 많이 다뤄보진 않아서 속도가 크게 다른지도 잘 모르겠다.)

$ git add psd/red-website.psd
$ git commit
[master dfebc6d] add psd design file
 2 files changed, 4 insertions(+)
 create mode 100644 .gitattributes
 create mode 100755 psd/red-website.psd
$ git push origin  master
(1 of 1 files) 139.15 MB / 139.15 MB  100.00 % 7m16s
Counting objects: 8, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (8/8), 716 bytes | 0 bytes/s, done.
Total 8 (delta 0), reused 0 (delta 0)
To git@github.com:outsideris/lfs-test.git
 * [new branch]      master -> master

Github에 표시된 psd 파일

Github 저장소에서도 LFS에서 파일을 다루고 있다는 표시는 전혀 볼 수가 없다.(아직 공식 오픈 전이라 그럴 수도...)

$ git clone git@github.com:outsideris/lfs-test.git
Cloning into 'lfs-test'...
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 8 (delta 0), reused 8 (delta 0), pack-reused 0
Receiving objects: 100% (8/8), done.
Checking connectivity... done.
Downloading psd/red-website.psd (139.15 MB)

이 프로젝트를 따로 클론 받아보면 psd/red-website.psd파일을 따로 받는 과정이 클론 중에 발생하는 것을 볼 수가 있다. 참고로 이번에 LFS를 테스트하면서 알게 되었는데 Github는 50MB가 넘는 파일은 경고하고 100MB가 넘는 파일은 올릴 수 없게 막고 있다. 그래서 100MB가 넘는 파일을 관리하고 싶다면 LFS를 사용해야 한다. 아직 공식 오픈 전이라서 그런지 LFS에 올린 파일이나 용량을 따로 확인하거나 관리하는 메뉴는 찾지 못했다.

Github의 LFS 가격 정책

Github의 가격정책 페이지에도 LFS는 Coming soon으로 표시되고 있다. 스토리지가 1GB까지 월간 트래픽 1기가까지는 무료고 그 이상 사용하려면 월간 5달러를 지불해야 한다. 개인적으로 Github를 쓰면서 대용량 파일을 쓸 일도 거의 없었지만, 그 때문에 불편함을 느낀 적도 없어서 아직 필요성을 크게 느끼진 못하는데 회사 프로젝트 등에서는 PSD파일 등은 버전 관리를 하기가 어려우므로 이런 파일이 많다면 사용해 볼 만해 보인다.(물론 디자이너가 Git을 써야 하는 건 다른 문제다.)

2015/05/24 22:14 2015/05/24 22:14