Skip to content

7주차 실시간 게임에서 유저 이탈 상황 처리하기

Mosball edited this page Dec 21, 2019 · 1 revision

이번에 웹으로 실시간성 게임을 처음 구현해보면서 유저 이탈에 대한 예외 상황이 생각보다 굉장히 많다는 것을 알게 되었습니다. 프로젝트 초기에는 테스트 중 발견한 예외 상황이나, 우리가 예측한 예외 상황에 대해서만 그때그때 예외 상황을 처리할 수 있는 코드를 추가하는 방식으로 작업을 했었습니다. 하지만, 프로젝트가 진행되면서 로직의 규모가 커지고 동일한 이탈 상황에서도 진행되고 있는 게임의 상태에 따라 각각 다른 예외 처리를 해줘야 했기 때문에 게임 진행 상태에 대한 명확한 정의가 필요했습니다. 먼저 저희는 게임 진행 상태와 흐름을 아래와 같이 정의했습니다.


게임 진행 흐름

game activity diagram


게임의 상태 정의가 완료되었다.

이제 게임이 시작되는 시점부터 종료되는 시점까지 6가지의 상태가 명확하게 정의되었고, 각각의 상태에서 유저의 이탈이 발생했을 때 어떤 식으로 예외 처리를 해줄지 생각해봐야 했습니다. 처음에는 각각의 상태별로만 예외 처리를 해주면 될 줄 알았는데, 여기서 또 한 가지 문제가 발생했습니다. 바로 이탈하는 유저가 출제자도전자냐에 따라서 다른 예외 처리가 필요한 것이었습니다.

출제자 : 선택한 단어를 영상 송출을 통해 몸으로 표현하는 사람
도전자 : 출제자가 몸으로 표현하는 것을 보고 어떤 단어인지 맞추는 사람

예를 들어서 세트가 진행되고 있는 중에 출제자가 이탈할 경우 더 이상 세트 진행이 불가능 해져서 즉시 세트를 종료해줘야 했고, 여러 도전자 중 한 명의 도전자가 이탈한 상황이라면 그대로 세트를 진행해도 됐습니다. 그렇기에 저희는 유저 이탈 상황에 대해서 게임의 진행 상태와 이탈한 유저가 출제자/도전자 중 어떤 유저인지를 고려하며 각 상황에 대해 예외 처리를 어떤 식으로 할지 생각해보게 되었습니다.


게임 진행 상태에 따른 예외 처리

상태 명 이탈 유저가 "출제자"일 때 처리 이탈 유저가 "도전자"일 때 처리
Waiting 유저가 이탈한 뒤에 남은 유저가 모두 레디 상태라면 게임 시작
Connecting 출제자없이는 세트 진행이
불가능하기 때문에 세트 종료 후 다음 세트 진행
방에 남은 인원이 2명 이상일 경우 그대로 세트 진행.
방에 남은 인원이 1명일 경우 더이상
게임 진행이 불가능하기 때문에 게임 종료
Initializing
Playing
Score Sharing 다음 세트를 진행할 수 없는 상황이라면 바로 게임 종료
Ending 처리할 사항 없음

예외 처리에 대한 정리를 마쳤다. 근데 어떻게 적용시키지?

위에서 각각의 유저 이탈 상황에 대해 어떤식으로 처리해줄지는 정리가 끝났습니다. 하지만 위 생각을 코드로 옮기는 일이 쉬운일은 아니였습니다. 많은 이탈 상황(뒤로가기, 새로고침, 브라우저 종료등등)들이 존재 했고, 각각의 이탈 상황을 감지하는 핸들러마다 위의 처리 코드를 넣어야 했기 때문입니다.

무언가 통합적으로 이탈 상황을 처리할 수 있는 방법이 필요했습니다. 그리고 답은 socket에서 찾을 수 있었습니다. 게임에 참여하는 모든 유저는 기본적으로 서버와의 socket연결을 유지했고, 유저가 이탈하면 socket의 disconnecting이벤트가 호출되었습니다. 한가지 신경을 써준 부분은 저희 웹 사이트가 spa로 동작했기 때문에 게임 진행중에 다른 페이지로 이동해도 socket 연결이 끊어지지 않았습니다. 이때는 클라이언트 쪽에서 socket.disconnect()를 호출 해줘서 임의로 socket연결을 끊도록 만들었습니다. 그리고 서버의 disconnecting이벤트를 처리하는 핸들러에 이탈 상황을 통합적으로 처리하는 코드를 넣었습니다.


switch (roomStatus) {
  case GAME_STATUS.WAITING:
    if (
      gameManager.checkAllPlayersAreReady() &&
      gameManager.getPlayers().length >= MIN_PLAYER_COUNT
    ) {
      gameController.prepareGame(gameManager, timer);
    }
    break;

  case GAME_STATUS.CONNECTING:
  case GAME_STATUS.INITIALIZING:
  case GAME_STATUS.PLAYING:
    if (!gameManager.isSetContinuable()) {
      gameController.repeatSet(gameManager, timer);
    }
    break;

  case GAME_STATUS.SCORE_SHARING:
    if (!gameManager.isNextSetAvailable()) {
      gameController.goToEnding(gameManager, timer);
    }
    break;
  default:
    break;
}

실시간성 게임이기 때문에 유저가 이탈했을 때 어떻게 처리를 하냐가 굉장히 중요했고, 가장 많은 버그를 발생 시킨 부분 또한 유저 이탈 상황이었습니다. 많은 고민 끝에 게임을 진행하면서 발생하는 모든 유저 이탈 상황은 disconnecting핸들러에서 통합적으로 처리하도록 설계를 했고, 이로써 새로운 유저 이탈 상황이 생겨도 클라이언트 쪽에서는 socket.disconnect()를 호출해주고, 서버쪽에서는 disconnecting핸들러만 조금 손봐준다면 쉽게 대응이 가능하게 되었습니다.

현재까지 발견한 유저 이탈 상황

  1. 브라우저 새로고침
  2. 브라우저 종료
  3. 탭 종료
  4. 뒤로가기 버튼 클릭
  5. 나가기 버튼 클릭(메인 페이지로 이동)
  6. 네트워크 끊김
Clone this wiki locally