CH5_멀티 플레이 과제 - 트러블슈팅

2024. 11. 1. 10:43[Node.js_6기 본캠프 TIL]

🚨 유저를 찾을 수 없습니다

 

게임 접속과 동시에 밀려드는 로그 속, 시선을 사로잡는 User not found. TCP 멀티 플레이 강의 중에 만들었던 코드를 참고해보기도 하고, 오타가 있는 것인가 싶어 한참을 뜯어봐도 문제가 해결되지 않았다.

 

user가 users 배열에서 사라지는 것은 접속을 종료할 때에만 기능하도록 구현해두었는데, 관련 파일들을 처음부터 끝까지 아무리 살펴봐도 문제가 없었다.

 

// userSession.js

export const removeUser = async (socket) => {
  const index = userSessions.findIndex((user) => user.socket === socket);
  if (index != -1) {
    const user = userSessions[index];
    await updateUserLocation(user.x, user.y, user.id);
    return userSessions.splice(index, 1)[0];
  }
};
// onEnd.js

export const onEnd = (socket) => async () => {
  console.log('클라이언트 연결이 종료되었습니다.');

  await removeUser(socket);

  const gameSession = getGameSession();
  gameSession.removeUser(socket);
};

 

 

그렇다면 유저가 아예 users 배열에 등록되지 않는 문제인가? addUser나 getUser 함수를 아무리 봐도, 오작동할 구석이 없는 단순한 구조였다.

 

// gameClass.js

class Game {
  constructor(id) {
    this.id = id;
    this.users = [];
    this.latencyManager = new LatencyManager();
  }

   addUser(user) {
    this.users.push(user);

    this.latencyManager.addUser(user.id, user.ping.bind(user), 1000);
   }
   
   getUser(userId) {
    return this.users.find((user) => user.id === userId);
   }
   
  }

 

console.log를 찍어보고 원인을 찾아내는 것이 아직 어려웠기 때문에, 오류 코드를 보고 고치기의 무한 반복이었다. User not found니까, class User까지 뜯어보려던 순간 튜터님들의 도움을 받을 수 있었다.

 

우선 user가 users에 들어간 뒤에 사라지는 것인지, 아예 처음부터 들어가지 않는 것인지 확인하기 위해 서버 접속 시 초기 세팅이 진행되는 initialHandler.js부터 살펴보기로 했다. 

 

// initailHandler.js

	user = new User(socket, deviceId, playerId, latency, coords);
    
    console.log(gameSession)
    console.log(user)

    addUser(user);
    const gameSession = getGameSession();
    gameSession.addUser(user);
    
    console.log(`after`, gameSession)
    console.log(`after`, user)

 

같은 값을 다른 시점에 호출하고 있기 때문에, 데이터를 구분하기 위해 백틱을 활용했다. 유저가 막 생성되었을 때의 로그와 users 배열에 넣고난 뒤의 로그를 구분하여 확인한 결과, user는 생성됨과 동시에 삭제되고 있었다. 

 

기이한 현상에 고민하던 중, '클라이언트 연결이 종료되었습니다' 라는 로그가 에러 메세지와 같은 빈도로 나오고 있는 것을 발견할 수 있었다. 

 

// onConnection.js

export const onConnection = (socket) => {
  console.log(`Client connected from: ${socket.remoteAddress}:${socket.remotePort}`);

  socket.buffer = Buffer.alloc(0);

  socket.on('data', onData(socket));
  socket.on('data', onEnd(socket));
  socket.on('data', onError(socket));
};

 

 

정말 놀랍고도, 슬프게도.... 첫 단추를 잘못 꿰어놓은 것을 DB를 연결할 때가 되어서야 깨닫게 된 것이다. TCP의 socket.on 방식이 웹소켓이랑 어떻게 다른지 공부해놓고, 실제 코드에서는 ctrl+c / ctrl+v를 한 뒤 함수 부분만 수정하고 'data' 신호에 연결과 종료와 에러를 동시에 호출하는 엄청난 짓을 저지르고야 말았다. 

 

export const onConnection = (socket) => {
  console.log(`Client connected from: ${socket.remoteAddress}:${socket.remotePort}`);

  socket.buffer = Buffer.alloc(0);

  socket.on('data', onData(socket));
  socket.on('close', onEnd(socket));
  socket.on('error', onError(socket));
};

 

 

위와 같이 코드를 수정하는 순간, 그토록 애타게 찾던 User의 로그를 볼 수 있었다.

 

정상적으로 출력되는 User

 

 

 

코드 전반의 흐름을 파악하고, 적절한 위치에 console.log를 찍어 트러블슈팅을 하는 능력이 정말 중요하다는 것을 배운 시간이었다. 부디 빠른 시일 내에 트러블 슈팅 능력이 향상되기를 바라며...