2025. 3. 29. 10:32ㆍ유니티 unity/디자인패턴
게임을 만들다 보면 “전체 흐름은 같지만, 세부는 다르게” 처리하고 싶은 순간이 정말 많습니다. 예를 들어 다음과 같은 상황을 떠올려 보세요:
- 여러 미니게임들이 있고, 공통된 흐름(셋업 → 실행 → 종료)은 같지만 내부 로직은 다를 때
- 다양한 UI 팝업이 열릴 때 공통 애니메이션을 재생하되, 보여주는 내용은 달라야 할 때
- 여러 개의 적 AI가 같은 순서로 행동하지만 구체적인 동작은 다르게 해야 할 때
이처럼 동일한 틀 안에서 세부만 바꾸고 싶을 때, 아주 적합한 디자인 패턴이 바로 템플릿 메서드(Template Method) 패턴입니다.
템플릿 메서드 패턴이란?
템플릿 메서드 패턴은 알고리즘의 전체 흐름(템플릿)은 상위 클래스에서 정의하고,
세부 동작은 하위 클래스에서 구현하도록 강제하는 패턴입니다.
즉, 전체 구조는 변하지 않도록 고정하고, 중간의 특정 단계만 자식 클래스에서 자유롭게 구현하게 만드는 방식입니다.
"뼈대는 부모가 잡고, 살은 자식이 붙인다"는 개념이라고 보시면 됩니다.
언제 쓰면 좋은가요?
- 여러 클래스가 동일한 순서를 따르되, 각 단계의 동작이 달라야 할 때
- 상속 기반으로 코드 중복을 줄이고 싶은 경우
- 상태 전이, UI 처리, 시나리오 실행 등 공통 흐름이 있는 시스템에서
- 코드 재사용성과 유지보수성을 높이고 싶을 때
기본 구조 예제
public abstract class GameFlow
{
public void Run()
{
Initialize(); // 공통 전처리
Play(); // 각 게임마다 다르게
Cleanup(); // 공통 후처리 or 오버라이드 가능
}
protected abstract void Initialize();
protected abstract void Play();
protected virtual void Cleanup()
{
Debug.Log("공통 정리 로직 실행");
}
}
자식 클래스에서는 이렇게 사용합니다:
public class PuzzleGame : GameFlow
{
protected override void Initialize() => Debug.Log("퍼즐 셋업");
protected override void Play() => Debug.Log("퍼즐 시작");
}
public class MemoryGame : GameFlow
{
protected override void Initialize() => Debug.Log("메모리 셋업");
protected override void Play() => Debug.Log("메모리 게임 시작");
protected override void Cleanup() => Debug.Log("메모리 게임 클리어 정리");
}
이제 Run()을 호출하면 각 클래스의 로직 흐름은 동일하지만, 내부 행동은 다르게 실행됩니다.
유니티에서의 실전 활용 예시
1. 미니게임 시스템에 적용
공통된 흐름: 셋업 → 시작 → 종료
public abstract class MiniGameBase : MonoBehaviour
{
public void StartGame()
{
Setup();
Play();
End();
}
protected abstract void Setup();
protected abstract void Play();
protected virtual void End()
{
Debug.Log("기본 종료 처리");
}
}
자식 클래스: 버튼 누르기 미니게임
public class ButtonMashGame : MiniGameBase
{
protected override void Setup()
{
Debug.Log("버튼 마시 게임 셋업 완료");
}
protected override void Play()
{
Debug.Log("플레이어가 버튼을 누릅니다");
}
}
자식 클래스: 공 피하기 미니게임
public class DodgeBallGame : MiniGameBase
{
protected override void Setup()
{
Debug.Log("공 피하기 게임 초기화");
}
protected override void Play()
{
Debug.Log("공이 날아오기 시작합니다!");
}
protected override void End()
{
base.End();
Debug.Log("특수한 종료 이펙트 재생");
}
}
이제 어떤 미니게임이든 StartGame()만 호출하면 공통된 흐름 속에서 각자 다르게 동작합니다.
2. UI 팝업 시스템에 적용
공통 흐름: 애니메이션 → 내용 설정 → 버튼 등록
public abstract class PopupBase : MonoBehaviour
{
public void Open()
{
PlayOpenAnimation();
SetContent();
RegisterButtons();
}
protected virtual void PlayOpenAnimation()
{
Debug.Log("기본 팝업 애니메이션 재생");
}
protected abstract void SetContent();
protected abstract void RegisterButtons();
}
자식 클래스: 경고 팝업
public class WarningPopup : PopupBase
{
protected override void SetContent()
{
Debug.Log("경고 내용 설정");
}
protected override void RegisterButtons()
{
Debug.Log("닫기 버튼 등록");
}
}
자식 클래스: 보상 팝업
public class RewardPopup : PopupBase
{
protected override void SetContent()
{
Debug.Log("획득한 보상 표시");
}
protected override void RegisterButtons()
{
Debug.Log("확인 및 더 보기 버튼 등록");
}
}
Open()만 호출하면 팝업마다 알맞은 연출과 버튼이 자동으로 처리됩니다.
장점 정리
장점 설명
코드 재사용성 | 공통 흐름을 상위 클래스에서 한 번만 구현하면 됨 |
유지보수 용이 | 흐름이 깨지지 않게 보호됨 (상위 클래스에서 통제) |
유연한 확장성 | 하위 클래스에서 필요한 부분만 자유롭게 구현 가능 |
디버깅 쉬움 | 흐름이 명확해서 디버깅이 쉬움 |
템플릿 메서드 패턴 vs 전략 패턴
구분 템플릿 메서드 전략 패턴
상속 기반 | ✅ (필수) | ❌ (구성 기반) |
실행 흐름 제어 | 부모 클래스가 전체 흐름 제어 | 전략 객체가 로직을 소유 |
변경 가능성 | 상속 트리로 확장 | 런타임에 전략 교체 가능 |
둘 다 비슷해 보이지만, 템플릿 메서드는 상속 기반, 전략 패턴은 조합 기반이라는 점에서 쓰임이 다릅니다.
마무리하며
템플릿 메서드 패턴은 유니티처럼 다양한 요소들이 유사한 흐름으로 움직이되, 세부 동작이 달라야 할 때 아주 강력한 선택입니다. 미니게임, UI 시스템, 적 AI, 이벤트 트리거 등 어느 곳이든 적용할 수 있고, 구조를 정리하고 유지보수를 쉽게 만들어줍니다.
프로젝트가 조금 복잡해지고, 비슷한 코드가 자꾸 반복된다면? 지금이 바로 템플릿 메서드 패턴을 도입할 타이밍입니다.
'유니티 unity > 디자인패턴' 카테고리의 다른 글
유니티에서 중재자(Mediator) 패턴 활용하기 (0) | 2025.03.29 |
---|---|
유니티에서 빌더(Builder) 패턴 활용하기 (0) | 2025.03.29 |
유니티에서 싱글톤(Singleton) 패턴을 활용하기 (0) | 2025.03.29 |
유니티에서 퍼사드(Facade) 패턴을 제대로 활용해보자 (0) | 2025.03.29 |
유니티에서 이벤트 버스(Event Bus) 패턴을 활용한 유연한 시스템 만들기 (0) | 2025.03.29 |