종속성 주입에 대해

종속성 주입.md

Updated at 2021.6.7

Dependency Injection

Dependency Injection는 종속성 주입 또는 의존성 주입이라고 하는데, 클래스를 설계할 때 두 개 이상의 클래스 사이의 관계를 설정하는 방법이다.

클래스 관계 설정

프로그래밍에서 많이 맞딱들이는 상황이 어떤 클래스에서 다른 클래스를 사용하고자 할 때 어떻게 할 것인가 이다.

예를 들어 아래와 같이 ClassB에서 ClassA를 초기화하여 사용하면 ClassA를 변경하고자 할 때 코드를 직접 수정하지 않으면 안된다.

class ClassA {
  public string Name { get; set;}
  
  public void SetAndPrintOut(string firstName, string familyName)
  {
    Name = $"{firstName}, {familyName}";
    Console.WriteLine(Name);
  }
}

class ClassB {
  private ClassA _classA = new();

  public void PrintOut(string firstName)
  {
    _classA.SetAndPrintOut(firstName, "Kim");
  }
}

더구나 아래의 다이어그램과 같이 ClassA와 유사한 클래스(ClassAA 등)나 객체가 많아서 그것을 ClassB에서 사용하고자 한다면 어떻게 하면 될까?

graph LR subgraph ClassB ClassA ClassAA end

인터페이스와 종속성 주입

아래의 다이어그램과 같은 방식으로 종속성 주입을 활용하면 이러한 문제를 해결할 수 있다.

graph LR subgraph ClassB IClassA end IClassA -.-> ClassA IClassA -.-> ClassAA

위의 코드를 아래와 같이 수정하면 ClassA가 변경되어도 ClassB를 다시 코딩할 필요가 없다. 즉 느슨하게 결합 (Loosely Coupled)된 코딩을 할 수 있다.

  • 인터페이스(Interface) IClassA 선언
  • 인터페이스를 상속받는 ClassA 선언
  • ClassB에서 IClass를 내부 필드(Field)로 선언하고, 생성자에서 변수를 입력 받아서 대입
interface IClassA {
  string Name { get; set;}
  void SetAndPrintOut(string firstName, string familyName);
}

class ClassA: IClassA {
  public string Name { get; set;}
  
  public void SetAndPrintOut(string firstName, string familyName)
  {
    Name = $"{firstName}, {familyName}";
    Console.WriteLine(Name);
  }
}

class ClassB {
  private readonly IClassA _classA;

  public ClassB(IClassA classA)
  {
    _classA = classA;
  }

  public void PrintOut(string firstName)
  {
    _classA.SetAndPrintOut(firstName, "Kim");
  }
}

ASP.NET에서의 종속성 주입이란?

웹코딩에서 글로벌 변수로 선언하여 모든 하위 웹에서 그 객체를 공용으로 사용하고 싶을 때 어떻게 할 것인가? ASP.NET에서는 Startup 클래스의 ConfigureServices 함수내에서 IServiceCollection의 사용할 수 있는 다음과 같은 3가지 함수를 제공한다.

graph TD A(IServiceCollection) --> AddTransient A --> AddScoped A --> AddSingleton

서비스 수명에 따라 선택해서 사용하면 된다. Server를 기준으로 간단히 정리해 보면 다음과 같다.

수명 설명
Transient 요청할 때마다
Scoped 새 창을 열고 다시 접속할 때마다 또는 새로 고침할 때마다
Singletone 처음 시작할 때만

실제 코딩에서의 사용법은 다음과 같다.

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    services.AddSingleton<ClassA>();
    services.AddSingleton<IClassA, ClassA>();
  }
}

파라미터를 가지고 종속성 주입하기

ClassA의 생성자가 파라미터를 가지고 있는 경우, 그리고 그 파라미터도 서비스로 등록이 되어 있는 경우에는 어떻게 할 것인가? 즉 글로벌 객체를 인자로 가지는 글로벌 객체를 선언해야 할 경우이다.

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    services.AddSingleton<IClassA, ClassA>();
    services.AddSingleton(x => new ClassB(x.GetService<IClassA, ClassA>()));
  }
}

댓글