docs(user-creator-chat): Redis pub/sub 고정 채널 계획을 기록한다

This commit is contained in:
2026-06-19 06:49:50 +09:00
parent 63e09fa848
commit f6cb07fc0b
2 changed files with 60 additions and 3 deletions

View File

@@ -36,8 +36,11 @@
- Redis key 기본안:
- `v2:user-creator-chat:ws:presence:{roomId}:{memberId}:{sessionId}`
- `v2:user-creator-chat:ws:room:{roomId}:member:{memberId}:sessions`
- `v2:user-creator-chat:ws:room:{roomId}`
- `v2:user-creator-chat:ws:room`
- presence TTL 기본값: 90초
- 운영 Redis는 AWS ElastiCache Serverless Valkey 7.2 또는 Redis OSS 7.1을 사용할 수 있다.
- AWS ElastiCache Serverless는 Redis pattern subscribe에 필요한 `PSUBSCRIBE`를 지원하지 않으므로, Redis listener는 `PatternTopic` 대신 `ChannelTopic` 기반 고정 채널 `SUBSCRIBE`만 사용한다.
- OCI Cache Redis/Valkey 호환성을 위해서도 Redis Pub/Sub은 `PUBLISH`/`SUBSCRIBE` 기본 명령만 사용하고, `roomId` 필터링은 channel name이 아니라 payload의 `roomId/memberId`로 수행한다.
---
@@ -520,6 +523,57 @@ spring:
- GREEN: 같은 focused 명령을 `cleanTest`와 함께 순차 재실행해 `BUILD SUCCESSFUL in 33s`로 통과했다. join presence key/member session set/room set/TTL, last session leave 정리, stale session pruning, Redis pub/sub listener를 통한 target local session payload 전달을 확인했다.
- Reviewer 보강 GREEN: embedded Redis 테스트에서 `user-creator-chat.websocket.server-id=redis-test-server`를 주입하고 실제 Redis presence value JSON의 `serverId`, `memberId`, `roomId`, `sessionId`, `lastSeenAt`과 TTL을 함께 검증하도록 갱신했다.
- [x] **Task 3.4: ElastiCache Serverless 호환 Redis pub/sub channel 보정**
- Files:
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/usercreatorchat/websocket/UserCreatorChatRoomMessageBroker.kt`
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/v2/usercreatorchat/websocket/UserCreatorChatRoomMessageBrokerTest.kt`
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/v2/usercreatorchat/websocket/UserCreatorChatRedisIntegrationTest.kt`
- Verify Docs: `docs/20260618_유저크리에이터채팅_WebSocket전환/prd.md`
- 배경:
- 운영 Redis는 AWS ElastiCache Serverless Valkey 7.2 또는 Redis OSS 7.1이다.
- 현재 `PatternTopic("v2:user-creator-chat:ws:room:*")`는 Redis `PSUBSCRIBE`를 사용한다.
- AWS ElastiCache Serverless는 `PSUBSCRIBE`를 지원하지 않아 애플리케이션 시작 시 `redisMessageListenerContainer` bean 시작이 실패한다.
- RED: broker 생성 테스트를 `PatternTopic` 검증에서 `ChannelTopic("v2:user-creator-chat:ws:room")` 검증으로 변경한다.
- RED: publish 테스트를 room별 channel `v2:user-creator-chat:ws:room:{roomId}`가 아니라 고정 channel `v2:user-creator-chat:ws:room``roomId`, `memberId`, `payload` JSON을 발행하는 검증으로 변경한다.
- 실패 확인:
- Run: `./gradlew --no-daemon test -Dkotlin.compiler.execution.strategy=in-process --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRoomMessageBrokerTest`
- Expected: 기존 구현이 `PatternTopic("v2:user-creator-chat:ws:room:*")`와 room별 channel publish를 사용하므로 변경된 테스트가 실패한다.
- GREEN: `UserCreatorChatRoomMessageBroker`에서 `PatternTopic` import와 room별 subscribe를 제거하고 `ChannelTopic("v2:user-creator-chat:ws:room")`만 등록한다.
- GREEN: `publish(roomId, memberId, payload)`는 기존 `UserCreatorChatRoomPublishedMessage` payload 구조를 유지하되 고정 channel `v2:user-creator-chat:ws:room`으로만 발행한다.
- GREEN: `onMessage`는 기존처럼 payload의 `roomId/memberId`로 local session을 필터링한다.
- 통과 확인:
- Run: `./gradlew --no-daemon test -Dkotlin.compiler.execution.strategy=in-process --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRoomMessageBrokerTest`
- Expected: `BUILD SUCCESSFUL`
- 통합 회귀:
- Run: `./gradlew --no-daemon test -Dkotlin.compiler.execution.strategy=in-process --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRedisIntegrationTest`
- Expected: `BUILD SUCCESSFUL`; embedded Redis pub/sub listener가 고정 channel 구독만으로 대상 local session에 payload를 전달한다.
- 인접 회귀:
- Run: `./gradlew --no-daemon test -Dkotlin.compiler.execution.strategy=in-process --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRoomMessageBrokerTest --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRedisIntegrationTest --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatWebSocketHandlerTest --tests kr.co.vividnext.sodalive.v2.usercreatorchat.UserCreatorChatServiceTest`
- Expected: `BUILD SUCCESSFUL`
- Lint:
- Run: `./gradlew --no-daemon ktlintCheck`
- Expected: `BUILD SUCCESSFUL`
- REFACTOR: channel 상수명은 room별 channel이 아님을 드러내도록 `ROOM_CHANNEL` 또는 동등한 이름으로 정리한다. 기존 external WebSocket/REST API 계약은 변경하지 않는다.
- 문서 확인:
- Run: `rg -n "PatternTopic|PSUBSCRIBE|v2:user-creator-chat:ws:room:\\{roomId\\}|v2:user-creator-chat:ws:room:\\*" docs/20260618_유저크리에이터채팅_WebSocket전환`
- Expected: 과거 검증 기록을 제외한 현재 요구사항/계획에는 `PatternTopic`, `PSUBSCRIBE`, room별 pub/sub channel 요구가 남아 있지 않다.
- 문서 작성 기록:
- 무엇: PRD의 Redis pub/sub channel 요구사항을 고정 channel `v2:user-creator-chat:ws:room` 기준으로 갱신하고, 계획 문서에 ElastiCache Serverless 호환 보정 Task를 추가했다.
- 왜: AWS ElastiCache Serverless Valkey 7.2/Redis OSS 7.1에서 `PSUBSCRIBE`가 지원되지 않아 `PatternTopic` 기반 구현이 애플리케이션 시작 실패를 유발하기 때문이다.
- 어떻게: `PatternTopic`/room별 channel을 제거하고 `ChannelTopic`/고정 channel을 사용하는 RED-GREEN 검증 절차, embedded Redis 통합 회귀, 인접 회귀, lint 검증 명령을 문서화했다.
- 문서 규칙 검증 Run: `./gradlew --no-daemon tasks --all`
- 문서 규칙 검증 Result: sandbox 권한 문제로 최초 실행은 `/Users/klaus/.gradle/.../gradle-8.1.1-bin.zip.lck (Operation not permitted)` 실패. 권한 승인 후 재실행해 `BUILD SUCCESSFUL in 6s`로 통과했다.
- 검증 기록:
- 무엇: `UserCreatorChatRoomMessageBroker`의 Redis pub/sub을 room별 `PatternTopic` 구독/room별 channel publish에서 고정 `ChannelTopic("v2:user-creator-chat:ws:room")` 구독/고정 channel publish로 변경했다.
- 왜: AWS ElastiCache Serverless에서 `PSUBSCRIBE`가 지원되지 않으므로 Spring Data Redis `PatternTopic` 경로를 제거하고 `SUBSCRIBE` 기반 channel만 사용하기 위해서다.
- RED: broker unit test를 먼저 고정 channel 기대값으로 변경한 뒤 `./gradlew --no-daemon test -Dkotlin.compiler.execution.strategy=in-process --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRoomMessageBrokerTest`를 실행해 publish channel과 listener topic 검증이 `ArgumentsAreDifferent`로 실패함을 확인했다.
- GREEN: `PatternTopic` import와 `roomChannel(roomId)`를 제거하고 `ChannelTopic(ROOM_CHANNEL)`, `convertAndSend(ROOM_CHANNEL, ...)`로 변경했다. 같은 focused test는 최초 120초 timeout 후 300초 timeout으로 재실행해 `BUILD SUCCESSFUL in 2m 45s`로 통과했다.
- 통합 회귀: `./gradlew --no-daemon test -Dkotlin.compiler.execution.strategy=in-process --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRedisIntegrationTest``BUILD SUCCESSFUL in 35s`로 통과했고, embedded Redis pub/sub listener가 고정 channel 구독만으로 대상 local session에 payload를 전달함을 확인했다.
- 인접 회귀: `./gradlew --no-daemon test -Dkotlin.compiler.execution.strategy=in-process --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRoomMessageBrokerTest --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatRedisIntegrationTest --tests kr.co.vividnext.sodalive.v2.usercreatorchat.websocket.UserCreatorChatWebSocketHandlerTest --tests kr.co.vividnext.sodalive.v2.usercreatorchat.UserCreatorChatServiceTest``BUILD SUCCESSFUL in 42s`로 통과했다.
- Lint: `./gradlew --no-daemon ktlintCheck``BUILD SUCCESSFUL in 31s`로 통과했다.
- 전체 회귀: `./gradlew --no-daemon test -Dkotlin.compiler.execution.strategy=in-process``BUILD SUCCESSFUL in 1m 32s`로 통과했다.
- 문서 확인: `rg -n "PatternTopic|PSUBSCRIBE|v2:user-creator-chat:ws:room:\\{roomId\\}|v2:user-creator-chat:ws:room:\\*" docs/20260618_유저크리에이터채팅_WebSocket전환` 실행 결과, 남은 항목은 PRD의 현재 금지 요구사항과 Task 3.4/Phase 3 과거 기록 및 presence key 설명으로 확인했다.
---
### Phase 4: WebSocket handler와 메시지 저장/전달