C# .NET 단순 인터페이스 사용과 의존성 주입 DI 차이 설명

sunghoon·2025년 10월 20일
0

2.0 Glove Project

목록 보기
36/40

의존성 주입(Dependency Injection)과 인터페이스의 차이

1. 개요

의존성 주입(Dependency Injection, DI)은 객체 간의 결합도를 낮추기 위해 사용되는 설계 패턴이다.
핵심은 “객체가 필요한 다른 객체를 스스로 만들지 않고, 외부에서 주입받는다”는 것이다.

반면 인터페이스는 결합을 느슨하게 만들기 위한 설계 원칙이지만,
객체 생성 책임까지 외부로 넘기지 않으면 여전히 강한 결합이 남는다.

이 글에서는 직접 의존 → 인터페이스 기반 → 의존성 주입의 순서로 구조와 차이를 비교한다.

2. 직접 의존 (Tight Coupling)

public class Notification
{
    private EmailService _email = new EmailService();

    public void Alert(string msg)
    {
        _email.Send(msg);
    }
}
  • NotificationEmailService를 직접 생성한다.
  • 완전히 구현체에 의존하고 있으며, 다른 서비스로 바꾸려면 코드를 수정해야 한다.
  • 테스트가 어렵고 확장성이 낮다.

대표 용어: 강결합(Tight Coupling), 전통적 설계(Traditional Design)

3. 인터페이스 기반 강결합 (Interface-coupled)

public interface IMessageService
{
    void Send(string msg);
}

public class EmailService : IMessageService
{
    public void Send(string msg) => Console.WriteLine("메일 전송");
}

public class Notification
{
    private IMessageService _service;

    public Notification()
    {
        _service = new EmailService(); // 여전히 직접 생성
    }

    public void Alert(string msg)
    {
        _service.Send(msg);
    }
}
  • 인터페이스로 역할은 분리했지만, 여전히 new로 직접 객체를 생성한다.
  • 구조는 개선되었지만, 코드 수정 없이 구현체를 바꾸기 어렵다.
  • 테스트 시 Mock 객체를 주입하기도 어렵다.

대표 용어: 인터페이스 기반 강결합(Interface-coupled), 부분적 의존 역전(Partial Inversion)

4. 의존성 주입 (Dependency Injection)

public class Notification
{
    private readonly IMessageService _service;

    public Notification(IMessageService service)
    {
        _service = service;
    }

    public void Alert(string msg)
    {
        _service.Send(msg);
    }
}
  • 객체 생성 책임을 외부로 옮겼다.
  • 외부에서 구현체를 주입하면 내부 로직을 수정하지 않고 교체 가능하다.
  • 단위 테스트에서 Mock 객체를 손쉽게 사용할 수 있다.

대표 용어: 의존성 주입(Dependency Injection), IoC(Inversion of Control)

5. 실제 .NET에서의 DI 예시

using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddTransient<IMessageService, EmailService>();
services.AddTransient<Notification>();

var provider = services.BuildServiceProvider();

var notification = provider.GetRequiredService<Notification>();
notification.Alert("테스트 메시지");
  • ServiceCollection은 DI 컨테이너로, 객체 생성과 관계를 자동으로 관리한다.
  • AddTransient, AddScoped, AddSingleton은 객체의 수명(Lifetime)을 정의한다.
  • 개발자가 직접 new를 호출하지 않아도 주입이 자동으로 이루어진다.

6. 비교표

항목직접 의존인터페이스만 사용의존성 주입
객체 생성 위치내부 (new)내부 (new)외부 (컨테이너 / 호출자)
결합도매우 높음중간낮음
유연성없음교체 시 코드 수정 필요교체 시 등록만 변경
테스트 용이성낮음낮음높음
대표 용어강결합(Tight Coupling)인터페이스 기반 강결합의존성 주입(DI)
제어 주체클래스 내부클래스 내부외부(IoC Container)

7. IoC(제어의 역전)과의 관계

DI는 IoC의 한 형태이다.
즉, 객체 생성과 의존성 관리의 제어권이 클래스 내부에서 프레임워크로 “역전”되는 것이다.

방식제어 주체IoC 적용 여부
직접 의존클래스 내부
인터페이스만 사용클래스 내부
DI외부 컨테이너

8. 핵심 요약

  • 인터페이스는 역할을 정의하는 설계 원칙이다.
  • DI(의존성 주입)은 그 설계를 실제로 구현하는 기술이다.
  • 인터페이스만 쓰면 구조상 결합을 줄일 수 있지만,
    객체 생성 책임까지 외부로 넘겨야 진정한 느슨한 결합(Loosely Coupled)이 완성된다.
profile
프라다 신은 빈지노와 쿠페를 타는 꿈을 꿨다.

0개의 댓글