개발/OOP

[ SOLID ] 개방-폐쇄 원칙 : 몬스터 공격 방식을 바꾸며 배운 것

김노라 2025. 5. 31. 20:48

게임개발 중 몬스터가 플레이어를 공격하는 로직을 만들고 있었다.

처음에는 몬스터가 그냥 근접공격만 해서 if문과 enum을 조합한 로직으로 개발하고 있었다.

if(attackType == AttackType.Melee)
{
    MeleeAttack();
}


근데 나중에 원거리 공격, 범위 공격, 도트 공격 등 여러 공격이 추가되며 if문을 switch-case문으로 변경하였다.

switch(attackType)
{
    case AttackType.Melee: 
        MeleeAttack();
        break;
    case AttackType.Ranged: 
        RangedAttack();
        break;
    case AttackType.Aoe: 
        AreaAttack();
        break;
    ...
}

몬스터 한 마리 추가할 때마다 케이스가 많아지면서 생각한게

이게 공격 로직인지 회의록인지 싶을 정도였다.

 

 

 

 


 

 

 

그래서 생각해본게 새로운 공격 방식을 추가할 때마다 기존 코드를 건드리지 않고 가능할까?

하고 검색하다가 OCP(Open-Closed Principle) 개방-폐쇄 원칙을 알게 되었다.

이것은 새로운 기능은 추가 가능하지만, 기존 코드는 그대로 유지해야 한다는 원칙이다.

 

 

 


 

 

 

이 원칙을 지키려면 if나 switch-case문이 아니라, 인터페이스 + 다형성 구조로 바꿔야 했다.

일단 공격 행동을 추상화 시켰다.

public interface IAttackStrategy
{
    void Attack();
}

 

그리고 공격 방식은 전략 클래스에서 구현시켰다.

// MeleeAttack.cs
public class MeleeAttack : IAttackStrategy
{
    public void Attack() => Debug.Log("근접 공격!");
}

// RangedAttack.cs
public class RangedAttack() : IAttackStrategy
{
    public void Attack() => Debug.Log("원거리 공격!");
}

 

마지막으로 몬스터는 전략만 가지고 실행한다.

public class Monster : MonoBehaviour
{
    private IAttackStrategy attackStrategy;
    
    private void Awake()
    {
        attackStrategy = new MeleeAttack();
    }
    
    public void DoAttack()
    {
        attackStrategy.Attack();
    }
}

 

 

 


 

 

 

처음에는 굳이 이렇게까지 클래스를 쪼개서 해야 하나 싶었는데,

이렇게 구현을 해보니까 기존 Monster 클래스에는 손을 대지 않고, 

IAttackStrategy 인터페이스만 상속한 클래스만 새로 추가하면 끝이여서 매우 편했다.

 

 

 


 

 

 

앞으로는 기능 설계할 때 확장에는 열려있고, 수정에는 닫혀 있는지를 항상 생각해봐야 겠다.