C#

C# 기초부터 고급까지 Chapter 2.9. 인터페이스 활용 – 다형성 실전 예제

Juan_ 2025. 5. 3. 22:19
728x90

📘 Chapter 2.9: 인터페이스 활용 – 다형성 실전 예제


✅ 이 챕터에서 배울 것

  • 인터페이스란 무엇인가?
  • 다형성이 왜 필요한가?
  • 구체 클래스 vs 인터페이스 비교
  • 실무 시나리오 예제 (MessageSender, Discount 등)
  • 전략 패턴과 인터페이스
  • DI와 인터페이스 궁합

1️⃣ 인터페이스란?


인터페이스란?
"기능만 약속하는 설계도" – 실제 구현은 안 담고, 메서드 이름, 파라미터만 정의

public interface IAnimal
{
    void MakeSound();
}
  • 객체의 기능 규칙만 강제
  • 클래스가 여러 인터페이스도 구현 가능
  • 상속과 다르게 다중 구현 가능

2️⃣ 왜 인터페이스가 필요한가?


문제 상황

public class EmailSender
{
    public void Send(string to, string msg)
    {
        Console.WriteLine($"📧 {to}에게 이메일 전송: {msg}");
    }
}

나중에 "카카오톡도 보내줘!" 하면?

public class MessageSender
{
    public void Send(string to, string msg)
    {
        if (사용자.카카오) { 카카오톡(); }
        else if (사용자.이메일) { 이메일(); }
        else if (사용자.슬랙) { ... }
    }
}

👎 if-else 지옥 😱 – 유지보수 지옥행

✅ 해결책: 인터페이스 사용

public interface IMessageSender
{
    void Send(string to, string message);
}

public class EmailSender : IMessageSender
{
    public void Send(string to, string message)
    {
        Console.WriteLine($"📧 이메일 전송: {message}");
    }
}

public class KakaoSender : IMessageSender
{
    public void Send(string to, string message)
    {
        Console.WriteLine($"📱 카카오톡 전송: {message}");
    }
}

3️⃣ 다형성 실전 활용


public class NotificationService
{
    private readonly IMessageSender _sender;

    public NotificationService(IMessageSender sender)
    {
        _sender = sender;
    }

    public void Notify(string user, string msg)
    {
        _sender.Send(user, msg);
    }
}

📌 이제 EmailSender, KakaoSender, SlackSender
전부 갈아끼울 수 있다!

예제 코드

IMessageSender sender = new EmailSender(); // 또는 KakaoSender
var service = new NotificationService(sender);

service.Notify("jang@daegu.com", "오늘도 화이팅!");

➡️ NotificationService는 Send가 어떤 방식인진 몰라도 됨


4️⃣ 전략 패턴과 인터페이스


전략(Strategy) 패턴 =
"행동을 객체로 캡슐화해서 교체 가능하게 만드는 구조"

public interface IDiscountStrategy
{
    decimal ApplyDiscount(decimal price);
}

public class PercentageDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price * 0.9m;
}

public class FixedAmountDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price - 5000;
}

public class OrderService
{
    private readonly IDiscountStrategy _strategy;

    public OrderService(IDiscountStrategy strategy)
    {
        _strategy = strategy;
    }

    public decimal CalculateFinalPrice(decimal price)
    {
        return _strategy.ApplyDiscount(price);
    }
}

➡️ 할인 정책을 바꾸려면? 클래스만 갈아끼우면 된다 😎


5️⃣ DI와 인터페이스는 찰떡궁합


builder.Services.AddTransient<IMessageSender, EmailSender>();
builder.Services.AddTransient<NotificationService>();
  • NotificationService는 생성자에서 IMessageSender만 의존
  • 나중에 EmailSender → KakaoSender 로 교체해도 코드 수정 없음

OCP (개방/폐쇄 원칙) 지킴

✅ 단위 테스트하기 쉬움

✅ Mocking도 쉬움 (xUnit/Moq 조합 가능)


✅ 정리 요약


항목 설명
인터페이스 메서드 시그니처만 정의, 다중 구현 가능
다형성 구현체를 바꿔도 동일하게 동작
전략 패턴 다양한 정책을 객체로 추상화
DI와 궁합 클래스 교체 자유로움, 테스트도 쉬움
실무 효과 유지보수성 ↑, 유연성 ↑, 재사용성 ↑

📢 다음 챕터 예고 🎓

다음 챕터 주제
Chapter 2.10 프로젝트 구조 패턴 – Layered, Clean Architecture 소개
728x90