티스토리 뷰

🧙‍♂️ God Object

전지전능한 오버시어, 고장나면 세계종말(?)

 

 

'갓 오브젝트'는 특정 오브젝트/스크립트가 한 씬(플로우, 시퀀스 등)에서 너무 많은 역할/책임을 가지게 되는 것으로 관리 혹은 오류에 취약해질 수 있는 상태가 될 수 있다.

 

유니티 게임 개발을 예로들면, 'GameManger' 클래스를 만들고 모든 흐름 제어를 'GameManager'가 하는 것과 같다.

 

이는 관리 차원에서는 쉬울 수 있다. 게임매니저가 허브의 역할을 하여 디버깅이 단순하고 각 매니저, 컨트롤러들의 독립성도 지킬 수 있다.

 

단, 이러한 경우 "기능 추가 = 게임매니저 코드 증가"로 쌓이게 되고 분기 처리와 책임 분리가 모호해질 수 있다. 또한 간단한 작업도 게임매니저를 통과해야하는 불필요한 절차가 반강제적으로 이루어질 수 있다.

 

 

✅ 장점 설명
일관된 흐름 제어 전체 게임 플로우를 GameManager가 조율하여, 버그나 로직 흐름 파악이 쉬움
디버깅 단순화 이벤트의 시작점과 연결된 액션이 GameManager에 집중되어 추적이 쉬움
낮은 결합도 매니저 간 직접 참조 없이 GameManager를 통해 통신하므로 유지보수에 유리
❌ 단점 설명
God Object 위험 GameManager가 너무 많은 책임을 지게 되어 거대 클래스가 될 가능성 있음
확장성 저하 기능이 늘어날수록 GameManager 코드가 복잡하고 무거워짐
유연성 부족 단순한 상호작용조차 GameManager를 거치면 오히려 구조가 불편해질 수 있음

 

 


💡 EventHub (이벤트 버스) 패턴

가장 간단하면서도 느슨하게 연결할 수 있는 방법.

각 컨트롤러가 사소한 이벤트를 주고/받고 싶을 때, 중간에 정적 이벤트 허브를 둬서 연결하는 방식이다.

 

구현 예시

public static class EventHub {
	public static Action<MonsterController> OnMonsterDied;
}
// MonsterController
public void Die() {
    Debug.Log("Monster died");
    EventHub.OnMonsterDied?.Invoke(this);
}
// BattleUIController.cs
private void OnEnable() {
    EventHub.OnMonsterDied += ShowDeathEffect;
}

private void OnDisable() {
    EventHub.OnMonsterDied -= ShowDeathEffect;
}

private void ShowDeathEffect(MonsterController monster) {
    Debug.Log($"{monster.name} 사망");
}
✅ 장점 ❌ 단점
컨트롤러 간 직접 참조 없음 → 낮은 결합도 구독 해제 누락 시 메모리 누수 가능
구현이 단순하고 직관적 어디서 이벤트가 발생했는지 추적이 어려움
유지보수성, 유연성 높음  
 
 

💡 인터페이스 + DI (의존성 주입)

조금 더 정형화된 구조를 원한다면, 인터페이스를 통해 의존성을 주입하는 방법도 좋다.
초기화 타이밍에서 명시적으로 연결을 해주는 방식이다.

 

구현 예시

 
// IMonsterEventReceiver.cs
public interface IMonsterEventReceiver {
    void OnMonsterDied(MonsterController monster);
}
// MonsterController.cs
private IMonsterEventReceiver receiver;

public void SetReceiver(IMonsterEventReceiver receiver) {
    this.receiver = receiver;
}

public void Die() {
    receiver?.OnMonsterDied(this);
}
// BattleUIController.cs
public class BattleUIController : MonoBehaviour, IMonsterEventReceiver {
    public void OnMonsterDied(MonsterController monster) {
        Debug.Log($"{monster.name} 사망");
    }
}
// GameInitializer.cs
public class GameInitializer : MonoBehaviour {
    [SerializeField] private MonsterController monster;
    [SerializeField] private BattleUIController battleUI;

    private void Awake() {
        monster.SetReceiver(battleUI); // 수동 주입
    }
}
✅ 장점 ❌ 단점
의존성 명확 → 테스트/리팩터링 용이  외부에서 반드시 주입해줘야 함 (타이밍 주의)
어디서 연결되는지 명확해서 추적 쉬움 코드가 살짝 길어짐
유닛 테스트에 강함  
 

 


🎯 결론

항목 EventHub 방식인터페이스 + DI
결합도 낮음 낮음 (더 명확함)
디버깅 어려움 쉬움
구현 난이도 낮음 중간
테스트 용이성 보통 높음
확장성 높음 높음

 

 

🧠 마무리

  • 게임의 흐름 전체를 조율하는 이벤트는 GameManager 또는 EventBus를 통해 처리하는 것이 좋다.
  • 기능 단위 컨트롤러 간의 이벤트 처리는 EventHub 혹은 인터페이스 + DI로 처리하면 깔끔.
  • 정말 고급 구조로 가고 싶다면, Zenject 같은 DI 컨테이너를 활용해도 좋다.
    • DI패턴도 너무 난발하면 안좋으니 필요에 따라 명확히 사용할것.
최근에 올라온 글
최근에 달린 댓글
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함