Skip to content

Conversation

@jung-min-ju
Copy link
Contributor

@jung-min-ju jung-min-ju commented May 4, 2025

1. 사용자 인증 및 예외 처리 전

1.1 헤더

image

모든 로직에 다음과 같이 핸드쉐이킹 때 생성한 principal 객체로 유저 정보를 가져올 예정입니다.

1.2 예외 처리

예외 코드는 restFul 서버의 예외처리를 최대한 따라, 추후 합쳐질 때 로직 혼동이 없도록 구성하였습니다.
그 외 소켓의 예외 처리 로직은 다음과 같습니다.

STOMP 수준에서 발생한 예외는 @MessageExceptionHandler를 통해 처리되며, 클라이언트는 /user/{userId}/queue/errors 경로를 구독함으로써 개별 에러 메시지를 수신할 수 있습니다. 이 방식은 HTTP의 전역 예외 처리와 유사한 흐름으로 동작합니다.

Redis Pub/Sub 과정에서 발생한 예외는 RedisIntegrationConfig 내부의 redisErrorFlow에서 포착됩니다. 해당 흐름은 Redis 메시지 처리 중 발생하는 MessageHandlingException이나 사용자 정의 CustomException 등을 감지하고, 위와 동일한 경로인 /user/{userId}/queue/errors 를 통해 개별 클라이언트에게 에러 메시지를 전송합니다.


2. 레디스 구조

2.1 레디스 수신 채널 (RedisIntegrationConfig.java)

스크린샷 2025-05-09 234840

현재는 비즈니스 로직에 따라 Redis 수신 채널을 도메인 단위(게임, 채팅, 대기방 등)로 분리하고 있으며, 공통적으로 하나의 스레드 풀(redisExecutor)을 사용하고 있습니다.

추후에는 테스트를 거쳐보고, 메시지 처리량이 높은 채널(ex. 게임, 채팅)에 대해 전용 스레드 풀을 부여하고, 상대적으로 부하가 적은 대기방 관련 CRUD 채널 등은 공통 스레드 풀을 사용하는 방식으로 스레드풀을 역할 및 트래픽 기반으로 분리해도 괜찮을 것 같습니다.


2.3. Redis Pub/Sub 기반 메시지 플로우 구성

  • spring-integration-core, spring-integration-redis 도입
  • Redis 메시지 수신 및 처리 흐름을 IntegrationFlow 기반으로 리팩토링
  • 메시지 라우팅/핸들링 구조 명확화 및 에러 흐름 추적 개선

📦 시스템 구성 흐름도 -> ( 현재 테스트 완료 된 핸들러만 표시 )

   [Redis Pub/Sub] → [RedisInboundAdapter]
                         │
                         ▼
                  [redisInputChannel]
                         │
              ┌─────────Routing────────────┐
              │                            │
        [roomMessageChannel]         [personalMessageChannel]
              │                            │
              ▼                            ▼
      RoomPubSubHandler.onMessage()   PersonalPubSubHandler.onMessage()

🔧 주요 구성 요소

컴포넌트 역할 설명
RedisInboundChannelAdapter Redis로부터 메시지 수신 (topic 패턴 매핑)
IntegrationFlow 메시지 흐름 정의 및 채널 라우팅
PubSubHandler 추상 클래스 채널별 메시지 처리 로직 위임
RoomPubSubHandler, PersonalPubSubHandler 도메인별 실제 처리기
redisErrorFlow 메시지 처리 실패 시 사용자에게 에러 전송

2.4 채널 구조

도메인 채널 구조는 다음과 같습니다.
image

에러 전용 채널은 ##1.2 를 참고해주세요.


3. 고유 방 번호 부여

3.1 기능 요약

  • 0000부터 9999까지의 방 ID 풀을 Redis에 관리
  • ID 할당 (allocateRoomId) 시 중복 없이 하나 꺼내줌
  • ID 반환 (releaseRoomId) 시 사용이 끝난 ID를 다시 Redis에 추가
  • 서버 시작 시 (@PostConstruct) ID 풀이 비어있으면 자동 초기화

3.2🔧 간단 흐름

애플리케이션 시작 시:
  - Redis에 room:ids:available 키 존재 여부 확인
  - 없으면 0000~9999 ID를 Redis Set에 미리 채워 넣음

방 생성 시:
  - allocateRoomId() 호출 → Redis Set에서 하나 꺼냄 (pop)

방 삭제 시:
  - releaseRoomId(id) 호출 → 다시 Redis Set에 삽입

4. 논의 사항

현재 방 생성 이후 로직은 다음 두 가지로 구성되어 있습니다:

  • 방 목록 브로드캐스트 → 모든 클라이언트가 새로운 방을 목록에 반영할 수 있도록 공용 채널로 전송
  • 방 생성자 전용 응답 → 본인이 생성한 방의 정보를 1:1로 별도 수신 (확인용)

이 구조는 메시지 내용은 동일하지만 두 번의 네트워크 전송이 발생하며, 특히 생성자 본인은 브로드캐스트만으로는 방 생성 여부를 명확히 확인할 수 없다는 점에서 1:1 응답이 필요합니다.

그럼 다음과 같은 질문이 생깁니다.

  1. 이중 전송이 과연 필수일까?
  2. 브로드캐스트 메시지 하나만으로 생성자도 처리하게 할 수는 없을까?
    -> 이 방법으로 가게 되면, 프론트 측에서 방금 전달 받은 방 정보가 내가 만든 것인지 확인하는 로직이 필요할 것 같습니다.

다들 이에 대해 어떻게 생각하시나요? 의견 부탁드립니다.
image

jung-min-ju added 30 commits May 5, 2025 03:30
(현재는 묘묘를 속여라/토선생의 미대입시란 이름 외 간단 명명)
[add] 유효하지 않은 채널 시 Exception 코드 추가
[add] 유효하지 않은 메시지 dto 형태 Exception 코드 추가
- Redis Pub/Sub 메시지 처리 entrypoint onMessage(topic, payload)로 구체화
- DTO 내 DTO로 인한 @Class 제거 처리 추가
- Redis Pub/Sub 메시지 수신 방식으로 DSL 기반 RedisInboundChannelAdapter로 전환
- 기존 RedisMessageListenerContainer, MessageListenerAdapter 기반 메시지 수신 코드 제거
- STOMP 메시지 직렬화를 위한 MappingJackson2MessageConverter 제거
- roomMessageFlow에서 errorHandlingAdvice 제거 및 기본 핸들러 구조로 환원
- RedisInboundAdapter의 topic 패턴 정비 및 채널별 Flow 구성 명확화
add: 테스트로 검증 안된 로직들(방 삭제, 업데이트, 입장) 주석 처리
modify : map의 key에 고유 prefix 부여
chore : 테스트 검증 안된 방 업데이트, 삭제, 입장 로직 주석처리
- 유효하지 않은 방 코드 경우
- 사용 가능한 방 코드가 없는 경우
Copy link

@Uralauah Uralauah left a comment

Choose a reason for hiding this comment

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

전체적으로 잘 작성된 것 같습니다.
기존 서버와 합친다면 없애야할 코드들도 여럿 보이네요..
처음부터 합치고 시작할 걸 그랬습니다..

그리고 방 목록은 SSE로 전달하는거 아니었나요??

@jung-min-ju
Copy link
Contributor Author

전체적으로 잘 작성된 것 같습니다. 기존 서버와 합친다면 없애야할 코드들도 여럿 보이네요.. 처음부터 합치고 시작할 걸 그랬습니다..

그리고 방 목록은 SSE로 전달하는거 아니었나요??

아 저번에 게임 완료 이전까지는 모두 소켓에서 관리하는 걸로 이야기가 된 줄 알았습니다. 그리고 이미 소켓 연결을 기반으로 유저 상태, 채팅 등을 처리하고 있으므로, 같은 연결을 통해 방 목록까지 처리하는 것이 리소스 측면에서도 효율적인 것 같습니담

@Uralauah
Copy link

아 저번에 게임 완료 이전까지는 모두 소켓에서 관리하는 걸로 이야기가 된 줄 알았습니다. 그리고 이미 소켓 연결을 기반으로 유저 상태, 채팅 등을 처리하고 있으므로, 같은 연결을 통해 방 목록까지 처리하는 것이 리소스 측면에서도 효율적인 것 같습니담

음 그렇다면 좀 생각을 해봤는데
userId를 쓰자니 프론트에서는 userId를 모르고,
닉네임을 쓰자니 게스트는 중복 닉네임이 가능하고,
백엔드만 가지고는 딱히 방법이 없는 것 같습니다.

프론트에서 방 생성 요청을 보낼 때, 요청ID(UUID와 같은 식별용 ID)와 같이 보낸다면
그걸 메타데이터에 같이 포함시켜서 보내주면 하나의 브로드캐스팅만으로 처리될 수 있지 않을까요??
그렇게 하면 프론트에서 방 생성자인지 판단하는 로직도 더 간단해질 것 같습니다.

@jung-min-ju
Copy link
Contributor Author

아 저번에 게임 완료 이전까지는 모두 소켓에서 관리하는 걸로 이야기가 된 줄 알았습니다. 그리고 이미 소켓 연결을 기반으로 유저 상태, 채팅 등을 처리하고 있으므로, 같은 연결을 통해 방 목록까지 처리하는 것이 리소스 측면에서도 효율적인 것 같습니담

음 그렇다면 좀 생각을 해봤는데 userId를 쓰자니 프론트에서는 userId를 모르고, 닉네임을 쓰자니 게스트는 중복 닉네임이 가능하고, 백엔드만 가지고는 딱히 방법이 없는 것 같습니다.

프론트에서 방 생성 요청을 보낼 때, 요청ID(UUID와 같은 식별용 ID)와 같이 보낸다면 그걸 메타데이터에 같이 포함시켜서 보내주면 하나의 브로드캐스팅만으로 처리될 수 있지 않을까요?? 그렇게 하면 프론트에서 방 생성자인지 판단하는 로직도 더 간단해질 것 같습니다.

좋은 아이디어입니다! 다음 구현에서 반영하여 pr 올리겠습니다.

@jung-min-ju jung-min-ju merged commit a4f5289 into develop May 15, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants