[WTH-389] sse 연결 끊기는 문제 해결#74
Hidden character warning
Conversation
📝 WalkthroughWalkthroughQR 출석 시스템의 활성 세션 추적을 QrAttendancePort를 중심으로 재설계하여, Redis 기반 원자적 저장/조회/삭제를 구현하고, 생성·구독·만료 흐름에서 일관되게 클럽별 세션 상태를 관리합니다. ChangesQR 활성 세션 포트 기반 관리
🎯 3 (Moderate) | ⏱️ ~25 minutesPossibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/kotlin/com/weeth/domain/attendance/infrastructure/RedisQrAttendanceAdapter.kt`:
- Around line 19-23: The two sequential redisTemplate.opsForValue().set calls in
RedisQrAttendanceAdapter.store can leave state inconsistent if the second set
fails; make the two key writes atomic by replacing them with a single Redis Lua
script (or MULTI/EXEC) that sets key(sessionId) with TTL
QrAttendancePort.TTL_SECONDS and activeKey(clubId) with TTL
QrAttendancePort.ACTIVE_TTL_SECONDS in one atomic operation. Implement the Lua
script (or transaction) and call it via redisTemplate.execute (or
DefaultRedisScript) from the store method, passing key(sessionId) and
activeKey(clubId) and their TTLs so both keys and expirations are written
together; ensure the code paths still handle and log failures from the script
execution.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 51d9bbf4-e0cd-4be8-9d77-51035eb97b2d
📒 Files selected for processing (15)
src/main/kotlin/com/weeth/domain/attendance/application/dto/response/QrStatusResponse.ktsrc/main/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapper.ktsrc/main/kotlin/com/weeth/domain/attendance/application/usecase/command/GenerateQrTokenUseCase.ktsrc/main/kotlin/com/weeth/domain/attendance/application/usecase/command/SubscribeAttendanceSseUseCase.ktsrc/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetQrStatusQueryService.ktsrc/main/kotlin/com/weeth/domain/attendance/domain/port/QrAttendancePort.ktsrc/main/kotlin/com/weeth/domain/attendance/infrastructure/QrExpiredEventListener.ktsrc/main/kotlin/com/weeth/domain/attendance/infrastructure/RedisQrAttendanceAdapter.ktsrc/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.ktsrc/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.ktsrc/test/kotlin/com/weeth/domain/attendance/application/usecase/command/GenerateQrTokenUseCaseTest.ktsrc/test/kotlin/com/weeth/domain/attendance/application/usecase/command/SubscribeAttendanceSseUseCaseTest.ktsrc/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetQrStatusQueryServiceTest.ktsrc/test/kotlin/com/weeth/domain/attendance/infrastructure/QrExpiredEventListenerTest.ktsrc/test/kotlin/com/weeth/domain/attendance/infrastructure/RedisQrAttendanceAdapterTest.kt
hyxklee
left a comment
There was a problem hiding this comment.
궁금한 부분이 있어 한 가지 질문 달았습니다!
고생하셨습니당
그리고 SSE의 경우는 1. QR이 없다 2. QR이 생겼다 3. QR의 만료 시간 4. QR 만료 시간 업데이트 이렇게 4가지 케이스에 대해서 실시간으로 값을 넣어주고 있는거졍??
| getAttendanceQueryService.findAllDetailsByCurrentCardinal(clubId, userId), | ||
| ) | ||
|
|
||
| @GetMapping("/qr-status") |
There was a problem hiding this comment.
혹시 해당 API의 사용 시나리오는 어떻게 되는걸까용??
SSE와 연동해서 설명해주시면 감사하겠습니닷
There was a problem hiding this comment.
- QR이 없다 -> qr-none 응답
- QR이 생겼다, 3. QR의 만료 시간 -> qr-open, expiredAt 응답
- QR 만료 시간 업데이트 -> QR을 다시 생성하면 Redis TTL이 갱신되고 다시 qr-open이 나가면서 새 expiredAt 응답
으로 실시간으로 값을 넣어주고 있습니다!
There was a problem hiding this comment.
해당 API는 SSE 이벤트가 아니라 프론트에서 요청한 현재 시점의 QR 활성 상태를 조회하는 API입니다!
사용 흐름은 아래와 같습니당
- 프론트에서 /qr-status 호출
- QR이 열려 있으면 isActive=true, currentSessionId, expiresAt으로 화면에 QR/만료시간 표시
- QR이 없으면 isActive=false로 QR 없음 상태 표시
There was a problem hiding this comment.
회의록 요청사항을 보고했는데.. 폴링방식으로 사용할 때의 경우엿던 것 같습니당..............
해당 API는 지우고 머지하겟습니닷
There was a problem hiding this comment.
앗 슬랙에서 말씀드린 부분만 했으면 됐는데 전달이 부족했었구뇽...
There was a problem hiding this comment.
SSE는 제가 이해한 방식으로 처리되고 있는게 맞을까용??
There was a problem hiding this comment.
네네 맞습니당
API만 추가된거고 SSE 흐름은 위에 말씀드린 것과 동일합니다!
📌 Summary
SSE 응답이 제대로 가지 않는 문제 해결
📝 Changes
What
클럽별 현재 활성 QR 세션을 Redis에 저장하도록 추가
SSE 구독 시 QR 상태 판단 기준을 Redis active QR 기준으로 변경
Why
기존에는 SSE 재접속 시 OPEN 세션 중 하나를 조회해 QR 상태를 추측해서 qr-none으로 응답이 보내졌습니다.
이전 QR 만료 이벤트가 최신 QR 상태를 닫는 상황이 있었습니다.
How
QR 생성 시 Redis에 QR 코드와 클럽별 active QR sessionId를 함께 저장했습니다.
Redis Lua script로 active QR 비교와 삭제를 원자적으로 처리했습니다.
📸 Screenshots / Logs
💡 Reviewer 참고사항
✅ Checklist
Summary by CodeRabbit
릴리스 노트
버그 수정
테스트