Spring MVC

springmvc.egloos.com


포토로그


2012/04/11 09:25

Load Time Weaver를 사용하지 않는 DDD Domain Driven Design

본래 가장 이상적인 주입방식이라 할 수 있는 것은 AspectJ에서 제공하는 LTW를 활용하여 객체가 생성될 때, 새로운 도메인 오브젝트를 주입하는 것이다. 그러나 지난 포스트에서 알아봤듯 이런 방식은 JVM에 특정 기술을 삽입해야 하고 여러가지 다른 기술들의 제약조건이 변할 수 있다는 단점이 있다. 오늘은 과도한 튜닝을 하지 않고도 기본적인 스프링의 기능만으로 DDD방식의 설계를 하는 방법을 알아 보고자 한다.

DI(Dependency Injection) 그리고 DL(Dependency Lookup)

스프링에서 기본적으로 제공하는 '@Autowired', '@Resource', '@Qualifier'과 같은 애노테이션은 모두 DI를 위해 사용되고 있는 기술들이다. 점차 기술이 발전함에 따라 단순히 'Dependency Injection'이란 단어는 뜻 이상으로 프로그래밍적 의미가 겹겹이 쌓이게 되었고 DI와 DL은 서로 같으면서도 미묘한 차이가 존재하는 기술을 분류하는 용어로 변모하였다.

우선 DI는 우리가 익히 알고 있는 의존적 주입방법을 일컫는다. 자바의 @Configuration을 이용해 자바 클래스로 구성된 컨텍스트에서 직접적으로 오브젝트를 주입해준다거나 빈오브젝트로 등록된 데이터를 어노테이션을 이용해 불러오는 것들 말이다. 이것 말고도 일반적으로 많이 사용되고 있는 XML 컨텍스트 문서를 통해 주입하고 불러오는 것도 마찬가지다.

그렇다면 DI가 가지고 있는 독특한 성질이란 과연 무엇일까? 일반적으로 DI한다는 것은 우리가 어떠한 객체를 불러올지 미리 개발자가 예측하고 정해진 값을 불러오는 것을 뜻한다. 예를들어 'A'란 아이디의 빈을 등록하고 '@Resource A'와 같은 형태로 빈을 불러오는 과정은 우리가 직접 'A'라는 빈을 가져오는 것과 같다고 할 수 있다. 물론 '@Autowired'와 같은 애노테이션은 타입에 의해 검색이 실패할 시에 빈아이디로 검색을 한다거나, '@Qualifier'과 같은 어노테이션을 함께 사용하면 좀 더 영리하게 빈 선택이 가능하다는 점 등을 들어 DI의 방식이면서도 미묘하게 DL과 같은 성질이라고도 할 수 있다.

그러나 궁극적으로 이런 어노테이션들은 DI방식을 돕기 위한 도구일 뿐 DL이라고 하기에는 많은 무리가 있다. DL은 DI와 동일한 역할을 수행하긴 하지만 빈오브젝트를 좀 더 지능적인 검색을 통해 주입하는 방식을 일컫으며 DI보다는 복잡한 방식을 통해서만 접근할 수가 있다. 그러나 여기서 지능적이고 복잡하다고 해서 DL을 사용하는게 굉장히 유익하고 DI보다 한차례 발전한 기술이라고 오해해서는 안된다. DL은 개발자가 기본적으로 고려해볼만한 주입전략이라기보단 어디까지나 특수한 상황에서만 사용될 수 있는 전략이라는 점이고 현재와 같이 프로토타입 오브젝트를 주입받는다거나 빈오브젝트를 지역변수처럼 취급하고 싶을 때 주로 사용된다.

DDD(Domain Driven Design)에서는 빈오브젝트가 매우 다이나믹하게 생성되어야 하며 이를 단순히 멤버변수로 할당해 사용하기는 너무 많은 제약사항이 따르기 때문에 반드시 지역변수로 생성하여 사용해야 한다. 앞서 배운 @Configurable을 활용하면 new 연산자로 의존적 주입이 가능하긴 하지만 DL방식을 이용하면 기본적인 스프링과 자바의 기술만으로도 구현가능하다.


Prototype

이제 DL을 하기에 앞서 우선적으로 알아야할 개념이 있는데 그것은 바로 프로토 타입이다. 프로그래밍을 하다보면 프로토타입 용어를 자주 목격하게 되는데 본래에는 원형, 초기상태란 뜻을 가지고 있으며 스프링에서는 빈오브젝트를 싱글톤으로 관리하지 않고 매번 요청 때마다 새로운 오브젝트를 생성하여 돌려준다는 것을 뜻한다. 스프링에서 빈오브젝트를 프로토타입으로 설정하는 것에는 XML 방식과 애노테이션 방식, 두가지가 있으며 각각의 방법은 아래와 같다.

① 애노테이션 방식

@Component
@Scope("prototype")
public class Domain {

}
② XML 방식
<bean id="domain" class="…" scope="prototype" />

이제 해당 객체는 프로토타입으로 설정되었으며 매번 요청 때마다 새로운 객체를 생성하여 리턴해줄 것이다.


@Autowired ApplicationContext

프로토타입을 알았으니 본격적으로 DL에 대해 알아보자. 우선적으로 설명할 것은 'ApplicationContext'를 직접 주입받아 사용하는 방법이다.

@Autowired ApplicationContext applicationContext;

public void dependencyLookup() {
Domain domain = applicationContext.getBean(Domain.class);
}

스프링을 이용하다보면 따로 컨텍스트에 등록하지 않아도 자동으로 등록되는 빈이 몇가지 있는 'ApplicationContext'가 그 중에 하나다. 위의 예제에서는 'ApplicationContext'를 멤버변수로 불러와 해당 메서드의 지역변수에서 주입해줌으로써 DL이 성립하게 되었다.

그러나 이런 방식에는 몇가지 단점이 있다. 일단 단 한번의 주입을 위해 너무 무거운 오브젝트를 생성하였다는 것과 개발자가 구현하고자 하는 클래스와 전혀 관계없는 스프링의 로우기술이 클래스에 등장하였다는 점이다. 위와 같은 코드는 단순한 테스트 용으로는 적당하나 실제 개발에서 사용하기에는 많은 무리가 있다.


팩토리 빈의 활용 (ObjectFactory, ObjectFactoryCreatingFactoryBean)

예전에 스프링의 트랜잭션을 설명하면서 잠깐 팩토리 클래스에 대해 설명한 적이 있었는데… 그걸 참고용으로 올리기에는 조금 부족함이 많은 듯 싶다. 그렇다고 여기서 팩토리빈까지 다루자니 그 이야기만 하다가 끝나버릴 것 같아 아무래도 팩토리빈에 대한 설명은 추후에 좀 더 자세하기 올리기로 하고 여기에서는 팩토리빈은 자바에서 제공하는 다이나믹 프록시의 확장판 개념으로만 이해하면 충분하다.

여하튼 팩토리 빈을 활용하여 DDD를 구현하는 스케치는 먼저 위의 ApplicationContext을 호출하되 직접적으로 호출하는 것이 아니라 중간에 팩토리 빈을 하나 껴서 호출한다. 이 때 생성되는 팩토리빈은 우리가 구현하고자 하는 도메인 오브젝트에 종속되는 형태이므로 적절한 관심사의 분리가 이루어지게 된다.

간단하게 ObjectFactory를 활용한 구현으로 팩토리빈의 활용을 마무리하도록 하자.

<bean id="domainFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName" value="domain" />
</bean>
※ 'value="domain"'은 applicationContext.getBean("domain")과 같다.

@Resource private ObjectFactory<Domain> domainFactory;

public void doSomething() {
Domain domain = this.domainFactory.getObject();

}


Provider<T>

'Provider'는 가장 최근에 소개된 DL 방법으로 'JSR-330'에 추가된 자바 표준 인터페이스이다. 'Provider'는 위와 같이 팩토리빈을 활용하는 것보다 훨씬 쉽고 간편한 방식으로 빈오브젝트를 가져오기 때문에 LTW를 이용하지 않는다면 가장 우선적으로 고려해야될 기능이라고 생각하는 것이 좋다.

게다가 이 인터페이스는 자바 표준 인터페이스이므로 굳이 스프링이 아니더라도 다른 DI프레임워크에서도 활용가능한 기술이라는 점이다. 다음의 'Provider'를 활용한 DL 구현 예제를 살펴보자.

@Autowired private Provider<Domain> domainProvider;

public void doSomething() {
Domain domain = this.domainProvider.get();

}

팩토리빈을 이용했던 방식과는 다르게 추가적인 빈오브젝트를 생성할 필요없이 곧바로 해당 오브젝트를 생성할 수 있게 되었다.

덧글

댓글 입력 영역