(TIL) 2025-02-19 FINAL(12)
분산 서버 작업 - 3일차
클라이언트로부터 어떤 요청을 받았을 경우 게이트 웨이 서버가 각 서버에 전달을 하고 결과를 받아오고 다시 클라이언트로 응답을 하기 위해서는 요청한 클라이언트(유저)의 소켓을 관리를 해야하는데 로그인 전에는 그 유저에 대한 정보를 특정할 수가 없어 어떻게 처리를 할지 고민을 하다보니 왜 로그인 서버를 따로 두는지 이해가 가기 시작했다. 로그인(인증 서버)를 별도로 두어 로그인이 되었을 때 해당 유저에 대한 정보를 정확하게 유저 아이디나 기타 정보로 유저가 연결을 종료할때까지 유지할 수 있으니 관리 편리상 따로 두는 것을 이해할 수 있었다.
그리하여 로그인 서버까지 따로 별도로 만들면 작업이 많아지니 우선은 게이트 웨이서버에서 인증기능을 처리하기로 하였다.
게이트 웨이 서버 작업
1
2
3
4
5
6
7
8
9
10
11
const onConnection = async (socket) => {
console.log('클라이언트가 연결되었습니다:', socket.remoteAddress, socket.remotePort);
socket.buffer = Buffer.alloc(0);
socket.on('data', onData(socket));
socket.on('end', onEnd(socket));
socket.on('error', onError(socket));
// id정보 없는 유저 생성
userSession.addUser(socket);
};
클라이언트와 연결이 되면 소켓을 키값으로 유저 세션에 등록처리를하여 소켓을 관리하기로 했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// header 생성
const packetTypeBuffer = Buffer.alloc(2);
// 생략
//유저 정보 추가
const userIdBuffer = Buffer.from('' + userId);
const userIdLengthBuffer = Buffer.alloc(1);
userIdLengthBuffer.writeUInt8(userIdBuffer.length);
const payloadLengthBuffer = Buffer.alloc(4);
payloadLengthBuffer.writeUInt32BE(payloadBuffer.length);
const packet = Buffer.concat([
packetTypeBuffer,
versionLengthBuffer,
versionBuffer,
userIdLengthBuffer,
userIdBuffer,
payloadLengthBuffer,
payloadBuffer,
]);
그리고 로그인을 하게되면 앞으로 각 서버에 패킷을 전달할때 패킷 헤더에 유저ID값을 담아 전송하게되며 다시 클라이언트로 응답 패킷을 보낼 때 서버에서 온 패킷의 유저ID 값을 디코딩하여 해당 유저의 소켓을 찾아 전송하는 방법으로 진행하였다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 로비서버 핸들러
const onLobbyServerHandler = ({ socket, payload, packetType }) => {
// 유저 객체 조회
const user = userSession.getUser(socket.id);
if (!user || !user.id) {
throw new CustomError('유저 정보가 없습니다.');
}
if (user.getGameState()) {
throw new CustomError(`올바르지 못한 요청입니다. (USER ID: ${user.id})`);
}
const packetInfo = Object.values(config.packetType).find(([type, name]) => type === packetType);
const packet = makeServerPacket(packetInfo, payload, user.id);
const serverSocket = serverSession.getServerById(config.server.lobbyServer);
serverSocket.write(packet);
};
각 서버로 패킷을 생성하는 메소드를 만들어 각 서버 핸들러별로 로직을 구현작업을 마무리하였다.
로비서버 작업
게이트웨이 서버에서 로그인을 성공하게되면 로비서버에서도 유저관리를 위해 동기화 처리를 해줘야 하니 별도로 로비서버에 패킷을 전송하여 로비서버에도 유저세션 정보를 추가할 수 있는 핸들러를 만들었다.
1
2
3
4
5
6
7
8
9
10
11
const loginCastHandler = ({ socket, payload }) => {
const { user, success } = payload;
if (!user.userId || !user.name || !success) {
throw new CustomError('올바르지않은 요청입니다.');
}
userSession.addUser(user.userId, user.name, socket);
console.log('들어간 유저들' + userSession.getUserIds());
};
테스트
그리고 기존에 로비서버의 프로세스들은 구현이 되어있으니 패킷전송하는 부분만 분산서버에 맞게 수정을 하여 더미클라이언트를 통해서 로그인 -> 방생성 -> 게임 시작까지 잘 패킷이 서로 주고 받는지 테스트를 해보았다.
로그인 클라이언트에서 로그인을 시도하게되면 게이트웨이 서버에서 DB로 로그인 정보를 조회하여 로그인 처리를 하고 로비서버에 정상적으로 유저정보를 보낸 것을 로비 서버 로그(1005번)을 통해 확인이 가능했다.
방 생성 로비 서버 로그를 보면 packetType 2001 로그가 생성된 걸 보면 정상적으로 게이트웨이 서버에서 로비서버로 전송된 것을 알 수 있었고 생성 로직 또한 방이 생성되었다는 로그를 통해 알 수 있었다.
게임 시작 게임 시작 요청을 보내면 게이트웨이 서버가 로비 서버에 시작 요청을 보내고 로비 서버에서 유저와 룸 상태 정보를 변경하는 로직 처리를 한다. 그리고 게임 서버에서 게임 세션을 만들기 위한 룸 세션 정보를 게임 서버로 전송을 하게된다. 아직 게임 서버에서의 작업은 만들지는 않았고 정상적으로 로비 서버 > 게이트 웨이 서버 > 게임 서버로 패킷을 전달하고 게임 서버에서 패킷이 도착한 결과까지 오늘 확인 할 수 있었다.
이것으로 클라이언트 - 게이트 웨이 - 로비 - 게임 서버까지 패킷이 잘 전달되고 있다는 것을 확인 할 수 있었다. 어제 오늘로 게이트웨이 , 로비 서버에 대한 작업을 완성하였고 내일은 게임 서버를 구조에 맞게 변경을 할 예정이다.

