Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
2ef1c53
feat(be): install websokets, redisAdapter, ioredis for studygroup
westsunh Apr 2, 2026
8516f75
feat(be): add Redis module and Socket.IO adapter
westsunh Apr 2, 2026
fb9ddbe
feat(be): add WebSocket auth to JWT auth guard
westsunh Apr 2, 2026
fb3ff4b
feat(be): integrate Redis module into application
westsunh Apr 2, 2026
2c5c395
feat(be): add Redis WebSocket adapter to main
westsunh Apr 2, 2026
7642979
feat(be): add WebSocket gateway for study group
westsunh Apr 2, 2026
30bd3a0
feat(be): add StudyGateway to StudyModule
westsunh Apr 2, 2026
cc23ef6
feat(be): add validateJoinableStudyGroup in study service
westsunh Apr 2, 2026
5273f8b
feat(be): add StudyRoom handleJoin()
westsunh Apr 24, 2026
71f6769
fix(be): add user data handling logic to websocket handler
westsunh Apr 24, 2026
0d3dc42
fix(be): map user id instead of socket id
westsunh Apr 24, 2026
3840feb
fix(be): fix spread to preserve data
westsunh Apr 24, 2026
98da048
fix(be): add params type
westsunh Apr 24, 2026
8b003fc
feat(be): add StudyRoom handleLeave()
westsunh Apr 24, 2026
c3c5dac
refactor(be): separate parsePayload logic
westsunh Apr 25, 2026
d81ac43
feat(be): setup BullMQ for study-room
westsunh Apr 25, 2026
d4b8eaf
feat(be): implement socket disconnect and reconnect recovery logic
westsunh Apr 25, 2026
d064f72
refactor(be): use socket data instead of payload for room:leave
westsunh Apr 25, 2026
a0bbb14
feat(be): implement room lifecycle handling with BullMQ
westsunh Apr 25, 2026
fe839ca
fix(be): resolve race conditions in reconnection and handle expired s…
westsunh Apr 25, 2026
253358f
chore(be): refine logging and clean up unused code
westsunh Apr 25, 2026
756c560
Merge branch 'main' into t2640-study-group-2depth-socket
westsunh Apr 25, 2026
751a879
chore(be): remove unused res field and simplify socket leave logic
westsunh Apr 25, 2026
d056e13
feat(be): reassign host on host leave
westsunh Apr 26, 2026
fe3ee59
refactor(be): use DB endTime for study room expiration
westsunh May 5, 2026
906486d
refactor(be): standardize key prefix structure for study
westsunh May 5, 2026
e11aafb
refactor(be): add safe Redis member parsing with error handling
westsunh May 12, 2026
0659894
refactor(be): normalize socket data structure for study room
westsunh May 13, 2026
0f5071c
refactor(be): improve study room join validation and lifecycle
westsunh May 13, 2026
05332bf
refactor(be): remove unused study room member fields
westsunh May 13, 2026
45bbfa2
docs(be): update JSDoc for study room leave
westsunh May 13, 2026
599bac5
Merge branch 'main' of into t2640-study-group-2depth-socket
westsunh May 13, 2026
d895f9d
fix(be): add Redis password to BullMQ
westsunh May 13, 2026
1c57e0d
refactor(be): prevent race condition in enterRoom
westsunh May 14, 2026
5c374ec
refactor(be): improve room cleanup and edge case handling in StudyRoo…
westsunh May 14, 2026
c3a86a2
Merge branch 'main' of into t2640-study-group-2depth-socket
westsunh May 19, 2026
11623ce
Merge branch 'main' into t2640-study-group-2depth-socket
Choi-Jung-Hyeon May 20, 2026
da3a676
feat(be): restrict WebSocket CORS origin by encironment
westsunh May 24, 2026
261d8fc
refactor(be): move study-room constants to libs
westsunh May 24, 2026
650b785
Merge branch 'main' into t2640-study-group-2depth-socket
Choi-Jung-Hyeon May 25, 2026
e9c33e2
Merge branch 'main' into t2640-study-group-2depth-socket
westsunh May 26, 2026
4cc3df3
refactor(be): remove host and simplify room state
westsunh May 26, 2026
b150c63
Merge branch 't2640-study-group-2depth-socket' into t2640-study-group…
westsunh May 26, 2026
90394e4
feat(be): install websokets, redisAdapter, ioredis for studygroup
westsunh Apr 2, 2026
4c4fbc2
feat(be): add Redis module and Socket.IO adapter
westsunh Apr 2, 2026
1ed587d
feat(be): add WebSocket auth to JWT auth guard
westsunh Apr 2, 2026
c1f24ea
feat(be): integrate Redis module into application
westsunh Apr 2, 2026
701c79b
feat(be): add Redis WebSocket adapter to main
westsunh Apr 2, 2026
d491a99
feat(be): add WebSocket gateway for study group
westsunh Apr 2, 2026
a01d583
feat(be): add StudyGateway to StudyModule
westsunh Apr 2, 2026
cdcf088
feat(be): add validateJoinableStudyGroup in study service
westsunh Apr 2, 2026
f753892
feat(be): add StudyRoom handleJoin()
westsunh Apr 24, 2026
b916700
fix(be): add user data handling logic to websocket handler
westsunh Apr 24, 2026
bd7226e
fix(be): map user id instead of socket id
westsunh Apr 24, 2026
c62f77f
fix(be): fix spread to preserve data
westsunh Apr 24, 2026
4fe37ed
fix(be): add params type
westsunh Apr 24, 2026
1bec0a7
feat(be): add StudyRoom handleLeave()
westsunh Apr 24, 2026
4307c1d
refactor(be): separate parsePayload logic
westsunh Apr 25, 2026
e87f111
feat(be): setup BullMQ for study-room
westsunh Apr 25, 2026
d9c8b3c
feat(be): implement socket disconnect and reconnect recovery logic
westsunh Apr 25, 2026
6e03654
refactor(be): use socket data instead of payload for room:leave
westsunh Apr 25, 2026
2fe4aaf
feat(be): implement room lifecycle handling with BullMQ
westsunh Apr 25, 2026
c95cbaa
fix(be): resolve race conditions in reconnection and handle expired s…
westsunh Apr 25, 2026
b78f66a
chore(be): refine logging and clean up unused code
westsunh Apr 25, 2026
4834b38
chore(be): remove unused res field and simplify socket leave logic
westsunh Apr 25, 2026
59490c6
feat(be): reassign host on host leave
westsunh Apr 26, 2026
b5951ba
refactor(be): use DB endTime for study room expiration
westsunh May 5, 2026
fb1dc28
refactor(be): standardize key prefix structure for study
westsunh May 5, 2026
0d57c8e
refactor(be): add safe Redis member parsing with error handling
westsunh May 12, 2026
10a0c70
refactor(be): normalize socket data structure for study room
westsunh May 13, 2026
468f0ed
refactor(be): improve study room join validation and lifecycle
westsunh May 13, 2026
9502c45
refactor(be): remove unused study room member fields
westsunh May 13, 2026
964976c
docs(be): update JSDoc for study room leave
westsunh May 13, 2026
a349099
fix(be): add Redis password to BullMQ
westsunh May 13, 2026
5b1c451
refactor(be): prevent race condition in enterRoom
westsunh May 14, 2026
0fac57f
refactor(be): improve room cleanup and edge case handling in StudyRoo…
westsunh May 14, 2026
246f926
feat(be): restrict WebSocket CORS origin by encironment
westsunh May 24, 2026
cfc9a4c
refactor(be): move study-room constants to libs
westsunh May 24, 2026
14e6141
refactor(be): remove host and simplify room state
westsunh May 26, 2026
b300302
Merge branch 't2640-study-group-2depth-socket' into t2640-study-group…
westsunh May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions apps/backend/apps/client/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { MailerModule } from '@nestjs-modules/mailer'
import { BullModule } from '@nestjs/bullmq'
import { CacheModule } from '@nestjs/cache-manager'
import { Module, type OnApplicationBootstrap } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { APP_FILTER, APP_GUARD, HttpAdapterHost } from '@nestjs/core'
import type { Server } from 'http'
import { OpenTelemetryModule } from 'nestjs-otel'
Expand All @@ -12,6 +13,7 @@ import { ClientExceptionFilter } from '@libs/exception'
import { openTelemetryModuleOption } from '@libs/instrumentation'
import { pinoLoggerModuleOption } from '@libs/logger'
import { PrismaModule } from '@libs/prisma'
import { RedisModule } from '@libs/redis'
import { AnnouncementModule } from './announcement/announcement.module'
import { AppController } from './app.controller'
import { AppService } from './app.service'
Expand Down Expand Up @@ -55,7 +57,20 @@ import { WorkbookModule } from './workbook/workbook.module'
NotificationModule,
LoggerModule.forRoot(pinoLoggerModuleOption),
OpenTelemetryModule.forRoot(openTelemetryModuleOption),
StudyModule
StudyModule,
RedisModule,
BullModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
connection: {
host: configService.get<string>('REDIS_HOST'),
port: configService.get<number>('REDIS_PORT'),
password: configService.get<string>('REDIS_PASSWORD'),
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.

하드코딩 머임!

},
prefix: 'bull-client'
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.

prefix 충돌 안하져?

})
})
],
controllers: [AppController],
providers: [
Expand Down
13 changes: 11 additions & 2 deletions apps/backend/apps/client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { ValidationPipe } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
import cookieParser from 'cookie-parser'
import type Redis from 'ioredis'
import Instrumentation from '@libs/instrumentation'
import { REDIS_CLIENT, RedisIoAdapter } from '@libs/redis'

/**
TODO: 가능하면 `app.module.ts`에서 PrismaModule 처럼 IoC 리팩터링 필요
지금은 Instrumentation.start가 AppModule 보다 먼저 실행되어야 함
TODO: 가능하면 `app.module.ts`에서 PrismaModule 처럼 IoC 리팩터링 필요
지금은 Instrumentation.start가 AppModule 보다 먼저 실행되어야 함
자세한 이유는 [이 comment](https://github.com/skkuding/codedang/pull/2705#discussion_r2072945663)를 참고해주세요.
*/
const bootstrap = async () => {
Expand All @@ -29,6 +31,13 @@ const bootstrap = async () => {
app.useGlobalInterceptors(new LoggerErrorInterceptor())
app.useGlobalPipes(new ValidationPipe({ whitelist: true }))
app.use(cookieParser())

// Redis WebSocket adapter 연결
const redisClient = app.get<Redis>(REDIS_CLIENT)
const redisIoAdapter = new RedisIoAdapter(app, redisClient)
await redisIoAdapter.connectToRedis() // redis 연결
app.useWebSocketAdapter(redisIoAdapter) // adapter 등록

if (process.env.APP_ENV !== 'production' && process.env.APP_ENV !== 'stage') {
app.enableCors({
origin: 'http://localhost:5525',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Room
export interface RoomState {
endAt: number
leaderId: number | null
}

export interface RoomMember {
userId: number
userName: string
joinedAt: number
}

// Socket Response
export interface SocketResponse<T = undefined> {
success: boolean
message?: string
data?: T
code?: string
}

export interface JoinResponse {
members: RoomMember[]
endAt: number
remainMs: number
leaderId: number | null
}

// PayLoad
export interface JoinPayload {
groupId: number
}
41 changes: 41 additions & 0 deletions apps/backend/apps/client/src/study/study-room.processor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Processor, WorkerHost } from '@nestjs/bullmq'
import { Logger } from '@nestjs/common'
import { Job } from 'bullmq'
import {
STUDY_ROOM_QUEUE,
JOB_RECONNECT_EXPIRE,
JOB_ROOM_REMINDER,
JOB_ROOM_END
} from '@libs/constants'
import { StudyRoomService } from './study-room.service'

@Processor(STUDY_ROOM_QUEUE)
export class StudyRoomProcessor extends WorkerHost {
private readonly logger = new Logger(StudyRoomProcessor.name)

constructor(private readonly studyRoomService: StudyRoomService) {
super()
}

async process(job: Job): Promise<void> {
switch (job.name) {
case JOB_ROOM_REMINDER:
await this.studyRoomService.reminderRoom(job.data.groupId)
break

case JOB_ROOM_END:
await this.studyRoomService.endRoom(job.data.groupId)
break

case JOB_RECONNECT_EXPIRE:
await this.studyRoomService.handleReconnectExpiry(job.data)
break

default:
this.logger.warn(
`Unknown job name: ${job.name}`,
JSON.stringify(job.data)
)
}
}
}
Loading
Loading