개발/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 인터페이스만 상속한 클래스만 새로 추가하면 끝이여서 매우 편했다.
앞으로는 기능 설계할 때 확장에는 열려있고, 수정에는 닫혀 있는지를 항상 생각해봐야 겠다.