Design Pattern

의존성 주입(Dependency Injection)

Russell Developer 2024. 10. 1. 00:19

의존성 주입(Dependency Injection) 디자인 패턴이란?

의존성 주입(Dependency Injection, DI)은 객체 지향 프로그래밍에서 객체 간의 의존성을 관리하고 결합도를 낮추기 위한 디자인 패턴입니다. 이 패턴은 객체의 의존성을 외부에서 주입하여 객체 간의 강한 결합을 제거하고, 코드의 유연성재사용성을 높이는 데 목적이 있습니다.


1. 의존성이란?

소프트웨어 개발에서 의존성은 하나의 객체가 다른 객체에 의존하는 관계를 의미합니다.
예를 들어, 클래스 A가 클래스 B의 기능을 사용해야 한다면, 클래스 A는 클래스 B에 의존하고 있다고 말할 수 있습니다.

문제점:

  • 강한 결합: 의존하는 객체가 변경될 경우, 의존성을 가진 객체도 수정해야 할 수 있습니다.
  • 테스트 어려움: 단위 테스트를 작성할 때 의존성이 강하면 테스트 대상 객체를 독립적으로 테스트하기 어렵습니다.

2. 의존성 주입(Dependency Injection)이란?

의존성 주입은 객체의 의존성을 외부에서 주입하는 방식으로 해결하는 디자인 패턴입니다.
주입된 의존성을 통해 객체는 자신이 필요한 객체를 직접 생성하지 않고, 외부에서 전달받게 됩니다.

예시:

다음 예시에서 Car 클래스는 Engine 클래스에 의존하고 있습니다.
하지만 의존성을 직접 생성하지 않고, 외부에서 주입받아 사용합니다.

// Engine 클래스
public class Engine
{
    public void Start() 
    {
        Console.WriteLine("엔진이 시동을 겁니다.");
    }
}

// Car 클래스
public class Car
{
    private readonly Engine _engine;

    // 의존성 주입을 위한 생성자
    public Car(Engine engine)
    {
        _engine = engine;
    }

    public void Drive()
    {
        _engine.Start();
        Console.WriteLine("차량이 운전 중입니다.");
    }
}

여기서 Car 클래스는 Engine 객체를 직접 생성하지 않고, 생성자를 통해 주입받습니다.
이는 Car 클래스가 Engine 클래스에 대해 덜 의존적이게 만들어 줍니다.


3. 의존성 주입의 종류

C#에서는 의존성을 주입하는 다양한 방법이 있습니다:

1) 생성자 주입(Constructor Injection)

가장 일반적인 방식으로, 클래스의 생성자를 통해 의존성을 주입합니다.
위 예시에서 Car 클래스는 생성자를 통해 Engine 객체를 주입받습니다.

public class Car
{
    private readonly Engine _engine;

    // 생성자를 통해 의존성 주입
    public Car(Engine engine)
    {
        _engine = engine;
    }
}

2) 세터 주입(Setter Injection)

생성자가 아닌 프로퍼티메서드를 통해 의존성을 주입하는 방식입니다.
이 방식은 선택적 의존성 주입에 유용합니다.

public class Car
{
    public Engine Engine { get; set; }

    public void Drive()
    {
        Engine?.Start();
        Console.WriteLine("차량이 운전 중입니다.");
    }
}

3) 인터페이스 주입(Interface Injection)

클래스가 의존성을 주입받을 인터페이스를 구현하도록 강제하는 방법입니다.
이 방법은 잘 사용되지 않지만, 의존성을 명확하게 보여줄 수 있습니다.

public interface IEngine
{
    void Start();
}

public class Car
{
    private IEngine _engine;

    public void SetEngine(IEngine engine)
    {
        _engine = engine;
    }
}

 


4. 의존성 주입의 장점

의존성 주입 패턴을 사용하면 다음과 같은 이점을 얻을 수 있습니다:

1) 결합도 감소

객체 간의 강한 결합을 줄이고, 서로 독립적으로 동작할 수 있도록 만듭니다.
이는 코드의 유지보수성을 높이는 데 기여합니다.

2) 유연성 및 확장성 향상

객체의 의존성을 쉽게 교체할 수 있습니다.
예를 들어, 테스트 환경에서는 실제 Engine 대신 MockEngine을 주입할 수 있습니다.

public class MockEngine : IEngine
{
    public void Start()
    {
        Console.WriteLine("Mock 엔진이 시동을 겁니다.");
    }
}

3) 테스트 용이성

객체 간의 의존성이 약해지므로, 단위 테스트를 할 때 모의 객체(Mocks)를 활용하여 객체를 독립적으로 테스트할 수 있습니다.

4) 코드 재사용성 증가

동일한 의존성을 여러 곳에서 재사용할 수 있으며, 새로운 의존성을 추가할 때도 코드 변경을 최소화할 수 있습니다.


5. 의존성 주입의 구현 예시

의존성 주입을 수동으로 구현할 수도 있지만, DI 프레임워크를 사용하면 더 편리하게 관리할 수 있습니다.
.NET에서는 기본적으로 ASP.NET Core에서 의존성 주입을 쉽게 설정할 수 있습니다.

// 의존성 등록
services.AddTransient<IEngine, Engine>();
services.AddTransient<Car>();

// 의존성 주입
public class HomeController : Controller
{
    private readonly Car _car;

    public HomeController(Car car)
    {
        _car = car;
    }

    public IActionResult Index()
    {
        _car.Drive();
        return View();
    }
}

이처럼 ASP.NET Core의 DI 컨테이너를 사용하면, 객체를 명시적으로 생성하지 않고도 자동으로 주입받아 사용할 수 있습니다.


 

의존성 주입(Dependency Injection)은 객체 간의 결합도를 낮추고, 코드의 유연성테스트 용이성을 크게 향상시키는 디자인 패턴입니다. 코드의 재사용성과 확장성을 높여주는 의존성 주입은 특히 단위 테스트, 유지보수성을 고려한 대규모 시스템 개발에 매우 유용한 패턴입니다.