행복한 아빠

스프링 XML Configuration을 위한 12가지 최선의 실천사항들 본문

잡다한기록

스프링 XML Configuration을 위한 12가지 최선의 실천사항들

행복한아빠 2010. 3. 11. 14:41

사용자 삽입 이미지
스프링은 Java 애플리케이션의 많은 범위에서 사용하고 있는 강력한 Java 애플리케이션 프레임워크입니다. 이것은 POJO(Plan Old Java Objects)에 엔터프라이즈 서비스의 힘을 제공합니다. 스프링은 간결함과 테스트 용이성을 이루기 위해 의존성 주입(dependency injection)을 이용합니다. 스프링 빈들과 의존성 그리고 빈들이 필요한 서비스들은 configuration 파일들에 설정하는데 일반적으로 XML 형식으로 되어 있습니다. 어쨌거나 이 XML configuration 파일들은 장황하고 다루기 쉽지 않습니다. 큰 프로젝트에서 스프링 빈을 많이 정의할 경우 이 configuration은 읽기도 어렵고 관리하기도 어려워집니다.

이 기사에서 스프링 XML configuration에 대한 12가지 최선의 실천사항들을 소개할 것입니다.  그 중 일부는 최선의 실천사항이라기 보다는 필수 실천사항들입니다. 도메인 모델 설계같은 다른 요소의 영향도 고려해야 하지만 이 기사에서는 configuration의 가독성(readability)과 관리편의성(manageablitity)에 초점을 두었습니다.


1. Autowiring 사용을 자제하라


스프링은 빈의 introspection을 통해 의존관계를 자동으로 묶을(autowirie) 수 있어 명시적으로 빈의 프로퍼티나 생성자 아규먼트를 지정하지 않아도 됩니다. 프로퍼티 이름이나 타입 매칭으로 빈의 프로퍼티를 자동 설정할 수 있습니다. 타입 패칭으로 생성자의 아규먼트를 자동 설정할 수 있습니다. 또한 자동탐지를 autowiring 모드로 지정하여 스프링이 적절한 메커니즘을 선택하게 할 수 있습니다. 다음 예제를 보십시오.

    <bean id="orderService"
        class="com.lizjason.spring.OrderService"
        autowire="byName"/>

컨테이너에서 OrderService 클래스의 프로퍼티 이름으로 빈 인스턴스를 매치하도록 사용하고 있습니다. Autowiring은 어쩌면 타이핑하는 수고를 줄이고 난잡함을 줄일 수 있습니다.  그러나 이것은 configuration의 명확성과 유지보수성을 희생하기에 실제 프로젝트에서 사용하면 안됩니다. 많은 튜토리얼과 프리젠테이션에서 이러한 숨겨진 영향을 언급하지 않고 스프링의 쿨한 특징으로 적극 추천하고 있습니다. 사견으로 스프링의 객체 풀링 같이 이것은 마케팅을 위한 특징에 가깝습니다. 이것은 XML configuration 파일을 작게 만드는 좋은 아이디어 같지만 실제로 복잡함을 증가시킬 것입니다. 특히 많은 빈들이 정의된 대규모 프로젝트에서 더합니다. 스프링에서 autowiring과 명시적인 wiring을 섞어서 쓸 수도 있지만 이러한 일관성 없는 방식은 XML configuration을 더욱 혼란스럽게 만듭니다.


2. 명명규칙을 사용하라


이것은 java 코드에서와 같은 사상입니다. 프로젝트 전반에 거쳐 명확함, 선언적 그리고 일관된 명명규칙을 사용하면 개발자들이 XML configuration을 이해하는데 많은 도움을 줍니다. 빈 ID로 예를 들자면 Java 클래스의 필드 이름 규칙을 사용할 수 있을 것입니다. OrderServiceDAO 인스턴스의 빈 아이디는 orderServiceDAO가 될 것입니다. 대규모 프로젝트에서는 빈 ID의 접두어로 패키지 이름을 추가할 수 있을 겁니다.


3. 간단한 형식을 사용하라

프로퍼티 값이나 레퍼런스를 자식 엘리먼트에서 속성으로 옮기기에 간단한 형식이 덜 장황합니다. 아래 예를 보십시오.

    <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <property name="companyName">
            <value>lizjason</value>
        </property>
        <constructor-arg>
            <ref bean="orderDAO">
        </constructor-arg>
    </bean>

이것은 다음과 같이 간단한 형식으로 다시 작성할 수 있습니다.

    <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <property name="companyName"
            value="lizjason"/>
        <constructor-arg ref="orderDAO"/>
    </bean>

간단한 형식은 1.2 버전부터 사용이 가능합니다. <ref local="...">을 위한 간단한 형식은 없다는 것을 염두해 두십시오.
간단한 형식은 타이핑을 줄이는 것뿐만 아니라 XML configuration 파일을 덜 난잡하게 해 줍니다. Configuration 파일에 많은 빈들을 정의할 경우 눈에 띄게 가독성을 향상시킬겁니다.


4. 생성자 아규먼트 매칭에서 index 보다는 type을 우선 선택하라


스프링은 생성자가 두 개이상의 같은 타입을 가질 때 모호한 문제를 풀기 위해 0부터 시작하는 index를 사용하는 것을 허용합니다. 예를 들면 다음과 같습니다.

    <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <constructor-arg index="0" value="lizjason"/>
        <constructor-arg index="1" value="100"/>
    </bean>

이것은 다음과 같이 type 속성을 사용하는게 더 낫습니다.
 

   <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <constructor-arg type="java.lang.String"
            value="lizjason"/>
        <constructor-arg type="int" value="100"/>
    </bean>

index를 사용하는 것이 어느 정도 더 간결하지만 더 에러가 발생하기 쉽고 type 속성에 비해 읽기도 어렵습니다. 생성자 아규먼트에 모호한 문제가 있을 경우에만 index를 사용하여야 합니다.


5. 가능하다면 빈의 정의를 재사용하라


Configuration 정보의 중복을 줄이고 XML configuration을 간결하게 하기 위해 스프링은 상속과 비슷한 메커니즘을 제공합니다. 자식 빈 정의는 부모 빈으로부터 configuration 정보를 상속받을 수 있습니다. 부모 빈 정의가 자식 빈을 위한 템플릿 역할을 합니다. 대규모 프로젝트에서는 반드시 사용하여할 기능입니다. 해야 할 일은 단지 부모 빈에 abstract=true로 설정하고 자식 빈에서 parent 로 참조하는 것입니다. 예를 들어보면.

    <bean id="abstractService" abstract="true"
        class="com.lizjason.spring.AbstractService">
        <property name="companyName"
            value="lizjason"/>
    </bean>

    <bean id="shippingService"
        parent="abstractService"
        class="com.lizjason.spring.ShippingService">
        <property name="shippedBy" value="lizjason"/>
    </bean>

shippingService 빈은 abstractService 빈으로 부터 lizjason 값을 가진 companyName 속성을 상속받습니다. 만일 빈 정의에 클래스나 팩토리 메소드를 지정하지 않는다면 그 빈은 암시적으로 abstract이 된다는 것을 알아두십시오.


6. 되도록 ApplicationContext에서 import를 통해 빈 정의를 조립하도록 하라


 Ant 스크립트의 import같이 스프링의 import 엘리먼트는 빈 정의를 모듈화하여 조립하는데 유용합니다.
예를 들면:

    <beans>
        <import resource="billingServices.xml"/>
        <import resource="shippingServices.xml"/>
        <bean id="orderService"
            class="com.lizjason.spring.OrderService"/>
    <beans>

그런데, import를 이용하여 XML configuration 안에서 미리 조립하는 대신 ApplicationContext 클래스를 이용하여 구성하는 것이 보다 유연합니다. 또한 ApplicationContext를 이용하면 XML configuration을 관리하기 쉬워집니다. 다음과 같이 빈 정의의 배열을 ApplicationContext 생성자에 전달할 수 있습니다.

    String[] serviceResources =
        {"orderServices.xml",
        "billingServices.xml",
        "shippingServices.xml"};
    ApplicationContext orderServiceContext = new
        ClassPathXmlApplicationContext(serviceResources);


7. 빈 식별자로 id를 사용하라


빈 식별자로 idname을 지정할 수 있습니다. id를 사용하면 가독성을 향상시키지는 않지만 XML 파서가 빈 참조를 검사하는데 도움이 됩니다. XML IDREF 제약때문에 id를 사용할 수 없으면 빈 식별자로 name을 사용할 수 있습니다. XML IDREF 제약의 이슈는 id는 반드시 문자(또는 XML 스펙에 정의된 구두문자 중 하나)로 시작하여 뒤에 문자, 숫자, 하이픈, 밑줄, 콜론 또는 마침표가 옵니다. 사실, XML IDREF 제약의 문제에 빠지는 것은 매우 드문 일입니다.


8. 개발단게에 dependency-check를 이용하라

 빈 정의에 dependency-check 속성을 기본값인 none 이나, simple, object 또는 all로 설정할 수 있습니다. 이는 dependency를 검증해 줍니다. 명시적으로든 자동(autowiring)으로 든  빈의 모든 속성(또는 속성의 어떤 카테고리들)에 반드시 값이 설정되어야 할 때 유용합니다.

    <bean id="orderService"
        class="com.lizjason.spring.OrderService"
        dependency-check="objects">
        <property name="companyName"
            value="lizjason"/>
        <constructor-arg ref="orderDAO"/>
    </bean>

이 예제에서 컨테이너는 기본 타입(primitive)이나 집합 타입이 아닌 orderService 빈의 속성 값은 설정될 것을 보장합니다. 기본으로 모든 빈에 dependency 검사를 하도록 할 수도 있지만, 반드시 설정할 필요가 없는 빈의 속성도 있을 수 있기 때문에 이 기능은 거의 사용하지 않습니다.


9. 각 configuration 파일 헤드에 주석을 달아라


XML configuration 파일 중간에 주석을 다는 것보다는 딱 봐도 알 수 있는(descriptive) id나 이름을 사용하는 것이 더 좋습니다. 거기에다 파일의 헤더에 빈 정의에 대한 요약을 다는 것도 많은 도움이 됩니다.
대신 다음과 같이 description 엘리먼트로 설명을 추가할 수도 있습니다.

    <beans>
        <description>
            This file defines billing service
            related beans and it depends on
            baseServices.xml,which provides
            service bean templates...
        </description>
        ...
    </beans>

이 description 엘리먼트를 사용하면 각종 도구에서 설명을 뽑아내기 쉬운 장점이 있습니다.

10. 변경사항에 대해 팀 동료와 의사소통하라


Java 소스 코드를 리팩토링할 때 관련된 configuration 파일들을 갱신할 필요가 있으면 팀 동료에게 알려야 합니다. XML configuration 역시 코드이며 애플리케이션에서 중요한 요소 중 하나이지만 읽기도 어렵고 유지하기도 어렵습니다. 대부분의 시간동안 뭐가 어떻게 돌아가는지 파악하기 위해 XML configuration과 Java 코드 둘 다 읽어야 할 필요가 있습니다.


11. 생성자를 이용한 주입(injection) 보다는 setter를 이용한 주입을 사용하라

스프링은 생성자 주입, setter 주입 그리고 메소드 주입 세 가지 의존성 주입(dependency injection) 방법을 제공합니다. 전형적으로 앞의 두 가지 방식만 사용합니다.

    <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <constructor-arg ref="orderDAO"/>
    </bean>

    <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <property name="billingDAO"
            ref="billingDAO">
    </bean>

이 예제에서 orderService 빈은 생성자 주입을 사용한 반면 billingService는 setter 주입을 사용합니다.  생성자 주입의 경우 올바르지 않은 상태로는 생성할 수 없도록 보증하지만 setter 주입이 보다 유연하고 관리하기 쉽습니다. 특히 클래스가 여러 개의 속성을 가지고 그 중 몇 개는 선택적일 경우 더욱 그렇습니다.


12. 의존성 주입을 남용하지 말라


 마지막으로, 스프링 ApplicationContext 는 여러분을 위해 Java 객체를 생성해 줍니다. 그러나 모든 Java 객체가 의존성 주입을 통해 생성해야 하는 것은 아닙니다. 그 예로 도메인 객체는 ApplicationContext를 통해 생성하지 말아야 합니다. 스프링은 뛰어난 프레임워크이나 많은 빈들이 정의되면 XML 기반의 configuration이 이슈가 될 수 있으므로 어느 정도 가독성과 관리성을 고려해야 합니다. 의존성 주입을 과용하면 XML configuration은 보다 복잡해지고 비대해질 것입니다. EclipseInteliJ와 같은 강력한 IDE를 사용하면 Java 코드가 XML 파일보다 훨씬 읽거나 유지하거나 관리하기가 쉽다는 것을 기억하십시오!


결말

XML은 일반적인 Spring configuration 포맷입니다. 많은 빈들을 정의할 경우 XML 기반 configuration은 장황해지고 다루기 어려워지기 쉽습니다. 스프링은 풍부한 configuration 옵션들을 지원합니다. 적절히 이 옵션들을 사용하면 XML configuration은 덜 복잡해지기도 하지만 autowiring 같은 다른 옵션들은 가독성이나 관리성을 떨어뜨립니다. 이 기사에서 논의한 좋은 실천사항들을 따른다면 깨끗하고 가독성이 좋은 XML configuration 파일들을 만드는데 도움이 될 것입니다!


참조

Jason Zhicheng Li is a senior software engineer with Object Computing, Inc. in St. Louis, MO.

0 Comments
댓글쓰기 폼