메시지나 이벤트를 보내는 시점과 처리하는 시점을 디커플링 한다.
풀어 말하면, 각 객체들이 이벤트를 관리하는 매니저에게 메시지를 보낼 수 있고,
특정 메시지가 오면 알려달라고 부탁할 수도 있다.
여기까지는 관찰자 패턴과 동일하다. 추가된 것은 '시점'이다.
이벤트 큐 패턴은 관찰자 패턴의 비동기 버전이라고 할 수 있다.
[ 이벤트 시스템의 간단한 예시 ]
몬스터 관리 클래스는 격파될 때마다 'OnMonsterDeath' 이벤트를 호출. 퀘스트 관리 클래스는 OnMonsterDeath 이벤트가 발생할 때마다 이벤트 큐 매니저가 호출해줘서 횟수를 기록한다.
이벤트 시스템을 만들 때 많이 사용되는 UniRX가 한 예시이다.
예시 : 사운드 시스템
일반적으로 사운드 매니저를 구현해보면 이런 식이 될 것이다.
public class AudioManager
{
public void PlaySound()
{
var _audioFile = LoadAudioFile();
_audioFile.Play();
}
}
사운드 시스템을 고려되야할 이슈가 몇 가지 있다.
1. PlaySound()를 호출하였을 때 메모리에 사운드 파일이 올라와 있지 않은 경우 동기적으로 로딩하면 프레임 드랍이 발생할 것이다.
2. 같은 사운드가 같은 프레임에 재생되면 파형이 증폭되어 문제가 생긴다.
3. 동시 재생 가능한 사운드의 개수가 제한적이다.
특히 3번의 경우 재생 요청이 들어오자마자 실행시키는 방식을 취할 경우 몬스터의 피격 소리를 재생하느라 더 우선순위가 중요한 사운드, 이를테면 레벨업이나 퀘스트 완료 심지어 BGM 등이 재생되지 않을 수 있다.
여기서 맨 앞에서 말한 '보내는 시점과 처리하는 시점을 디커플링 한다.'의 의도가 무엇인지 눈치챘을 것이다.
시점의 디커플링
자료구조 '큐'는 선입 선출로 처리된다. 요청하는 쪽에서는 결과를 기다리지않고 메시지를 보낼 뿐이고, 요청을 처리하는 쪽에서는 나중에 처리한다. 이때, 요청을 처리하는 곳은 여러 곳이 될 수도 있다.
명령을 요청하는 곳과 명령을 수행하는 곳을 분리하기만 할 뿐이면 관찰차 패턴으로 충분하다.
이벤트 큐 패턴은 '시점'또한 분리된다.
시점을 분리하는 이유는 몇 가지가 있는데,
위의 사운드 시스템 이슈 3번 처럼 메시지들의 필터링을 거쳐야 할 때 (우선순위에 따라서 중요한 사운드 먼저 재생)
큰 파일의 사운드를 비동기 로딩해야할 때. (주로 BGM이 그럴 것이다) 등이 있다.
주의해야 할 점
지연 실행때문에 상태가 변할 수 있다.
요청 시점과 실행 시점이 다르다는 것은 장점이자 단점이다. 이벤트 처리를 위해서 필요한 정보가 실행 시점에서는 메모리에서 사라졌을 수도 있고, 값이 변했을 수 있다.
몬스터의 레벨이 플레이어 레벨보다 낮을수록 얻는 경험치가 낮아지는 시스템이 존재한다고 해보자.
몬스터를 잡았을 때 경험치를 얻는 이벤트를 설정했다고 했을 때, 이벤트를 요청하고 처리하는 시점 사이에 플레이어가 레벨업을 했다면 어떻게 될 것인가?
이런 수치적인 부분은 큰 문제가 되지 않겠지만, 객체의 레퍼런스가 사라졌다던지 하는 경우에는 에러가 발생하여 일이 커질 것이다.
피드백 루프에 빠질 수 있다.
A가 이벤트를 보냈는데, B가 받아서 특정 메서드를 실행했다. 해당 메서드에서는 또 다른 이벤트를 호출했는데 공교롭게도 A가 그 이벤트를 구독하고 있었다!
A -> B -> A -> B ... 의 이벤트 핑퐁이 시작되지만 비동기라서 눈치채지 못하고 메모리 누수처럼 성능을 점점 잡아먹는 상황이 벌어질 수 있다.
때문에 가급적 이벤트를 처리하는 코드 내에서는 이벤트를 호출하지 않는 것이 좋다.
마치며..
요즘에는 유니티로 개발할 때 UniRX를 적극 활용하고 있어서, 이벤트 메시지의 취합, 필터링 개념등에 익숙해지고 있다.
이벤트 큐 패턴은 확실히 디버깅은 조금 힘들지만, 디커플링에 큰 도움이 되고 있다.
'🛡️ 디자인 패턴' 카테고리의 다른 글
타입 객체 패턴 (Type Object Pattern) (1) | 2021.09.22 |
---|---|
커맨드 패턴 (Command Pattern) (0) | 2021.07.11 |
컴포넌트 패턴 (Componenet Pattern) (1) | 2021.07.04 |