← SEARCHABLE ARCHIVE
C# · 2026-07-25

C# 기초부터 마스터까지 — 4화

상속, 인터페이스, 제네릭으로 역할 나누기

INTERACTIVE READING MODE →

본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.

여러 클래스가 같은 동작을 제공해야 할 때 상속과 인터페이스를 사용할 수 있다. 공통점을 묶는 일보다 어떤 계약을 외부에 보여줄지가 더 중요하다.

인터페이스로 역할 표현하기

public interface IDamageable
{
    bool IsAlive { get; }
    void TakeDamage(int amount);
}

public sealed class Monster : IDamageable
{
    public int Health { get; private set; } = 100;
    public bool IsAlive => Health > 0;

    public void TakeDamage(int amount)
    {
        Health = Math.Max(0, Health - amount);
    }
}

공격 코드는 Monster 전체가 아니라 IDamageable만 알면 된다. 나중에 훈련용 허수아비나 파괴 가능한 상자를 추가해도 같은 코드를 사용할 수 있다.

상속은 is-a 관계에 사용한다

public abstract class Skill
{
    public string Name { get; }

    protected Skill(string name) => Name = name;
    public abstract void Execute(IDamageable target);
}

public sealed class FireSkill : Skill
{
    public FireSkill() : base("Fire") { }

    public override void Execute(IDamageable target)
    {
        target.TakeDamage(30);
    }
}

상속은 기반 클래스의 상태와 규칙까지 함께 물려받는다. 단순히 코드 몇 줄을 재사용하려고 깊은 상속 구조를 만들면 부모 변경이 모든 자식에 영향을 준다.

제네릭으로 타입 안전성 유지하기

public sealed class Registry<T> where T : class
{
    private readonly Dictionary<int, T> values = new();

    public void Add(int id, T value) => values.Add(id, value);

    public T? Find(int id)
    {
        return values.TryGetValue(id, out T? value) ? value : null;
    }
}

Registry<Monster>와 Registry<Skill>은 같은 저장 규칙을 사용하지만 서로의 값을 섞을 수 없다. 제약 조건인 where는 T가 제공해야 할 성질을 컴파일러에 알려준다.

인터페이스는 역할, 상속은 공통 기반, 제네릭은 타입이 다른 같은 규칙을 표현한다. 세 기능을 한 번에 많이 쓰기보다 가장 작은 계약부터 만들고 필요할 때 확장하는 편이 안전하다.