Game Development, 게임개발/배경지식

이벤트 큐의 활용 - 서버 요청 응답의 순서 보장

게임이 더 좋아 2022. 4. 3. 20:01
반응형
728x170

 

Event Queue에 대한 글을 이미 썼다.

[Game Developer, 게임개발자/디자인패턴] - Event Queue, 이벤트 큐 [디자인패턴](디커플링)

솔직히 지식으로 배울 때랑 차원이 다르다.

왜냐하면 요구 사항이 너무나 달라지기 때문이다.

하지만 기본을 알면 모든 것은 응용이 가능했다고 했던가... 결국 해냈다.

 


 

 

이벤트 큐는 실제로 어떠한 이벤트를 차례대로 수행하고 싶을 때 사용한다.

예를 들어 효과음이 일어나는 모든 상황을 이벤트 큐에 넣고 큐에서 빼면서 효과음을 실행한다던지

정말 무궁무진하다.

 

다시 말해서 내가 이벤트 큐에 넣는 시점에 실행되는 것이 아니라 이벤트 큐에서 빼낼 때 실행된다는 것이다.

 

실제로 1초동안의 모든 이벤트를 넣고 1초 이후에 이벤트 큐를 모두 비워내며 실행한다던지 등.. 

해당 이벤트를 처리하는 시간을 자유롭게 할 수 있는 것이다.

 

이번에 나는 서버 요청과 응답에 대해 적용하려고 했다. 

 


 

왜 이벤트 큐가 필요한가???

 

서버 요청은 항상 바로 되지 않는다.(응답이 바로오지 않는다)

서버는 수많은 클라이언트에 대해서 응답을 해줘야 하기 때문이다.

그 때문에 항상 비동기적으로 작동하여 I/O를 기다리지 않고 다른 처리를 할 수 있게 해주는데

서버 응답이 꼭 필요한 처리가 있다.

더군다나 서버 응답 처리의 순서가 보장되어야 하는 경우가 있다.

그래서 나는 이벤트큐를 실제로 서버 요청,응답에 대해 구현했다.

 

베이스 코드는 이렇다.

Queue

using System.Collections.Generic;

namespace CommandQueuePattern
{
    public class CommandQueue 
    {
        // 명령을 담는다( 이벤트든 명령이든 본질적으로 같다.)
        // -> 해당 interface를 상속받는 모든 것이 들어감
        private readonly Queue<ICommand> _queue;

        // 현재 명령을 처리중인지 여부
        private bool _isPending;

        public CommandQueue()
        {
            // 생성자
            _queue = new Queue<ICommand>();

            // 처음에는 당연히 처리하는 것들이 없음
            _isPending = false;
        }

        public void Enqueue(ICommand cmd)
        {
            // 큐에 넣음
            _queue.Enqueue(cmd);

            // 만약 처리 가능하면 바로 처리함.
            if (!_isPending)
                DoNext();
        }

        public void DoNext()
        {
            // 큐가 비었다면 처리하지 않음.
            if (_queue.Count == 0)
                return;

            // 큐에 명령이 있다면 꺼내서 실행함.
            var cmd = _queue.Dequeue();
            // 실행하면 변수 바꿔줌
            _isPending = true;
            // 아래 명령에 함수 추가 -> 실행 끝났을 때
            cmd.OnFinished += OnCmdFinished;
            // 해당 명령이 실행됨.
            cmd.Execute();
        }

        private void OnCmdFinished()
        {
            // 명령이 끝나면 이 메서드가 실행되고 이제 다음 이벤트 큐를 조사하는 DoNext를 하게됨
            _isPending = false;

            DoNext();
        }
    }
}

 

실제로 위 큐를 이용해서 작동은 아래 같이 한다.

using System.Collections;
using UnityEngine;

namespace CommandQueuePattern
{
    public class GameController : MonoBehaviour
    {
        public Popup firstPopUp, secondPopup, thirdPopup;

        private CommandQueue _commandQueue;

        private void Start()
        {
            // create a command queue
            _commandQueue = new CommandQueue();

            // add commands after a period of time
            StartCoroutine(StartCommandsCr());
        }

        private IEnumerator StartCommandsCr()
        {
            // wait for 2 seconds
            yield return new WaitForSeconds(2f);

            // add commands
            _commandQueue.Enqueue(new FirstCmd(this));
            _commandQueue.Enqueue(new SecondCmd(this));
            _commandQueue.Enqueue(new ThirdCmd(this));
        }
    }
}

 

여기서 주목할 점은.. 서버 처리하는 메서드다. 

비동기로 구현해야 했기에 Task를 가지며 비동기 함수는 저 OnFinished 함수의 순서를 보장하지 않는다.

즉, Reponse를 기다리는 작업을 따로 해줘야 한다는 말이다.

-> Task Document를 잘 읽으면 된다. 베이스 코드를 커스터마이징 하면 된다.

 

 

일반적인 EventQueue와는 다른 점이고 다른 요구 사항이다.

위의 처리를 적절히 해준다면 서버 요청과 응답의 순서를 보장하는 이벤트큐를 만들고

작업의 이전 처리 결과의 의존성을 없앨 수 있다.

 

사실 디자인 패턴을 배우면서도 이딴 걸 어디다 써? 라고 했었지만

내 실력이 부족해서 그런 것이었다. 디자인 패턴은 옛부터 유용한 것들의 집합이며 쓸 때가 무조건 있다.

 

 

반응형
그리드형