Node 강의/심화

1-6 유저 접속 관리 ( 점프 게임 )

kagan-draca 2024. 9. 27. 15:46

 

유저 접속 관리 : 유저가 서버에 접속을 하게 되는데 유저를 받아드리기 위한 과정

                              유저들의 정보를 메모리에 저장할 수 있다.

 

1. 유저가 접속할 때 서버가 받는 데이터

 

socketId 가 존재하는 이유는 현재 유저가 서버에 접속해 있다는 상태를 저장하기 위해서 사용 됩니다.

(같은 socketId 정보가 왔다 갔다 하는 것을 바탕으로 접속해 있다는걸 확인 할 수 있다.)

 

임시적인 것으로 연결이 끊기고 다시 연결되면 새로운 socketId가 생성 돼 

socketId로 유저를 특정하지 못 한다.

 

uuid는 서버가 유저에게 발급해줄 id 입니다.

유저를 특정할 수 있게 해준다. 

 

2. 핸들러를 통한 로직 처리

이벤트를 처리하는 함수를 핸들러라고 합니다.

앞으로 모든 이벤트는 handler를 통해서 처리할 예정입니다.

그 핸들러들을 서버에 등록해주는 함수입니다.

 

src(현재 존재 중)/handers 폴더를 만들어줍니다.

 

handers/register.handler.js 파일을 생성해줍니다.

(각종 handler를 서버에 등록하는 함수)

 

const regiserHandler = (io) => {
  io.on('connection', (socket) => {
    // 최초 커넥션을 맺은 이후 발생하는 각종 이벤트를 처리하는 곳
  });
  // io.on을 사용하면 'connection'이 발생할 때 까지
  // 소켓 객체가 대기하겠다는 의미
};

export default regiserHandler;

 

위와 같이 regiserHandler를 등록하기 위한 함수를 만들고,

 

io.on으로 connection이 들어오기 전 까지

소켓 객체가 대기하게 만들어줍니다.

 

소켓이 호출 돼야 할 위치는

 

src/init/socket.js의 initSocket 함수 입니다.

 

따라서 socket.js 파일에서

import registerHandler from '../handlers/register.handler.js';

핸들러를 등록하는 함수 import 해주고,

const initSocket = (server) => {
  const io = new SocketIO();
  // 소켓 IO 객체를 생성하고
  io.attach(server);
  //io.attach라는 메서드를 사용해 server에 연결해줍니다.
};

 

  registerHandler(io);
  //소켓을 등록해준다.

을 추가해줍니다.

 

변경된 코드 :

 

socket.js의 initSocket 함수

const initSocket = (server) => {
  const io = new SocketIO();
  // 소켓 IO 객체를 생성하고
  io.attach(server);
  //io.attach라는 메서드를 사용해 server에 연결해줍니다.

  registerHandler(io);
  //소켓을 등록해준다.
};

 

현재 우리는 유저 관리를 위한 작업 중 입니다.

유저가 서버에 접속하면 uuid(unique user id)를 생성해줘야 하고,

socket_id도 저장해줘야 합니다.

 

그럼 uuid와 socket_id를 저장하기 위한 .js가 필요해집니다.

 

src(현재존재합니다.)/models/user.model.js를 만들어줍니다.

 

user.model.js

 

에는 

const users = [];
//user들의 정보를 담을 배열

const addUser = (user)=>{
    users.push(user)
}

const getUser = ()=>{
    return users;
}

export {addUser, getUser}

 

users라는 유저들의 정보를 담을 배열을 만들고,

 

addUser로 유저가 서버에 접속하면 추가해주고,

 

getUser로 users 배열을 외부 스크립트에서 사용할 수 있게 만들어줍니다.

 

 

addUser에서 uuid를 생성해줘도 되지만, 이 작업은

handler가 작업하는 것이 더 바람직하기 때문에

다시 register.handler.js로 돌아갑니다.

 

register.handler.js

const registerHandler = (io) => {
  io.on('connection', (socket) => {
    // 최초 커넥션을 맺은 이후 발생하는 각종 이벤트를 처리하는 곳
 
  });
  // io.on을 사용하면 'connection'이 발생할 때 까지
  // 소켓 객체가 대기하겠다는 의미
};

에서 

 

유저 정보를 models/user.model.js에 저장하기 위해

 

import { addUser } from "../models/user.model";

 

addUser 함수를 import 해오고,

 

io.on함수 안에

  io.on('connection', (socket) => {
    // 최초 커넥션을 맺은 이후 발생하는 각종 이벤트를 처리하는 곳

    const userUUID = "1234"
    // 현재는 uuid(uer unique id)가 고정된 상태라 바꿔줘야 한다.
    addUser({uuid : userUUID, socketId : socket.id})
    // socketId는 socket.id로 받아온다.
  });

 

위와 같이 유저의 정보를 객체 형식으로 

addUser 함수의 매개변수로 전달해줍니다.

 

uuid(user unique id)를 만들기 위해 터미널에서

 

yarn add uuid

 

를 입력해줍니다.

 

그리고 현재(register.handler.js) 파일에

 

import { v4 as uuid } = from 'uuid'

( V4 형식으로 uuid를 생성해줍니다.)

(

 우리는 편리하게 사용하기 위해 as uuid를 붙여줘

  uuid로 사용할 수 있게 해줍니다.

)

를 import 해줍니다.

 

import { v4 as uuid } from 'uuid';

 

기존 코드인

    const userUUID = '1234';

    const userUUID = uuid();

로 바꿔줍니다.

 

추가 및 변경된 register.handler.js 전체 코드 : 

import { addUser } from '../models/user.model.js';
import { v4 as uuid } from 'uuid';

const registerHandler = (io) => {
  io.on('connection', (socket) => {
    // 최초 커넥션을 맺은 이후 발생하는 각종 이벤트를 처리하는 곳

    const userUUID = uuid();
    // v4 메서드를 바탕으로 uuid를 생성 및 담아준다.
    addUser({ uuid: userUUID, socketId: socket.id });
    // socketId는 socket.id로 받아온다.
  });
  // io.on을 사용하면 'connection'이 발생할 때 까지
  // 소켓 객체가 대기하겠다는 의미
};

export default registerHandler;

 

그런데 유저가 접속을 한 경우 무조건 로그아웃도 존재하기 때문에

user.model.js 안에 removeUser라는 함수를 만들어줍시다.

 

user.model.js에 removeUser 함수 만들기

// 유저가 접속 해제 할 때 호출되는 함수
const removeUser = (socketId) => {
  const index = users.findIndex((user) => user.socketId === socketId);
  // 접속을 종료 할려는 Client(유저)의 index를 가져옵니다.
  if (index !== -1) return users.splice(index, 1)[0];
  // index -1이 아닌 상황에서는 users.splice(index, 1)로 그 index에서 1개를 지워줍니다.
  // -> 그 유저 정보 삭제
  // 그리고 남은 users 배열을 return 해줍니다.
  // 만약 -1이라면, 해당 유저가 users 배열에 없는 대상이라(사실 오류인 상황)
};

 

접속을 종료 할려는 Client(유저)의 index를 가져옵니다.

 

그 index를 바탕으로 해당 유저를 지운 나머지 users 배열을 가져옵니다.

 

그리고 removeUser 함수가 사용될 핸들러를 등록해보겠습니다.

 

register.handler.js에 removeUser 함수 추가하기

 

접속 종료는 "connection"이후에 접속 종료가 돼야 하기 때문에

 

io.on('connection', (socket) =>{

 

}) 

 

안에 맨 마지막 부분에 작성해야 합니다.

 

  io.on('connection', (socket) => {
    // 최초 커넥션을 맺은 이후 발생하는 각종 이벤트를 처리하는 곳

    const userUUID = uuid();
    // v4 메서드를 바탕으로 uuid를 생성 및 담아준다.
    addUser({ uuid: userUUID, socketId: socket.id });
    // socketId는 socket.id로 받아온다.

    //접속 해제시 이벤트
    socket.on('disconnect', ()=>{});
  });

 

우리는 disconnect 될 disconnection 함수가 필요하게 됩니다.

 

우린 handlers/helper.js 스크립트를 만들어 안에 disconnection 함수를 만들어보겠습니다.

 

handlers/helper.js에 Disconnect 함수 만들기

import { getUser, removeUser } from '../models/user.model.js';

const handleDisconnect = (socket, uuid) => {
  removeUser(socket.id);
  console.log(`User disconnected : ${socket.id}`);
  console.log(`Current users : `, getUser());
};

export { handleDisconnect };

 

helper.js에서 

 

import로 getUser와 removeUser를 가져와

매개 변수로 받아온 socket.id를 이용해

유저를 제거해주고, 남은 유저들의 정보를 출력해줍니다.

 

register.handler.js에 removeUser로 돌아와

    //접속 해제시 이벤트
    socket.on('disconnect', (socket)=>);

    //접속 해제시 이벤트
    socket.on('disconnect', (socket) => handleDisconnect(socket, userUUID));

으로 변경해 handleDisconnect함수로 socket을 매개변수로 보내주고,

userUUID도 보내줍니다.(현재는 사용하는 부분이 없지만 향후 필요할 수 있다)