IT개발

C#에서 인터페이스, 추상화, 클래스: 스타크래프트로 알아보기

HyochulLab 2025. 3. 11. 11:37

안녕하세요! 스타크래프트 유닛들이 C# 코드에서는 어떻게 구현될 수 있을지 상상하며, 객체지향 프로그래밍(OOP)의 핵심 개념인 클래스, 추상 클래스, 인터페이스를 간단히 살펴보겠습니다.

1. 클래스(Class)란?

클래스는 객체를 생성하기 위한 ‘설계도’로, 속성(프로퍼티)과 행위(메서드)를 정의합니다.

예를 들어, 스타크래프트에서 ‘마린(Marine)’은 테란(Terran) 종족의 기본적인 보병 유닛입니다.

  • 체력(HP), 공격력(Attack Power), 이동 속도(Move Speed) 등의 속성이 있을 것이고,
  • 적을 공격(Attack)하거나 이동(Move)하는 등의 행위를 할 수 있죠.

기본 예시

public class Marine
{
    // 속성(프로퍼티)
    public string Name { get; set; } = "Marine";
    public int Health { get; set; } = 45;
    public int AttackPower { get; set; } = 6;

    // 메서드(행위)
    public void Move()
    {
        Console.WriteLine($"{Name}이(가) 이동합니다.");
    }

    public void Attack()
    {
        Console.WriteLine($"{Name}이(가) 공격합니다! 공격력: {AttackPower}");
    }
}
  • Marine 클래스는 **구체적(Concrete)**으로 존재하므로 new Marine()을 통해 바로 객체(인스턴스)를 만들 수 있습니다.
  • 실제 스타크래프트 설정에서는 사거리, 방어력(Armor) 등 더 많은 속성이 있지만, 여기서는 간단히 예시만 잡아봤습니다.

2. 추상화(Abstraction) & 추상 클래스(Abstract Class)

추상화는 불필요한 구체적인 정보를 숨기고, 중요한 개념을 부각하는 과정을 의미합니다.
**추상 클래스(Abstract Class)**는 상속받는 클래스들에게 공통적인 속성과 메서드 시그니처(틀)를 제공하되, 일부 구현은 자식 클래스에 맡기는 클래스입니다.

스타크래프트에는 마린뿐 아니라, 저글링(Zergling), 질럿(Zealot), 드라군(Dragoon) 등 다양한 유닛이 있죠. 이들은 서로 종족도 다르고 능력치도 다르지만, 전반적으로 **‘유닛’**이라는 공통점이 있습니다.

  • 모든 유닛은 체력이 있고, 이동을 하고, 공격을 한다.
  • 구체적인 이동 방식이나 공격 방식(근접/원거리, 공격력 등)은 각 유닛마다 다르다.

이러한 공통점을 추상 클래스로 뽑아낼 수 있습니다:

추상 클래스 예시

public abstract class Unit
{
    // 공통 속성
    public abstract string Name { get; }
    public abstract int Health { get; set; }
    public abstract int AttackPower { get; set; }

    // 공통 메서드(시그니처만 제공)
    public abstract void Move();
    public abstract void Attack();
}
  • public abstract class Unit 형태로 선언된 추상 클래스는 인스턴스화할 수 없습니다. 즉, new Unit()은 불가능합니다.
  • abstract 키워드가 붙은 Name, Health, AttackPower, Move(), Attack()는 **구현(Implementation)**이 없습니다.
  • 해당 추상 멤버들은 자식 클래스(예: Marine, Zergling, Zealot)가 반드시 **오버라이드(Override)**하여 구체 구현을 제공해야 합니다.

추상 클래스를 상속받아 구현한 구체 클래스

public class Marine : Unit
{
    public override string Name => "Marine";
    public override int Health { get; set; } = 45;
    public override int AttackPower { get; set; } = 6;

    public override void Move()
    {
        Console.WriteLine($"{Name}이(가) 이동합니다. (보병 걸음)");
    }

    public override void Attack()
    {
        Console.WriteLine($"{Name}이(가) 총으로 사격! 공격력: {AttackPower}");
    }
}

public class Zealot : Unit
{
    public override string Name => "Zealot";
    public override int Health { get; set; } = 100;
    public override int AttackPower { get; set; } = 8;

    public override void Move()
    {
        Console.WriteLine($"{Name}이(가) 이동합니다. (프로토스 근접 보병 이동)");
    }

    public override void Attack()
    {
        Console.WriteLine($"{Name}이(가) 사이 블레이드로 공격! 공격력: {AttackPower}");
    }
}
  • 이제 Marine과 Zealot은 Unit을 상속받아 공통 멤버를 모두 구현했습니다.
  • 코드에서 Unit 타입으로 여러 유닛을 한꺼번에 관리할 수도 있고, 각 유닛 타입마다 구현 세부사항을 다르게 처리할 수도 있습니다.

3. 인터페이스(Interface)

인터페이스는 클래스나 구조체가 구현해야 할 **메서드/프로퍼티의 ‘규약(Contract)’**만 정의하고, 구현 내용은 전혀 없는 형태입니다.

스타크래프트에는 이동 방식, 공격 방식 외에도 특수 능력이 많은 유닛이 있습니다. 예를 들어,

  • 뮤탈리스크는 공중 유닛이기도 하고, 공격 시 투사체가 튕기는 특수 효과가 존재합니다.
  • **메딕(Medic)**은 공격 대신 아군을 치유(Heal)할 수 있는 능력이 있습니다.

이때, 모든 유닛이 치유 능력을 갖고 있진 않으므로, 추상 클래스 Unit에 Heal() 메서드를 넣으면 어색합니다. 대신 아래처럼 IHealable 인터페이스를 따로 정의할 수 있죠.

인터페이스 예시

public interface IHealable
{
    // 구현 없이 '어떤 기능을 수행해야 하는지' 규약만 제시
    void Heal(Unit target);
}
  • IHealable을 구현하는 클래스는 반드시 Heal(Unit target) 메서드를 구현해야 합니다.

메딕(Medic) 클래스

public class Medic : Unit, IHealable
{
    public override string Name => "Medic";
    public override int Health { get; set; } = 60;
    public override int AttackPower { get; set; } = 0; // 공격 불가

    public override void Move()
    {
        Console.WriteLine($"{Name}이(가) 이동합니다. (치유 유닛 걸음)");
    }

    public override void Attack()
    {
        Console.WriteLine($"{Name}은(는) 공격 능력이 없습니다!");
    }

    // IHealable 인터페이스 구현
    public void Heal(Unit target)
    {
        Console.WriteLine($"{Name}이(가) {target.Name}을(를) 치유합니다.");
        target.Health += 10; // 예시로 체력 10 회복
    }
}
  • Medic는 Unit을 상속받아 유닛으로서 공통 멤버를 가진 동시에, IHealable을 구현해 치유 기능을 수행합니다.
  • 같은 IHealable을 구현하는 다른 유닛(예: 프로토스의 Shield Battery를 흉내낸 클래스 등)을 만들 수도 있겠죠.

인터페이스 vs 추상 클래스

  • 추상 클래스: 공통 속성, 공통 구현 일부를 가지고 있고, ‘is-a’ 관계에 사용
  • 인터페이스: 특정 기능/역할을 표현하며, 구현체에 규약만 강제. 공통 구현은 없음

예:

  • Unit 추상 클래스 → 모든 유닛이 가져야 할 공통 코드(체력, 이동, 공격)를 정의(또는 시그니처라도)
  • IHealable 인터페이스 → 치유 기능을 제공하려면 어떤 메서드를 구현해야 하는지 규약만 명시

4. 간단 사용 예시

실제 사용 예시를 들어 보겠습니다.

class Program
{
    static void Main()
    {
        // 다양한 유닛 생성
        Unit marine = new Marine();
        Unit zealot = new Zealot();
        Medic medic = new Medic();

        // 공격 & 이동
        marine.Move();
        marine.Attack();

        zealot.Move();
        zealot.Attack();

        // 메딕의 치유 - IHealable 기능 활용
        medic.Move();
        medic.Attack(); // 공격 불가
        medic.Heal(marine); // 마린 체력 10 회복
        Console.WriteLine($"마린 체력: {marine.Health}");
    }
}

실행 결과 (예시):

Marine이(가) 이동합니다. (보병 걸음)
Marine이(가) 총으로 사격! 공격력: 6
Zealot이(가) 이동합니다. (프로토스 근접 보병 이동)
Zealot이(가) 사이 블레이드로 공격! 공격력: 8
Medic이(가) 이동합니다. (치유 유닛 걸음)
Medic은(는) 공격 능력이 없습니다!
Medic이(가) Marine을(를) 치유합니다.
마린 체력: 55

5. 요약

  • 클래스(Class): 객체를 생성하기 위한 기본 설계도. Marine, Zealot처럼 구체적인 실체를 표현할 수 있음.
  • 추상 클래스(Abstract Class): 공통점을 묶되, 인스턴스화할 수 없고 일부(또는 전부)를 자식 클래스에 구현하도록 강제. Unit처럼 ‘모든 스타크래프트 유닛’이 공통으로 가져야 할 특성을 정의.
  • 인터페이스(Interface): 특정 기능(메서드, 프로퍼티 등)을 사용할 수 있는지를 나타내는 계약(Contract). IHealable처럼 ‘치유할 수 있음’을 나타내는 행위를 명시.

언제 어떤 걸 써야 할까?

  1. 추상 클래스
    • 부모 클래스가 구체적인 일부 동작이나 필드를 가지고 있어야 하고, 자식들 간 공통 구현부가 있어야 할 때
    • ‘is-a(OO 상속) 관계’가 성립
  2. 인터페이스
    • 전혀 다른 계층의 클래스도 특정 기능을 ‘공유’해야 할 때
    • 공통 구현이 필요하지 않고, 기능의 시그니처(규약)만 필요할 때
    • 다중 구현(여러 인터페이스 동시 구현)이 가능

맺음말

스타크래프트 유닛을 예시로 들어보니,

  • 추상 클래스는 ‘유닛’이라는 공통점을 정의하는 데 적합하고,
  • 인터페이스는 필요에 따라 ‘특수 능력’을 선언하는 데 적합함을 알 수 있었습니다.

OOP에서는 상속, 인터페이스 구현, 추상화 등의 개념을 어떻게 조합하느냐에 따라 코드 구조와 재사용성이 크게 달라집니다.
게임 속 유닛처럼 다양한 특성을 가진 객체들이 상호작용할 때, 적절한 추상화인터페이스를 활용해 유연하고 유지보수하기 쉬운 코드를 만들어보시길 바랍니다.

감사합니다!