IOC using Ninject

Posted in .NET Framework // Posted at 2013. 6. 18. 23:40
728x90

모니터링 시스템에 알림을 구현하는 독립된 서비스가 존재한다.

이 서비스는 Email, SMS, Messenger와 같은 수단으로 알림 전송을 대행해 주는 역할을 하고 있다.

 

대략적인 내부 구현은,

Mail의 경우 사내 SMTP 서버를 이용하여 메일 전송을 수행하며, SMS의 경우 3rd Party에서 제공하는 모듈을 이용하고 Messenger의 경우 사내 메신저가 노출하는 연동 인터페이스로 메시지 전송을 수행한다.

 

서비스는 잘 동작하고 있지만, 문제는 환경의 변화에 유연하지 못하다는 것이다.

다시말해 Mail, SMS, Messenger의 연동 환경이 달라진 환경에 알림 서비스가 설치되어야 한다면 서비스의 내부 코드도 같이 변경되어야 하고 다시 빌드해서 배포해야 한다는 것이다.

 

따라서 알림 서비스의 유연성을 좋게 하기 위해 인터페이스 기반으로 동작하도록 하는 것이 첫번째 할 일이다.

(코드를 단순화 하기 위해 Email 전송 부분만을 예로 든다. 구차한 매개변수도 역시 생략한다)

 

* NotificationService

//이메일 전송을 위한 인터페이스를 선언한다

public interface IEmailSender
{
     string Send();
}

 

 

//메일 전송기 클래스를 생성한다

//IEmailSender를 매개변수로 받음으로써 모듈간 커플링을 제거한다

public class MailService
{
     private IEmailSender _sender;

     public MailService(IEmailSender sender)
     {
         this._sender = sender;
     }

     public string Send()
     {
         return this._sender.Send();
     }

}

 

이제 실제 이메일 전송을 수행하는 구상 클래스를 생성한다. 이 구상클래스는 환경에 따라 달라질 수 있는 부분으로 교체가능한 모듈이 된다

* EmailSenderA

//메일 전송은 환경에 따라 달라질 수 있는 부분이므로'A' 타입 전송으로 가정한다

public class EmailSenderA : NotifactionService.IEmailSender

{
     string NotifactionService.IEmailSender.Send()
     {
         return "EmailSender-A";
     }
}

 

이제 메일 서비스를 사용해보자.

* NotifcationAgent

//메일 서비스를 이용해서 메일 전송을 수행하는 콘솔 어플리케이션이다

static void Main(string[] args)
{
       NotifactionMethodA.EmailSenderA sender = new NotifactionMethodA.EmailSenderA();
       MailService mailService = new MailService(sender);
       string result = mailService.Send();
       Console.WriteLine(result);

}

 

동작은 잘 하지만, 역시 구상 객체에 의존하게 된다(new NotificationMehodA.EmailSenderA())

 

이제 IOC 컨테이너 유틸리티인 Ninject가 개입할 시기다. Ninject 라이브러리를 참조하고 아래와 같이 코드를 수정한다

static void Main(string[] args)
{
       IKernel ninjectKernel = new StandardKernel();
       ninjectKernel.Bind<IEmailSender>().To<NotifactionMethodA.EmailSenderA>();
       NotifactionService.MailService mailService = ninjectKernel.Get<NotifactionService.MailService>();
       string result = mailService.Send();
       Console.WriteLine(result);

}

 

역시 잘 동작한다. 그러나 좀 더 유연하게 하고 싶다. 위 코드에서도 EmailSenderA가 슬그머니 한 자리를 차지하고 있다. 이것마저 제거하고 싶어 졌다. 즉 Config 파일에 구상객체 정보를 설정해서 보다 유연하게 만드는 것이다. 다음 주소에서 훌륭한 코드를 볼 수 있었다.

http://www.stum.de/2009/12/30/loading-a-type-specified-in-web-config-for-example-a-ninject-module/

 

 

먼저 실제 구상객체가 표현되는 Ninject 바인딩 부분을 NotificationAgent와 분리시킨다

* NinjectMoudleA

public class NinjectMoudleA: NinjectModule
    {
        public override void Load()
        {
            Bind<IEmailSender>().To<NotifactionMethodA.EmailSenderA>();
        }
    }

여기까지만 해도 꽤나 유연해진 모습이다. 필요시 Dll을 교체해서 환경 변화를 수용할 수 있다.

 

그러나 조금 더 나아가 Config에서 위 모듈을 동적으로 불러오도록 변경해 보자.

NotifcationAgent에 다음과 같이 KernelCreator라는 클래스를 생성한다. 이 클래스는 Config 파일로부터 (앞서 생성한) NoinectMoudle을 동적으로 불러들여서 Ninject 커널 객체를 반환한다.

//Config 파일에서 실제 구상객체를 바인딩하는 어셈블리를 로딩하고

public class KernelCreator
{
        public IKernel Create()
        {
            AppSettingsReader appSettingsReader = new AppSettingsReader();
            string moduleName = (string)appSettingsReader.GetValue("ninjectModule", typeof(string));
           
            Type moduleType = Type.GetType(moduleName);

            NinjectModule module;
            if (moduleType != null)
            {
                module = Activator.CreateInstance(moduleType) as NinjectModule;
            }
            else
            {
                throw new Exception(string.Format("Could not find Type: '{0}'", moduleName));
            }

            return new StandardKernel(module);
        }
}  

 

Config 파일 설정은 다음과 같다.

* App.Config

<appSettings>
    <add key="ninjectModule" value="MyNinjectModule.NinjectModuleA, MyNinjectModule" />
  </appSettings>

 

이제 NotificationAgent는 다음과 같이 작성할 수 있다.

static void Main(string[] args)
{
        IKernel ninjectKernel = new KernelCreator().Create();
        NotifactionService.MailService mailService = ninjectKernel.Get<NotifactionService.MailService>();
        string result = mailService.Send();
        Console.WriteLine(result);

}

 

이제 변화에 유연하도록 구성이 완료되었다.

 

만일 환경이 달라저서 다른 이메일 전송 방법을 사용해야 한다면, 다시말해 NotifactionMethodB로 교체되어야 한다면 다음의 과정을 거치면 된다

 

1. MailService의 IEmailSender 인터페이스를 구현한다(NotifactionMethodB)

2. NinjectMoudleB를 생성해 Ninject 바인딩을 정의한다

3. Config 파일을 수정한다

 

이 과정이 복잡해 보일 수 있으나 모든 시스템 환경에 적용할 필요는 없을 것이다. 유연성 사치를 부릴 필요가 없는 시스템이라면 위의 전체 과정에서 몇 가지를 제거할 수도 있다. 위 과정은 이미 개발된 시스템의 변경을 최소화하고 확장을 유연하게 할 수 있도록 하는 만큼 시스템 환경에 따라 적절히 적용할 수 있을 것이다.

 

 

 

'.NET Framework' 카테고리의 다른 글

나에게 유용했던 닷넷 서적  (2) 2013.07.04
WCF Service Reference Error(Cannot import wsdl:portType)  (0) 2013.07.03
Security in OAuth  (0) 2013.05.29
Bit Flag of Enum  (0) 2013.05.28
TimeStampHasCreationTimeInFuture in WCF Security 2  (0) 2013.05.23