dh_0e

[Server] 세션(Session)과 인터벌 관리자(Interval Manager)의 차이 및 사용 (with Node.js) 본문

내일배움캠프/Server

[Server] 세션(Session)과 인터벌 관리자(Interval Manager)의 차이 및 사용 (with Node.js)

dh_0e 2024. 7. 5. 21:11
세션과 인터벌 관리자, 용어로만 봤을 때, 비슷해 보이는 두 개념에 대한 이해가 부족하여 강의를 듣다 정리했는데 완전히 다른 두 개념에 놀라 글을 작성한다. 사실 비교하기도 웃길만큼 다른 두 개념..

세션 (Session)

 

목적

  • 사용자의 상태 및 정보를 관리
  • 사용자가 로그인했을 때 생성되고, 로그아웃하거나 일정 시간 동안 활동이 없을 때 만료됨

주요 기능

  • 사용자 인증 상태를 유지
  • 사용자와 관련된 데이터를 저장하고 관리 (ex. 사용자 프로필, 설정, 게임 진행 상황 등).
  • 세션 타임아웃을 설정하여 일정 시간 동안 활동이 없을 경우 세션을 만료시킴

ex)

  • 사용자가 게임에 로그인했을 때, 세션을 생성하여 사용자의 상태를 유지
  • 사용자가 로그아웃하거나 일정 시간 동안 활동이 없을 경우 세션을 만료시키고 사용자 데이터를 정리

 

인터벌 관리자 (Interval Manager)

목적

  • 일정 시간 간격으로 반복적으로 실행해야 하는 작업을 관리
  • 주기적으로 수행해야 하는 작업을 설정하고, 이를 관리

주요 기능

  • 특정 간격으로 콜백 함수를 실
  • 각 사용자가 개별적으로 주기적인 작업을 수행할 수 있도록 관리 (ex. 위치 업데이트, 게임 상태 체크 등).
  • 특정 사용자의 인터벌 작업 추가, 제거, 수정을 관리

ex)

  • 사용자가 게임에서 이동할 때 주기적으로 위치 업데이트를 서버에 전송
  • 서버에서 일정 간격으로 게임 상태를 체크하고 업데이트
  • 사용자가 게임에서 로그아웃할 때, 해당 사용자의 모든 인터벌 작업을 정리

 

정리

특징 세션(Session) 인터벌 관리자(Interval Manager)
목적 사용자의 상태 및 정보 유지 일정 시간 간격으로 작업 수행
주요 기능 사용자 인증 상태 관리, 사용자 데이터 저장 주기적인 작업 설정 및 관리, 콜백 함수 실행
사용 예 로그인/로그아웃 상태 유지, 사용자 데이터 관리 위치 업데이트, 게임 상태 체크
관리 대상 사용자 상태 및 정보 주기적인 작업 (인터벌)
  • 세션은 주로 서버 측에서 관리되고, 사용자 상호작용을 추적하고 인증하는 데 사용됨
  • 반면에 인터벌 관리자는 클라이언트나 서버의 특정 작업을 정기적으로 실행하거나 관리하는 데 사용된다!

 

"세션"이라는 용어는 클라이언트의 상태 관리를 의미하며,
"인터벌 관리자"는 코드 실행 간격 관리를 의미하는 것

Image by ChatGPT


Node.js 사용 예시

  • 세션과 인터벌 관리자를 함께 사용하여 유저의 세션을 관리하고, 주기적인 작업을 설정

세션 setting

sessions.js: User, Game 클래스로 생성된 객체를 담을 userSessions, gameSessions 배열 선언

// sessions.js

export const userSessions = [];

export const gameSessions = [];

 

user.session.js: userSession에 user 정보를 추가, 삭제 및 수정

// user.session.js

import User from '../class/model/user.class.js';
import { userSessions } from './session.js';

export const addUser = (socket, userId) => {
  const user = new User(socket, userId);
  userSessions.push(user);
};

export const removeUser = (socketOrUserId) => {
  let index;
  if (typeof socketOrUserId === 'object') {
    index = userSessions.findIndex((user) => user.socket === socketOrUserId);
  } else {
    index = userSessions.findIndex((user) => user.userId === socketOrUserId);
  }
  userSessions.splice(index, 1);
};

export const editUser = (socket, replacedId) => {
  removeUser(socket);
  addUser(socket, replacedId);
};

export const getUser = (socketOrUserId) => {
  if (typeof socketOrUserId === 'object') {
    return userSessions.find((user) => user.socket === socketOrUserId);
  } else {
    return userSessions.find((user) => user.userId === socketOrUserId);
  }
};

export const getAllUserSessions = () => {
  return userSessions;
};

export const clearSession = () => {
  userSessions.splice(0, userSessions.length);
};

 

game.session.js: gameSession에 game 정보를 추가, 삭제 및 수정

// game.session.js

import Game from '../class/model/game.class.js';
import { gameSessions } from './session.js';

export const addGame = (socket, gameId) => {
  const session = new Game(gameId, socket);
  gameSessions.push(session);
};

export const removeGame = (socketOrGameId) => {
  let index;
  if (typeof socketOrGameId === 'object') {
    index = gameSessions.findIndex((game) => game.socket === socketOrGameId);
  } else {
    index = gameSessions.findIndex((game) => game.gameId === socketOrGameId);
  }
  gameSessions.splice(index, 1);
};

export const editGame = (socket, replacedId) => {
  removeGame(socket);
  addGame(socket, replacedId);
};

export const getGame = (socketOrGameId) => {
  if (typeof socketOrUserId === 'object') {
    return gameSessions.find((game) => game.socket === socketOrGameId);
  } else {
    return gameSessions.find((game) => game.gameId === socketOrGameId);
  }
};

export const getAllGameSessions = () => {
  return gameSessions;
};

export const clearSession = () => {
  gameSessions.splice(0, gameSessions.length);
};

 

 

인터벌 관리자 setting

interval.manager.js: addUser에서 userId를 key값으로 하는 value는 또 Map 객체이며 type을 key값으로 하는 value에 setInterval 함수가 반환한 intervalId가 저장됨

// interval.manager.js

class IntervalManager {
  constructor() {
    this.intervals = new Map();
  }

  // userId가 key값으로 있는 value에 Map을 만들어 type을 key값으로 갖는 value에 Interval 생성(callBack 함수 interval 마다 실행)
  addUser(userId, callBack, interval, type = 'user') {
    if (!this.intervals.has(userId)) {
      this.intervals.set(playerId, new Map());
    }
    this.intervals.get(playerId).set(type, setInterval(callBack, interval));
  }

  // userId로 저장된 모든 데이터 삭제
  removeUser(userId) {
    if (this.intervals.has(userId)) {
      const userIntervals = this.intervals.get(playerId);

      // forEach에 map 객체를 넣으면 첫 번째 인자에 value값이 들어가므로
      // intervalId에 setInterval함수가 반환한 ID가 들어가 이를 clearInterval 시키는 원리
      userIntervals.forEach((intervalId) => {
        clearInterval(intervalId);
        this.intervals.delete(playerId);
      });
    }
  }

  // userId로 저장된 interval중 type이 key값으로 저장된 interval들을 삭제
  removeInterval(userId, type) {
    if (this.intervals.has(userId)) {
      const userIntervals = this.intervals.get(userId);
      if (userIntervals.has(type)) {
        clearInterval(userIntervals.get(type));
        userIntervals.delete(type);
      }
    }
  }

  clearAll() {
    // 마찬가지로 userIntervals에 value인 map 객체가 들어가고
    this.intervals.forEach((userIntervals) => {
      // intervalId에는 key 값인 type 대신 value 값인 setInterval함수가 반환한 ID가 들어감
      userIntervals.forEach((intervalId) => clearInterval(intervalId));
    });
    this.intervals.clear();
  }
}

export default IntervalManager;
  • 이제 User나 Game class에 IntervalManager를 추가하여 주기적인 작업을 실행할 수 있으며 그 class를 session에 넣어 한꺼번에 관리할 수 있다!