- Posted by 꽃미남 on 2009. 04. 10 16:44
안녕하세요. 꽃미남 입니다.
날이 부쩍 따뜻해졌습니다.
아직 아침저녁으로는 약간 쌀쌀히지만 따땃한 날씨에 꽃놀이의 유혹에 빠지기 좋은 날입니다.
이번주말은 윤중로나 거닐며 봄날을 만끽해봐야 겠습니다. ^ ^
이번에 포스팅 하려는 Spring.Net을 이용한 Depency Injection(DI) 에 대한 내용입니다.
이번 글은 주로 DI에 대한 개념을 잡는 글이므로 이에 대한 이해가 있으신 분들은 그냥 넘어가셔도 됩니다.
Spring.Net 이란?
엔터프라이즈 닷넷 어플레케이션의 개발을 좀 더 쉽게 개발할 수 있도록 도와주는 오픈소스 애플리케이션 프레임워크 입니다.
Spring.Net은 Java 진영에서 유명한 Spring을 닷넷으로 포팅한 것으로 단순히 Java 버전을 닷넷으로 포팅한 것이 아니라 닷넷에 맞게 변형되었습니다.
Spring은 자바 진영에서 상당히 오랜기간동안 안정성과 성능을 검증받았으며 이미 많은 사이트에서 사용되고 있습니다.
Spring.Net은 IoC의 핵심기능을 Core에 담고 있으며 그 외의 기능들은 별도로 분리되어 사용하는 기능만을 참조하여 사용하면 됩니다.
다음은 Spring.Net의 핵심모듈은 입니다.
Spring.Net은 springframework.net 에서 다운로드 받으실 수 있습니다.
(현재 최신 버전이 1.2.0 이며 진행은 1.2.0, .net 2.0 버전으로 하도록 하겠습니다.)
Dependency Injection(DI) 란?
직역하자면 '의존성 주입' 이라고 해석이 되겠군요.
그렇다면 '의존성 주입'은 어떻게 풀이할 수 있을까요?
이 설명을 위해 메시지를 보내는 예를 들어 설명하겠습니다.
회사 홈페이지에서 에러가 발생하였습니다. (예.. 생각하기 싫은 상황이군요.)
에러가 발생하면 개발자에게 메시지를 보내게 되어 있습니다.
SMS, MAIL 선택 폭도 넓습니다.
우리의 '메시지' 오브젝트는 SMS 인스턴스를 생성하여(new) 개발자에게 SMS를 보냅니다.
SMS를 받은 우리의 개발자.. 훌륭히 에러를 처리합니다.
그러던 중 SMS 시스템에 장애가 발생하여 MAIL로 메시지를 보내야 합니다.
MAIL로 메시지를 보내도록 프로그램을 수정 컴파일 후 다시금 본연의 기능을 수행하도록 합니다.
여기서 메시지 클래스는 대상이 되는 오브젝트에 강하게 의존(Dependency)하고 있습니다.
코드로 보자면 다음 코드정도일 것입니다.
(예제 코드는 콘솔 프로그램 형식으로 의사 코드(pseudo code)를 포함 합니다.)
static void Main(string[] args)
{
SMS sms = new SMS();
MessageSender messageSender = new MessageSender(sms);
messageSender.SendMessage("연락주세요.");
}
public class MessageSender
{
private SMS _sms;
public MessageSender(SMS sms)
{
_sms = sms;
}
public void SendMessage(string msg)
{
_sms.SendMessage(msg);
}
}
명시적으로 SMS 인스턴스를 생성하여 MessageSender 클래스에 할당 한 후 메시지를 보냅니다.
만약 SMS가 사용하지 않고 MAIL을 사용한다면 사용하는 객체(SMS)를 MAIL로 변경 후 사용하여야 합니다.
SMS, MAIL을 수시로 번갈아가며 사용한다면 여간 불편하지 않을 것입니다.
이를 사용 객체만이라도 통일성을 가지도록 인터페이스 형식으로 변경하도록 해보겠습니다.
IMessageService라는 인터페이스를 만들고 그 인터페이스를 그 인터페이스를 상속받은 SMS, MAIL를 구현하여 사용하는 것입니다.
대략의 소스를 보면 다음과 같을 것입니다.
static void Main(string[] args)
{
IMessageService messageService = (IMessageService)new SMS();
MessageSender messageSender = new MessageSender(messageService);
messageSender.SendMessage("연락주세요.");
}
public class MessageSender
{
private IMessageService _messageService;
public MessageSender(IMessageService messageService)
{
_messageService = messageService;
}
public void SendMessage(string msg)
{
_messageService.SendMessage(msg);
}
}
처음보다는 한결 나아진 모습입니다.
SMS, MAIL 클래스는 둘 다 IMessageService로부터 상속을 받으므로 SMS를 MAIL로 변경한다면 인스턴스 생성 부분만 수정하면 됩니다.
그러면 이런 변경이 있을 시 인스턴스 할당 부분의 코드를 고치지 않도록 팩토리 패턴으로 수정하여 보겠습니다.
(팩토리 패턴은 서로 관련성이 있는 클래스들을 생성하여 주는 중간 클래스를 두어 복잡도를 줄이는 방식입니다.)
코드는 다음과 같은 형식 입니다.
static void Main(string[] args)
{
MessageServiceFactory messageServiceFactory = new MessageServiceFactory();
MessageSender messageSender = new MessageSender(messageServiceFactory.GetMessageServiceObject());
messageSender.SendMessage("연락주세요.");
}
public class MessageSender
{
private IMessageService _messageService;
public MessageSender(IMessageService messageService)
{
_messageService = messageService;
}
public void SendMessage(string msg)
{
_messageService.SendMessage(msg);
}
}
public class MessageServiceFactory
{
public MessageServiceFactory() { }
public IMessageService GetMessageServiceObject()
{
return new SMS();
}
}
이제 SMS를 MAIL로 교체하여야 한다면 MessageServiceFactory만 고치면 되며, 호출하는 쪽에는 영향이 전혀 가지 않습니다.
이전 코드보다 또 한결 나아졌군요.
그러나 SMS를 MAIL로 교체하면 여전히 소스 코드 수정이 가해지며(MessageServiceFactory 클래스), MessageServiceFactory와 SMS, MAIL의 의존관계 역시 존재합니다.
만약 SMS 혹은 MAIL 인스턴스를 클래스 내부가 아닌 외부의 조립기(assembler)가 처리하여 준다면
SMS를 MAIL로 변경시 클래스의 내부는 수정 할 필요 없이 외부 조립기만 변경하여 주면 될 것입니다.
결론적으로 내부 클래스를 건드리지 않아도 된다는 것이죠.
네.. 그렇습니다.
이 이야기를 하기 위해 받기 싫은 에러 SMS까지 받아가며 장황하게 설명하였습니다.
방금 설명드린 것이 Dependency Injection(DI)의 기본 개념입니다.
의존(Dependency)에 대한 주입(Injection)을 외부에서 처리하여 주는 것입니다.
위의 예제에서는 SMS, MAIL의 생성부분을 조립기가 하여 주는 것인데,
SMS를 MAIL로 변경하여야 한다면 프로그램을 수정하지 않고 외부의 조립기에서 SMS에 대한 생성을 MAIL로 수정하기만 하면 됩니다.
우선 대략의 코드를 보면 다음과 같습니다.
static void Main(string[] args)
{
using (Spring.Context.IApplicationContext ctx = Spring.Context.Support.ContextRegistry.GetContext())
{
MessageSender messageSender = (MessageSender)ctx.GetObject("MessageSender");
messageSender.SendMessage("연락주세요.");
}
}
public class MessageSender
{
private IMessageService _messageService;
public MessageSender(IMessageService messageService)
{
_messageService = messageService;
}
public void SendMessage(string msg)
{
_messageService.SendMessage(msg);
}
}
여기서 외부조립기는 Spring.Net IoC 컨테이너의 조립기이며 MessageObject 는 App.Config에 정의 되어 있습니다.
App.Config 의 설정은 다음과 같습니다.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<description>Mvpmagazine DI Sample</description>
<object name="SMS" type="MessageService.SMS, MessageService" singleton="true"></object>
<object name="Mail" type="MessageService.Mail, MessageService" singleton="true"></object>
<object name="MessageSender" type="MessageService.MessageSender, MessageService" singleton="false">
<constructor-arg index="0" ref="SMS"></constructor-arg>
</object>
</objects>
</spring>
</configuration>
지금은 이해가 언뜻 가지 않으실 수도 있겠지만 다음 포스트에서 설명을 들으시면 명쾌하게 이해가 되실 것입니다.
(짤리먹기 신공인가요.. ^ ^;)
결과적으로 DI의 목적은 루즈 커플링(Loose Coupling)으로 볼 수 있으며, 객체들 사이의 의존 관계를 객체 자신이 아닌 외부의 조립기를 통해 수행하는 것입니다.
흔히 DI는 IoC(Inversion Of Control)와 헐리우드 법칙(Hollywood Principle) 혼용되어 불리기도 합니다.
IoC는 '제어의 역전'으로 풀이되는데요, 말 그대로 제어가 역전 된 것입니다.
SMS 객체를 예로 든다면 이 객체에 대한 생성부터 파괴까지 Life Cycle에 대한 관리를 프로그램이 아닌 외부의 조립기가 가져간 것입니다.
말 그대로 제어를 프로그램이 아닌 외부 컨터네이가 담당하므로 제어가 역전 된 것입니다.
(헐리우드 법칙 - 우리를 부르지 마라. 우리가 당신을 부르겠다.(Do not call us, we will call you))
이 조립기는 외부의 컨테이너(객체 인스턴스의 LifeCycle 관리)의 한 부분이며, 이제 사용할 컨테이너가 Spring.Net 입니다.
이제 이 글의 목적이 나왔네요. (먼~ 길이었습니다.)
바로 Spring.Net의 DI 컨테이너에 대한 사용이 이 글의 목적입니다.
이제 Spring.Net의 IoC 컨테이너에 대한 사용법으로 넘어가려 했습니다만은...
글이 길어져 다음 글로 바로 올리도록 하겠습니다.
(짤라먹기 신공입니다. ^ ^;)
I'll be back...
추가로 DI와 혼용되는 IoC는 DI와 동의어가 아닙니다.
DI는 IoC의 한 패턴입니다.
IoC는 DI(Dependency Injection)과 DL(Denepcy Lookup) 으로 나뉩니다.
DL은 DI 처럼 리소스를 주입(Injection)받는 방식이 아닌 찾는(Lookup) 방식 입니다.
DI는 또 다시 Constructor Injection, Setter Injection, Interface Injection 방식으로 나뉩니다만 이에 대한 설명은 차후로 미루기로 하겠습니다.
참고자료 :
Martin Fowler 'Inversion of Control Containers and the Dependency Injection pattern'
Wiki - Dependency Injection
평점 4.3 / 3회 참여
- Currently 4.333333/5 Stars.
- 1
- 2
- 3
- 4
- 5