[Project_] R - 서버 설계 (Session)

2025. 2. 11. 02:44·Project_xxx

Redis를 사용해서 세션관리

● Redis는 key-value 구조의 저장소, 각 유저의 고유 식별자를 key로 사용하고

   값은 Hash 형식으로 저장

서버 구조가 위와 같을 때 예시 ▼

 

 - session:{user_id} : 유저별 세션 정보 관리

 - Hash 구조 예시

{
  "userId": "test",
  "nickname": "player1",
  "loginTime": "2025-02-11T00:00:00Z",
  "currentServer": "Town1",
  "status": "online"
}

 - 신경 써야할 부분은 'currentServer'

 - 현재 설계한 서버의 형태로는 Town 서버가 여러 개 존재하기 때문에 현재 위치한 서버를 명시해야 한다.

 - 'currentServer'를 명시해줌으로써 현재 어디에 있는지 정확하게 알 수 있다.

 

로그인은 MySQL로

● 클라이언트 UI에서 입력한 로그인 데이터 패킷을 서버로 보내면, 서버에서 MySQL에 해당 사용자를 조회하여

   인증을 진행

 

● 인증이 성공하면 Redis에 로그인 정보에 부합한 사용자를 Redis에 세션 정보 저장

 

● Redis TTL(Time-To-Live) 설정하여 일정 시간 동안만 정보 유지 및 삭제

const redis = require("redis"); // Redis를 사용하기 위한 모듈 import
const client = redis.createClient(); // Redis 연결
const moment = require("moment");  // 시간을 보기 좋게 만들어주는 모듈

async function createSession(userId, username) {
  const sessionKey = `session:${userId}`;
  const sessionData = {
    userId,
    username,
    loginTime: moment().toISOString(),
    currentServer: "Town1", // 초기 접속 서버
    status: "online"
  };

  // Redis에 세션 저장 (TTL 설정: 1시간)
  await client.hSet(sessionKey, sessionData);
  await client.expire(sessionKey, 3600); // TTL 설정 (초 단위)
}

  - MySQL 데이터와 일치하여 로그인에 성공했을 때 사용

  - session에 추가되었다는 것은 게임에 접속했다는 뜻임으로 status를 online으로 설정

 

● userSessions를 조회하고자 한다면

async function getSession(userId) {
  const sessionKey = `session:${userId}`;
  
  // Redis에서 세션 정보 가져오기
  const sessionData = await client.hGetAll(sessionKey);
  
  return sessionData;
}

 - hGetAll : h는 Hash를 의미. 즉, Hash에서 GetAll 모두 가져와!

 - 세션에 접속한 모든 유저를 조회

 

● 접속을 종료하거나 클라이언트가 비정상 종료되었을 때 사용할 함수

async function deleteSession(userId) {
  const sessionKey = `session:${userId}`;
  
  // Redis에서 세션 삭제
  await client.del(sessionKey);
}

 - del은 이름에서 유추하듯이 delete를 줄인 것, 삭제하는 메서드이다.

 

 

흐름을 정리하자면

● 로그인을 했다

   1) 클라이언트 로그인 요청

   2) MySQL에서 인증

   3) 인증 성공 시 Redis에 세션 생성

    → 초기 상태 : 'status : online', 'currentServer : Town1'

 

● 서버 이동 (Town1에서 Dungeon으로 이동)

   1) 클라이언트가 던전 UI에서 버튼을 클릭해서 이동 요청을 함

   2) Manager Server에서 이동 처리 (Town 1 → Dungeon 1)

   3) 'currentServer : Dungeon1' 상태 변경!

 

● 접속 상태를 확인하려면

   1) Manager Server가 Redis에 userId value를 사용해서 특정 사용자의 상태('status')와

       위치('currentServer')를 조회하고 관리

   2) 접속한 사용자를 확인하는 UI가 있다면?

 

● 로그아웃 요청 시

   1) 클라이언트 로그아웃 요청 → Manager Server에서 Redis 해당 세션 삭제

 

던전에서 게임이 팅겼어요!

● 던전에서는 다른 로직을 사용해서 추가적인 데이터를 저장하는 key-value로 설계

  ○ 어떤 데이터가 추가적으로 필요한가

    - 마지막으로 있던 위치 = 가장 중요할 듯 함 

    - 클라이언트가 종료되던 시점의 스탯, 아이템 = 패널티로 초기부터 시작하도록 해도 되고...

 

● Hash 구조 예시

{
  "userId": "test",
  "username": "player1",
  "currentDungeon": "Dungeon1",
  "position": { "x": 100, "y": 200, "z" : 0 },
  "hp": 80,
  "mp": 50,
  "lastUpdateTime": "2025-02-11T00:58:00Z"
}

 

●  코드로 작성

async function saveSession(userId, dungeonId, position, hp, mp) {
  const sessionKey = `session:${userId}`;
  const sessionData = {
    userId,
    currentDungeon: dungeonId,
    position: JSON.stringify(position),
    hp,
    mp,
    lastUpdateTime: moment().toISOString()
  };

  // Redis에 세션 정보 저장
  await redisClient.hSet(sessionKey, sessionData);

  // 해당 Dungeon의 플레이어 목록에 추가
  await redisClient.sAdd(`dungeon:${dungeonId}:players`, userId);
}

  - hset : HashSet

  - sAdd : set Add

 

● 클라이언트가 재접속했을 때

  - 클라이언트에서 Notice창을 띄어줌으로써 재접속했음을 서버에 알려줘야 한다.

  - 서버가 재접속 패킷을 받았다면 던전에 다시 배치해주는 로직이 필요하다.

async function reconnectSession(userId) {
  const sessionKey = `session:${userId}`;

  // Redis에서 세션 정보 조회
  const sessionData = await redisClient.hGetAll(sessionKey);
  
  if (!sessionData) {
    throw new Error("세션 정보 없음");
  }

  // 유저를 마지막 접속했던 Dungeon으로 재연결
  const { currentDungeon, position, hp, mp } = sessionData;
  
  return {
    dungeon: currentDungeon,
    position: JSON.parse(position),
    hp: parseInt(hp),
    mp: parseInt(mp)
  };
}

 

근데... 아이템은?

● 따로 던전 내에서 먹은 아이템은 JSON에 저장해서 사용

[
  {
    "itemId": "item1",
    "name": "한손검",
    "type": "Weapon",
    "attributes": {
      "atk": 10,
      "def": 0,
      "magic": 0,
      "speed": 0,
    }
  }, 
  {
    "itemId": "item1",
    "item_name": "흰색 무지 반팔",
    "item_type": "Armor",
    "item_attributes": {
      "atk": 0,
      "def": 5,
      "magic": 0,
      "speed": 1,
    }
  }, 
]

 

- key 구조 : dungeon:{dungeonId}:items:{userId}

  → 사용자가 특정 던전에서 소유한 아이템 리스트

 

● 아이템을 획득했을 때

async function addItemToDungeon(userId, dungeonId, item) {
  const key = `dungeon:${dungeonId}:items:${userId}`;
  
  // 기존 아이템 리스트 가져오기
  let items = await redisClient.get(key);
  items = items ? JSON.parse(items) : [];
  
  // 새로운 아이템 추가
  items.push(item);
  
  // Redis에 업데이트
  await redisClient.set(key, JSON.stringify(items));
}

 

● 위치, 스탯을 재접속 시 가져왔을 때와 동일하게 세션 확인 후 데이터를 복구

async function reconnectToDungeon(userId, dungeonId) {
  const key = `dungeon:${dungeonId}:items:${userId}`;
  
  // Redis에서 기존 데이터 로드
  const items = await redisClient.get(key);
  
  if (!items) {
    throw new Error("이전에 저장된 세션 데이터가 없습니다.");
  }

  return JSON.parse(items);
}

 

● 게임 설계 상 무기, 방어구 등은 던전에서만 사용하기 때문에 저장되던 key들은 던전 종료 시 삭제

async function leaveDungeon(userId, dungeonId) {
  const key = `dungeon:${dungeonId}:items:${userId}`;
  
  // Redis에서 데이터 삭제
  await redisClient.del(key);
}

 

 

Redis를 설계하면서 생각하게 된 것들은...

● Redis는 확실히 캐시 데이터를 저장하기에 좋다.

 

● TTL 설정이 있기에 자동으로 세션이 삭제되기에 메모리 관리가 좋다.

 

● Redis에서도 JSON 형태로 저장해서 사용한 다음 삭제할 수 있다는 것을 알게 되었다.

 

'Project_xxx' 카테고리의 다른 글

[Project_] R - DB 사용 방식과 Redis 사용 방향  (0) 2025.02.21
[Project_] R - 로그인, 캐릭터 생성에 대한 고민  (0) 2025.02.14
[Projcet_] TDO - Trouble Shooting  (0) 2025.01.31
[Project_] MG - Trouble Shooting  (0) 2025.01.16
[Project_] TD - Final  (0) 2025.01.02
'Project_xxx' 카테고리의 다른 글
  • [Project_] R - DB 사용 방식과 Redis 사용 방향
  • [Project_] R - 로그인, 캐릭터 생성에 대한 고민
  • [Projcet_] TDO - Trouble Shooting
  • [Project_] MG - Trouble Shooting
wakelight23
wakelight23
했던 공부를 기록하는 곳
  • wakelight23
    wakelight23 공부기록
    wakelight23
  • 전체
    오늘
    어제
    • 분류 전체보기 (110)
      • Programming Language (26)
        • Error Note (1)
        • HTML, CSS (3)
        • Javascript 공부기록 (5)
        • SQL 문제풀이 (4)
      • Develop Diary (34)
        • Interview (20)
        • TheFourFs (13)
      • Project_xxx (18)
      • IT (32)
        • DB (0)
        • Hardware (3)
        • Network (10)
        • Server (11)
        • OS (4)
        • 게임수학 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    today i leraned
    weekly i learned
    Today I learned
    Til
    Wil
    KPT회고
    today i learnd
    today i lerned
  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
wakelight23
[Project_] R - 서버 설계 (Session)
상단으로

티스토리툴바