[디자인 패턴] 전략 패턴(Strategy Pattern)과 ScriptableObject 활용

이준영·2026년 2월 21일
post-thumbnail

서론

게임을 개발하다 보면 처음에는 단순한 '공격' 기능만 있으면 됐는데, 기획이 확장되면서 '불꽃 공격', '얼음 공격', '흡혈 공격' 등 수많은 변종이 생겨나기 시작한다.

대부분은 가장 먼저 '상속(Inheritance)'을 생각한다. 하지만 기능이 늘어날수록 클래스 계층 구조는 복잡해지고, 비슷한 코드를 여러 곳에 복사 붙여넣기 하고있다.

(Gemini 왈/대충 저도 비슷한 생각)
"새로운 기능을 만들 때마다 매번 코드를 새로 짜야 할까? 인스펙터에서 레고 블록처럼 기능을 조립할 수는 없을까?"

이 고민을 해결해 주는 패턴이 바로 전략 패턴(Strategy Pattern)이다. 특히 유니티의 ScriptableObject와 결합하면, 코드 한 줄 수정하지 않고도 에디터에서 드래그 앤 드롭만으로 완전히 새로운 스킬과 유닛을 창조할 수 있는 강력한 시스템을 구축할 수 있다.

추상 클래스 구현

AbilityEffect
  • 모든 효과의 기본 형태가 되는 부모 클래스이다.
  • 더 쉽게 만드려면, 매개변수를 클래스나 구조체로 묶는 것도 방법이다.
  • Unit은 테스트용 클래스.
Unit
  • 만들어진 효과를 사용하는 클래스.

효과 구현

데미지 효과
  • player가 target에게 대미지를 주는 효과를 가진 클래스이다.
이펙트 효과
  • 이펙트를 실행하는 클래스이다. 직렬화 형태로 이펙트를 넣으면 더 효율적인 형태가 될 것 같다.
버프 효과
  • 데미지를 증가시켜 주는 효과를 가진 클래스.
힐 효과
  • player의 체력을 회복시키는 효과를 가진 클래스.
ScriptableObject 생성
  • CreateAssetMenu로 생성할 수 있다.

복합 구조 설명

전략 패턴의 장점은 '조합'이다. 예를 들어, '공격'과 '힐'를 각각 만들었다면, 이 둘을 코딩 없이 합쳐서 '흡혈 공격'이라는 새로운 스킬을 만들 수 있다.

이를 위해 Unit 클래스를 구현했다.

Unit
  • 일단 간단하게 하려고 Unit 을 구현했지만, 더 좋은 방법은 AbilityEffect를 상속 받은 흡혈 공격 클래스를 구현하는 것이다.
CompositeAbility
  • 해당 SO를 생성하고 이름을 "흡혈 공격"으로 변경.
  • 힐과 공격 효과를 넣으면 된다.
Drain Attack Effect

장점

  • 재사용성 극대화: DamageEffect 하나만 잘 만들어두면, 일반 공격, 독 화살, 폭발 마법 등 모든 곳에 드래그해서 재사용할 수 있다.

  • 재귀적 구조: CompositeAbility 역시 AbilityEffect를 상속받기 때문에, 복합 기능 안에 또 다른 복합 기능을 넣는 것도 가능.

  • 기획자와의 협업: 프로그래머가 기능을 하나 추가할 때마다 기획자는 수십 가지의 새로운 조합을 스스로 테스트해 볼 수 있다.

주의할 점

해당 방법을 사용할 때, 주의할 점이 있는데 바로 실행 순서이다.

직렬화로 넣은 순서대로 작성하기 때문에, Buff와 Damage를 조합한 "강화 공격" 만든다고 가정하면, Damage가 먼저 실행되는 경우에는 데미지가 예상과 다르게 들어갈 수 있다.

OX

테스트

GameManager
  • 비주얼과 대미지 테스트
PlayerLogTarget
  • 피흡과 버프 공격 테스트
PlayerLogTarget

정리

전략 패턴과 ScriptableObject를 결합한 시스템은 확실히 장점이 많다.

1. "코드 수정 없는" 무한한 확장성

개방-폐쇄 원칙(Open-Closed Principle)에 따라, 기존 코드를 건드리지 않고도 새로운 기능을 추가할 수 있다.
새로운 공격 방식이 필요하다면 새로운 ScriptableObject 클래스를 하나 더 만들기만 하면 된다.
기존의 캐릭터 로직이나 시스템 코드를 열어볼 필요가 없으므로 예기치 못한 버그로부터 자유로워진다.

2. 개발자와 기획자의 "협업성 증가"

이 아키텍처의 가장 큰 매력은 데이터 중심 설계라는 점이다. 프로그래머는 '기능 조각'이라는 도구를 만들고, 기획자는 그 도구를 조합해 '콘텐츠'를 만든다.
기획자가 스킬 데미지를 10에서 20으로 바꾸거나, 이펙트를 교체하기 위해 프로그래머를 찾을 필요가 없다.
인스펙터에서 에셋을 교체하는 것만으로 밸런스 조정과 기능 테스트가 가능해지므로 개발 속도가 비약적으로 상승한다.

3. 유닛 테스트와 디버깅의 간소화

거대한 하나의 클래스는 어디서 버그가 터졌는지 찾기 어렵다. 하지만 전략 패턴으로 쪼개진 기능들은 각각 독립적인 단위이므로, '데미지' 로직에 문제가 있다면 해당 클래스만 확인하면 된다.
기능이 모듈화되어 있어 특정 상황을 재현하거나 단위 테스트(Unit Test)를 작성하기에도 훨씬 수월하다.

profile
게임 개발자가 되기 위해서 공부하는 중입니다.

0개의 댓글