11. Unity + WebSocket, Coroutine을 실행시킬 수 없을 때?

sonohoshi·2021년 3월 30일
0

무슨 글인가?

말 그대로, 유니티와 웹소켓을 이용한 개발을 하던 도중 코루틴을 실행시킬 수 없는 문제에 빠졌었다. 누가 유니티를 웹소켓을 같이 쓰겠냐 싶긴 하지만, 비슷한 문제에서도 적용할 수 있을거라 생각되어 적어보게 되었다.

알고있으면 좋은 것

스레드의 개념, 그리고 스레드를 이용할 때 주의해야 하는 점 등을 알고 있으면 좋습니다. 모르면 이제부터 알면 되죠.
콜백 메소드(call-back) 에 대해 알고 있으면 좋습니다. 모르시는 분들은 간단하게, 메소드를 메소드의 인자로 넘긴 것 정도라고 생각하시면 됩니다.

문제 상황

  1. 플레이어의 위치를 동기화하기 위해 웹소켓 서버에 접속 된 후 부터 0.1초마다 자신의 위치를 서버로 보내기로 했다.

  2. 이를 위해, 서버에 접속하는 메소드에 성공 후 실행되는 콜백으로 1번에서 언급한 작업을 하는 코루틴을 실행시키도록 넘겨주었다.

  3. 서버에는 연결이 됐지만, 위치를 0.1초마다 보내는 코루틴은 전혀 실행되지 않았다?

라는 것이 오늘의 문제 내용이다. 하다못해 코루틴이 실행된 뒤 찍히는 로그조차도 찍히지 않는데다 에러도 뱉는 내용이 아무것도 없어 큰 벽에 가로막힌 기분이었다.

문제가 있었던 코드

socketManager.AddOpenEvent((sender, e) =>
{
    print("Established!");
    StartCoroutine(SendPositionInfinitely());
});

소켓 매니저 어쩌구는 여러분은 모르셔도 됩니다. 결론만 따지자면, 서버에 접속 됐을 때 저 코루틴을 실행시키기로 했다. 이 부분만 이해하시면 됩니다.

코루틴 내용은 다음과 같습니다.

private IEnumerator SendPositionInfinitely()
{
    Debug.Log("Start coroutine");
    while (true)
    {
        SendPositionPacket();
        yield return new WaitForSeconds(.1f);
    }

    yield return null;
}

SendPositionPacket() 메소드는 그냥, 서버에 자기 위치 담은 패킷 보내는거니까 신경 안쓰셔도 됩니다.
결론적으로는 저런식으로 로그를 한번 찍고 매번 저 코루틴을 통해서 위치를 보내기로 했었어요.

근데? 로그도 안찍히고 패킷도 안가고 아무튼 참 착잡했다는거지... 에러가 뜨는것도 아니고. 진짜 이거 이랬을때는 정신 나갈 것 같더라고요. 한시간동안 패닉에 빠져있었음.

해결 방법

결론부터 이야기하자면, WebSocket 에서 돌아가는 작업들은 전부 비동기 작업이고, 해당 작업마다 스레드를 따로 파서 작업이 돌아가고 있던겁니다. 이제부터 이 글에서는 얘를 웹소켓 스레드 라고 부르겠습니다.

그리고, 유니티의 코루틴은 유니티의 메인 스레드 에서 돌아가야 합니다. 라고 스택오버플로우가 가르쳐주더라고요.
그 글을 읽고나니 비동기 작업이라서 유니티 메인 스레드 대신 웹소켓 스레드에서 코루틴이 실행돼서 안됐던거구나! 싶은 생각이 들었습니다.

그래서 코드를 다음과 같이 고쳤습니다.

private bool established;

socketManager.AddOpenEvent((sender, e) =>
{
    print("Established!");
    established = true;
    //StartCoroutine(SendPositionInfinitely());
});

이런식으로, 연결이 됐을 때 established 라는 이름의 플래그를 메인 스레드에서 켜주기로 했습니다. 이런 다음 SendPositionInfinitely() 의 내용을 다음과 같이 바꿨습니다.

private IEnumerator SendPositionInfinitely()
{
    while (!established) yield return null;

    Debug.Log("Start coroutine");
    while (true)
    {
        SendPositionPacket();
        yield return new WaitForSeconds(.1f);
    }

    yield return null;
}

establishedtrue 가 되기 전까지는 계속 루프를 돌고, true가 된 시점부터는 0.1초에 한번씩 패킷을 보내기로 했습니다.

이런 방식으로 고치면, 웹소켓 스레드에서 코루틴이 실행되던걸 메인 스레드에서 실행되는 식으로 바꾸게 되고, 그로 인해 thread-safe 하게 코루틴을 실행시켜줄 수 있었습니다.

결국 이렇게 한 뒤부터는 잘 돌아가더라고요.

결론

오늘의 결론!

  1. UnityCoroutine 은 유니티 메인 스레드에서만 돌아가야 실행된다.
  2. 여러 스레드에 걸쳐 콜백 등을 이용하게 될 때는, bool 형식의 플래그 를 이용하는 방식으로 thread-safe 한 상태로 작업을 해줄 수 있다.
  3. 사실 이게 꼭 WebSocket 과 관련이 있는건 아니다!

메인스레드에서 돌려야 한다는 사실을 몰라서 대충 30분~1시간 정도를 왜 안되지? 하면서 엎드려 있었습니다. 힘들더라고요. 유니티 포럼은 무적이고 스택오버플로우는 신이다...

고등학생 게이ㅁ 개발자 김선민이었습니다. 감사합니다.

profile
22년 기준 글을 작성하지 않고 있습니다. 해당 블로그의 글은 학생 시절 공부하다 적은 내용이며 잘못된 정보가 있을 수 있음을 알려드립니다.

0개의 댓글