Skip to content

feat(be): implement participant presence in study rooms#3541

Open
westsunh wants to merge 80 commits into
mainfrom
t2640-study-group-2depth-socket
Open

feat(be): implement participant presence in study rooms#3541
westsunh wants to merge 80 commits into
mainfrom
t2640-study-group-2depth-socket

Conversation

@westsunh
Copy link
Copy Markdown

@westsunh westsunh commented Apr 25, 2026

Description

Study Room(Study Group 2depth)에서 사용자의 실시간 출입 상태 처리 기능을 구현합니다.
WebSocket(Socket.IO) + Redis + BullMQ 기반으로 입장/퇴장/재연결/세션 종료를 처리합니다.

Additional context

인프라 설정

  • @libs/redis: Redis 모듈 및 RedisIoAdapter 추가
  • main.ts: RedisIoAdapter 등록 (멀티 서버 환경 대응)
  • JwtAuthGuard: WebSocket 컨텍스트에서 handshake.auth.token으로 JWT 인증 처리
  • study.module.ts: StudyGateway, StudyRoomService, StudyRoomProcessor 등록

구현

입장 (room:join)

  • Redis SET NX로 race condition 방지 — 동시 입장 시 단 한 명만 첫 번째 입장자로 처리
  • 신규 입장 / 재연결 / 중복 탭을 Redis 상태 기반으로 구분
  • 세션 시간은 2시간 고정 -> DB에서 endTime 가져오는 것으로 수정

퇴장

  • 정상 퇴장(room:leave): ack 응답으로 처리, 잔류 인원에게 room:participantsChanged emit
  • 비정상 끊김(handleDisconnect): 30초 유예 후 완전 퇴장 처리
  • 재연결: reconnectKey DEL 반환값으로 원자적 복구 판단 (TOCTOU 방지)

세션 종료 (BullMQ)

  • 종료 10분 전: room:reminder emit
  • 종료 시: room:ended emit → 소켓 강제 퇴장 → Redis 초기화
  • jobId 고정(room-end:{groupId})으로 중복 job 방지

이벤트 목록

방향 이벤트 설명
C→S room:join 룸 입장
C→S room:leave 정상 퇴장
S→C room:started 세션 시작
S→C room:participantsChanged 참여자 목록 변경
S→C room:participantReconnecting 재연결 대기 중
S→C room:participantReconnected 재연결 완료
S→C room:reminder 종료 10분 전 알림
S→C room:ended 세션 종료

Before submitting the PR, please make sure you do the following

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a real-time study room feature using NestJS WebSockets, Redis, and BullMQ for session management. It introduces a StudyRoomService, a StudyGateway, and a global RedisModule. Feedback highlights a critical issue where modifying socket data via fetchSockets() fails to persist across clustered nodes, and identifies a bug where a code property is missing from join validation results. Further improvements are suggested to make the Redis database index configurable, remove the redundant redis dependency, and provide specific error codes for consistent client-side handling.

useFactory: async (config: ConfigService) => {
const host = config.get<string>('REDIS_HOST')
const port = config.get<number>('REDIS_PORT')
const db = 1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Redis database index is hardcoded to 1. It is better to make this configurable via ConfigService to allow for different environments or shared Redis instances.

Suggested change
const db = 1
const db = config.get<number>('REDIS_DB', 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것도 뭐임! app.module.ts도 확인부탁

Copy link
Copy Markdown
Author

@westsunh westsunh May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

admin 단에서 사용하던 Redis 설정을 그대로 가져온 코드였어서 확인해보니

이전에 BullMQ와 Cache 간 Redis DB 충돌로 장애가 발생해서 (#3069) DB를 분리한 이력이 있는데, 해당 설정을 그대로 차용했습니다.

현재 서비스 상 Redis DB 구조는 다음과 같은데

db 0 → CacheModule
db 1 → BullMQ (client: prefix 'bull-client', admin: prefix 'bull')
db 1 → RedisModule (웹소켓 어댑터, 스터디룸 기능)

BullMQ는 client/admin 각각 prefix로 키를 구분하고 있어서 같은 db 1을 사용해도 충돌은 없을 것 같습니다. (근데 분리하는 것이 좋을 것 같기도 합니다.)

또한 RedisModule(웹소켓 어댑터 + 스터디룸)은 prefix 없이 db 1을 그대로 사용하고 있어서 다른 db로 분리하는 게 안전할 것 같은데, 이 부분은 제가 임의로 결정하기보다 같이 논의하고 싶은데 어떻게 생각하시나요?

Comment thread apps/backend/package.json
"pino-http": "^10.5.0",
"pino-pretty": "^11.3.0",
"prisma-graphql-type-decimal": "^3.0.1",
"redis": "^5.11.0",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The redis package is added as a dependency, but the implementation uses ioredis (line 86). Unless another library specifically requires the redis package, it should be removed to keep dependencies clean.

westsunh added 29 commits May 26, 2026 12:33
@westsunh westsunh requested a review from Choi-Jung-Hyeon May 26, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants