ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [WPF] DI(의존성주입), IoC(제어의역전) for Ninject
    Dev/닷넷 C# 2019. 7. 13. 15:34

    WPF Ioc, DI 라이브러리 NInject에 대해서 알아보자.

     

     

    DI, IoC


    ...더보기

    IoC(Inversion of Control)란, 개발자가 직접 생성하고 컨트롤 해야하는 객체를 프레임워크단에서 직접 컨트롤 해주는 패턴이다. 객체를 new()로 직접 생성하거나, 클래스 내부에서 싱글톤으로 생성하여 개발자가 'Class.Instance.GetDatabases();' 이런식으로 사용하던 것을 프레임워크가 직접 컨트롤 해준다는 의미이다. 그렇다면 직접 생성해도 되고 내부적으로 싱글톤을 사용해도 될텐데, 왜 프레임워크 단에서 이런 기술을 지원하는 것 일까? 프로젝트의 규모가 커지면 그 만큼 관리해야할 서비스단도 많이 질것이고, WPF나 모바일 환경에서의 경우 앱의 상태관리에 필요한 싱글톤객체가 우후죽순으로 생겨 날 것이다. 만약 Ioc가 없이 개발한다면 필요한 서비스나 싱글톤 객체를 전부 필요할때마다 생성해야 하는 괴물 코드가 나타날 것 이다..

     

    그렇다면 객체를 어떤 타이밍에? 어떻게? 어떤방법으로? 핸들링해서 넣어줘야 할까 ?

    .net mvc나 Spring같은 경우 Dispercher Servlet를 지나 Controller단이 호출될때 해당 컨트롤러가 생성되면서 프레임워크 단이 어노테이션(java)이나 어트리뷰트(C#)을 참조하여 해당 서비스를 주입 해주도록 설계 되어있다. 하지만 WPF같이 다음 화면을 new Page1(); 처럼 직접 객체를 생성해서 호출하게되면 앞단에서 처리해주는 클래스가 없다보니, IoC 같은 기능을 활용하려면 별다른 처리가 필요하다.

     

    여기서 '주입' 해주는 방법에 대해서 어떤 방법으로 객체를 넣어줄 것 인거에 대한 문제가 DI(Defendeny Injection)이다. DI를 주입하는 방식은 다음과 같다.

     

    1. 객체 생성자를 통한 방법,

    2. Setter를 통한 방법.

    3. 필드에 직접 주입하는 방법.

     

    가장 많이 쓰는 순서로는 3-> 2-> 1 순이고, 가장 이상적인 방법으로는 3 => 2 > 1 이라고 한다.

    가장많이 쓰이는 이유는 당연히 코드 가독성이 좋고, 편하고 생산속도가 빠르다는 이유이다. 하지만 필드에 직접 주입하는 방법은 안티 패턴이라 추천하지 않는다.

     

    정리하자면 , 

    IoC는 프레임워크가 직접 객체를 생성하고 관리해주는 것 이고

    DI는 의존성 있는 객체를 찾아서 해당 필드나 Setter, 생성자에 넣어주는 역활을 한다.

    그리고 이렇게 의존성을 가지고 Setter를 통해 추상화 된 객체가 변경 됐을때도 동일한 행동을 보장하는 패턴을 Strategy Pattern(전략화패턴)이라고 한다.

     

    그래서 WPF는 ? (NInject)


    NInject를 설정해보자.

    1. Nuget Pakage Manager를 통해 NInject를 종속성 주입을 한다.

    2. NInection을 감쌀수 있는 IoC Container를 만들어 준다.

       (NInject는 DI에 관련된 기능만 수행하므로 IoC의 대한 부분은 개발자가 만들어 줘야한다.)

      public static class Ioc
      {
        private static StandardKernel _kernel;
    
        public static T Get<T>()
        {
          return _kernel.Get<T>();
        }
    
        /// <summary>
        /// Create IoC Container
        /// </summary>
        /// <param name="config">IocConfiguration Implements Object</param>
        public static void Initialize(NinjectModule config)
        {
          if (_kernel == null)
            _kernel = new StandardKernel(config);
        }
      }

     

     

    3. app.cs 파일에 OnStartUp을 오버라이드 하고 Ioc 설정을 해준다.

    4. NinjectModule을 구현한 클래스를 만들고, 구현체에서 Load부분을 오버라이드 해준다.

    5. OnStartUp에서 Ioc 컨테이너를 Initailize 해준다.

      public partial class App : Application
      {
        protected override void OnStartup(StartupEventArgs e)
        {
          Ioc.Initialize(new IocContainer());     // Dependency Injection Configuration
          IService serivce = Ioc.Get<IService>(); // Get Inferface Object
          
          base.OnStartup(e);
        }
      }
    
        // NinjectModule 상속
      public class IocContainer : NinjectModule
      {
        public override void Load()  
        {
          Bind<IService>().To<Service>().InSingletonScope();    // IService -> Service  Binding
          Bind<Page2>().ToSelf().InTransientScope();            // Page Scope (not use WPF)
        }
      }

     

    5. Load() 내부에 의존주입을 설정한다.

    설정은 아래와 같다.

    Bind<InterfaceClass>().To<ImplementsClass>().InSingletonScope();  
    Bind<InterfaceClass>().To(typeof(ImplementsClass)).InSingletonScope();  // 타입에 의한 의존주입 
    Bind<InterfaceClass>().To<ImplementsClass>().InSingletonScope().Named("memberService"); // 이름에 의한 의존주입 
    

     

     

    6. Injection Pattern

       객채를 주입할때는 기본적인 어트리뷰트를 이용하는 방법 이외에도, IoC에 직접 접근해서 빼오는 방법도 있다.

    [Inject]
    public IService Service {set;get;}
    
    public IService Service { get {return Ioc.Get<IService>(); } } // IoC에 직접 접근
    

     

    처음에 적용하면서 가장 어려웠던 점은, [Inject] 어트리뷰트가 있어도 Service가 정상적으로 Set이 되질 않는다는 점 이였다. [Inject]를 달고 있는 Property는 해당 클래스가 IocContainer를 통해 생성되야지만 작동한다.

     

    예를 들면 ,

    TestViewModel이 있고 내부에 TestService가 있다. TestService에 [Inject]가 달려있다면 TestViewModel 자체를 new TestViewModel()로 생성해선 안되고 , Ioc.Get<TestViewModel>()로 생성하여야 [Inject]가 정상 작동한다.

     

    그렇다면 예시에서 TestViewModel도 Ioc Container에 등록하고 사용해야하는가? ViewModel은 싱글톤으로 관리할 생각이 없는데? 이런 생각을 할수도 있지만 IoC Container내부적으로 이미 등록된 객체는 싱글톤을 유지해주고, 등록되지 않은 객체를 Get하면 새 객채를 매번 만들어서 리턴해주니 염려하지 않아도 된다.

     

     

    ViewModel은 IoC에 등록되지 않았고, MemberService는 등록된 객체이다. 콘솔로그 결과로 싱글턴 유무를 확인할수 있다.

     

     

     

Designed by Tistory.