숏컷 코드
체력 변경
-> Health가 이벤트 발행
-> UI / SFX / 퀘스트 시스템이 각자 구독문법과 예시
직접 참조는 빠르지만 관계가 늘어날수록 얽힌다
플레이어 체력이 줄었을 때 UI, 사운드, 카메라 흔들림, 튜토리얼, 퀘스트 진행이 모두 반응해야 한다면, Health가 이 시스템들을 직접 다 알고 있을
필요는 없습니다. 직접 참조는 처음엔 간단하지만 반응 대상이 늘어날수록 결합이 빠르게 커집니다. observer 패턴은 사건을 발행하는 쪽과 반응하는 쪽을
분리해, 한 사건에 여러 시스템이 느슨하게 붙을 수 있게 합니다.
직접 참조
Health -> UI.Update()
-> SFX.Play()
-> Quest.Check()
이벤트 방식
Health -> OnHealthChanged 발행
UI / SFX / Quest가 각각 구독observer의 핵심은 "누가 누구를 몰라도 되는가"다
관찰자 패턴이 유리한 지점은 사건 발행자와 수신자가 서로를 몰라도 될 때입니다. EnemyDeath를 발행하는 적은 퀘스트 시스템이나 드롭 시스템을 몰라도 됩니다.
반대로 호출 순서와 결과가 강하게 묶여 있고, 실패 시 즉시 처리 분기가 필요하다면 직접 호출이 더 읽기 쉬울 수 있습니다. 판단 기준은 "방송형 사건인가,
즉시 협력 호출인가"입니다.
Unity에서는 C# event, UnityEvent, ScriptableObject event channel이 서로 다르다
코드 중심 흐름이면 C# event가 가볍고 명확합니다. Inspector에서 연결하고 싶다면 UnityEvent가 편하지만 런타임 디버깅과 refactor 안전성은 더 주의해야 합니다. 씬/프리팹 경계를 넘어 이벤트 채널을 asset으로 두고 싶다면 ScriptableObject event channel이 맞을 수 있습니다. 모두 같은 "이벤트"처럼 보여도 운영 방식이 다릅니다.
observer의 가장 흔한 버그는 해제 누락이다
구독 해제를 빼먹으면 이미 파괴된 객체가 콜백을 받거나, 씬 전환 뒤 옛 리스너가 남아 중복 반응이 생깁니다. 그래서 관찰자 패턴은 발행보다 등록/해제 수명주기를 먼저 설계해야 합니다. "언제 subscribe하고 언제 unsubscribe하는가"가 패턴 품질을 좌우합니다.
장점은 낮은 결합도, 단점은 설정과 추적 비용이다
observer의 장점은 주체와 관찰자의 분리, UI와 코어 로직의 느슨한 연결, 각 관찰자의 독립적인 응답 로직입니다. 반대로 단점도 분명합니다. 이벤트를 위해 추가 설정이 필요하고, 발행자를 여전히 참조해야 하며, 대형 화면이나 많은 오브젝트에서는 오버헤드가 생길 수 있습니다. 즉 사건 수가 많아질수록 "어디서 발행되고 누가 듣는지"를 추적하는 운영 규칙도 같이 필요합니다.
명명 규칙과 payload 설계가 커지면 더 중요해진다
ThingHappened, OnThingHappened, DoThing처럼 이벤트, 핸들러, 발행 메서드의 이름을 분리해서 관리하면 사건 경계가 선명해집니다.
실무에서는 이벤트 이름과 payload 구조를 일관되게 잡아야, 사건이 많아져도 해석이 가능합니다. 둘 이상의 관찰자가 같은 이벤트를 듣는다면
고유 ID나 핵심 payload를 함께 보내는 편이 더 안전합니다.
이벤트 구조를 고를 때 핵심
| 상황 | 적합한 선택 |
|---|---|
| 한 사건에 여러 시스템이 반응해야 할 때 | observer / event 구조 |
| 호출 순서와 결과가 강하게 묶여 있을 때 | 직접 호출이 더 단순 |
| 코드 중심 런타임 이벤트 | C# event |
| Inspector 연결이 중요한 UI 이벤트 | UnityEvent 검토 |
| 프리팹/씬을 넘는 느슨한 채널이 필요할 때 | ScriptableObject event channel 검토 |
| 씬 전환 뒤 중복 반응 버그가 날 때 | subscribe / unsubscribe 수명주기부터 점검 |
| 같은 이벤트를 여러 대상에 구분 적용해야 할 때 | ID 또는 payload를 함께 전달 |
특이 케이스와 주의할 점
흔한 실패는 모든 신호를 하나의 범용 이벤트 버스에 몰아넣는 것입니다. 이렇게 되면 누가 어떤 이벤트를 발행하고 누가 듣는지 추적이 어려워집니다. observer 패턴은 결합을 줄이는 도구이지, 사건 이름과 책임을 흐리는 도구가 아닙니다. 이벤트 이름과 payload 규칙이 없으면 나중에 시스템이 커질수록 디버깅 비용이 급격히 올라갑니다.
실패 예시
- GlobalEventBus 하나에 모든 게임 이벤트를 string 이름으로 발행
- 리스너는 어디서든 임의 문자열을 구독
결과
- 오타와 리팩터링 취약점 증가
- 어떤 사건이 누구에게 영향을 주는지 추적 어려움참고 링크
1 sources