22
33import com .back .global .exception .CustomException ;
44import com .back .global .exception .ErrorCode ;
5+ import com .back .global .websocket .config .WebSocketConstants ;
56import com .back .global .websocket .dto .WebSocketSessionInfo ;
67import com .fasterxml .jackson .databind .DeserializationFeature ;
78import com .fasterxml .jackson .databind .ObjectMapper ;
89import com .fasterxml .jackson .datatype .jsr310 .JavaTimeModule ;
9- import lombok .RequiredArgsConstructor ;
1010import lombok .extern .slf4j .Slf4j ;
1111import org .springframework .data .redis .core .RedisTemplate ;
1212import org .springframework .stereotype .Component ;
1313
14- import java .time .Duration ;
1514import java .util .LinkedHashMap ;
1615import java .util .Set ;
1716import java .util .stream .Collectors ;
@@ -30,61 +29,44 @@ public class RedisSessionStore {
3029 private final RedisTemplate <String , Object > redisTemplate ;
3130 private final ObjectMapper objectMapper ;
3231
33- // Redis Key 패턴
34- private static final String USER_SESSION_KEY = "ws:user:{}" ;
35- private static final String SESSION_USER_KEY = "ws:session:{}" ;
36- private static final String ROOM_USERS_KEY = "ws:room:{}:users" ;
37-
38- // TTL 설정
39- private static final Duration SESSION_TTL = Duration .ofMinutes (6 );
40-
41- // 생성자에서 ObjectMapper 설정
4232 public RedisSessionStore (RedisTemplate <String , Object > redisTemplate ) {
4333 this .redisTemplate = redisTemplate ;
44-
45- // ObjectMapper 초기화 및 설정
4634 this .objectMapper = new ObjectMapper ();
4735 this .objectMapper .registerModule (new JavaTimeModule ());
4836 this .objectMapper .configure (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES , false );
4937 }
5038
51- // ============= 세션 정보 저장/조회/삭제 =============
52-
53- // 사용자 세션 정보 저장
5439 public void saveUserSession (Long userId , WebSocketSessionInfo sessionInfo ) {
5540 try {
56- String userKey = buildUserSessionKey (userId );
57- redisTemplate .opsForValue ().set (userKey , sessionInfo , SESSION_TTL );
41+ String userKey = WebSocketConstants . buildUserSessionKey (userId );
42+ redisTemplate .opsForValue ().set (userKey , sessionInfo , WebSocketConstants . SESSION_TTL );
5843 log .debug ("사용자 세션 정보 저장 완료 - userId: {}" , userId );
5944 } catch (Exception e ) {
6045 log .error ("사용자 세션 정보 저장 실패 - userId: {}" , userId , e );
6146 throw new CustomException (ErrorCode .WS_REDIS_ERROR );
6247 }
6348 }
6449
65- // 세션ID → 사용자ID 매핑 저장
6650 public void saveSessionUserMapping (String sessionId , Long userId ) {
6751 try {
68- String sessionKey = buildSessionUserKey (sessionId );
69- redisTemplate .opsForValue ().set (sessionKey , userId , SESSION_TTL );
52+ String sessionKey = WebSocketConstants . buildSessionUserKey (sessionId );
53+ redisTemplate .opsForValue ().set (sessionKey , userId , WebSocketConstants . SESSION_TTL );
7054 log .debug ("세션-사용자 매핑 저장 완료 - sessionId: {}" , sessionId );
7155 } catch (Exception e ) {
7256 log .error ("세션-사용자 매핑 저장 실패 - sessionId: {}" , sessionId , e );
7357 throw new CustomException (ErrorCode .WS_REDIS_ERROR );
7458 }
7559 }
7660
77- // 사용자 세션 정보 조회
7861 public WebSocketSessionInfo getUserSession (Long userId ) {
7962 try {
80- String userKey = buildUserSessionKey (userId );
63+ String userKey = WebSocketConstants . buildUserSessionKey (userId );
8164 Object value = redisTemplate .opsForValue ().get (userKey );
8265
8366 if (value == null ) {
8467 return null ;
8568 }
8669
87- // LinkedHashMap으로 역직렬화된 경우 변환
8870 if (value instanceof LinkedHashMap || !(value instanceof WebSocketSessionInfo )) {
8971 return objectMapper .convertValue (value , WebSocketSessionInfo .class );
9072 }
@@ -97,10 +79,9 @@ public WebSocketSessionInfo getUserSession(Long userId) {
9779 }
9880 }
9981
100- // 세션ID로 사용자ID 조회
10182 public Long getUserIdBySession (String sessionId ) {
10283 try {
103- String sessionKey = buildSessionUserKey (sessionId );
84+ String sessionKey = WebSocketConstants . buildSessionUserKey (sessionId );
10485 Object value = redisTemplate .opsForValue ().get (sessionKey );
10586
10687 if (value == null ) {
@@ -115,10 +96,9 @@ public Long getUserIdBySession(String sessionId) {
11596 }
11697 }
11798
118- // 사용자 세션 정보 삭제
11999 public void deleteUserSession (Long userId ) {
120100 try {
121- String userKey = buildUserSessionKey (userId );
101+ String userKey = WebSocketConstants . buildUserSessionKey (userId );
122102 redisTemplate .delete (userKey );
123103 log .debug ("사용자 세션 정보 삭제 완료 - userId: {}" , userId );
124104 } catch (Exception e ) {
@@ -127,10 +107,9 @@ public void deleteUserSession(Long userId) {
127107 }
128108 }
129109
130- // 세션-사용자 매핑 삭제
131110 public void deleteSessionUserMapping (String sessionId ) {
132111 try {
133- String sessionKey = buildSessionUserKey (sessionId );
112+ String sessionKey = WebSocketConstants . buildSessionUserKey (sessionId );
134113 redisTemplate .delete (sessionKey );
135114 log .debug ("세션-사용자 매핑 삭제 완료 - sessionId: {}" , sessionId );
136115 } catch (Exception e ) {
@@ -139,36 +118,31 @@ public void deleteSessionUserMapping(String sessionId) {
139118 }
140119 }
141120
142- // 사용자 세션 존재 여부 확인
143121 public boolean existsUserSession (Long userId ) {
144122 try {
145- String userKey = buildUserSessionKey (userId );
123+ String userKey = WebSocketConstants . buildUserSessionKey (userId );
146124 return Boolean .TRUE .equals (redisTemplate .hasKey (userKey ));
147125 } catch (Exception e ) {
148126 log .error ("사용자 세션 존재 여부 확인 실패 - userId: {}" , userId , e );
149127 throw new CustomException (ErrorCode .WS_REDIS_ERROR );
150128 }
151129 }
152130
153- // ============= 방 참가자 관리 =============
154-
155- // 방에 사용자 추가
156131 public void addUserToRoom (Long roomId , Long userId ) {
157132 try {
158- String roomUsersKey = buildRoomUsersKey (roomId );
133+ String roomUsersKey = WebSocketConstants . buildRoomUsersKey (roomId );
159134 redisTemplate .opsForSet ().add (roomUsersKey , userId );
160- redisTemplate .expire (roomUsersKey , SESSION_TTL );
135+ redisTemplate .expire (roomUsersKey , WebSocketConstants . SESSION_TTL );
161136 log .debug ("방에 사용자 추가 완료 - roomId: {}, userId: {}" , roomId , userId );
162137 } catch (Exception e ) {
163138 log .error ("방에 사용자 추가 실패 - roomId: {}, userId: {}" , roomId , userId , e );
164139 throw new CustomException (ErrorCode .WS_REDIS_ERROR );
165140 }
166141 }
167142
168- // 방에서 사용자 제거
169143 public void removeUserFromRoom (Long roomId , Long userId ) {
170144 try {
171- String roomUsersKey = buildRoomUsersKey (roomId );
145+ String roomUsersKey = WebSocketConstants . buildRoomUsersKey (roomId );
172146 redisTemplate .opsForSet ().remove (roomUsersKey , userId );
173147 log .debug ("방에서 사용자 제거 완료 - roomId: {}, userId: {}" , roomId , userId );
174148 } catch (Exception e ) {
@@ -177,10 +151,9 @@ public void removeUserFromRoom(Long roomId, Long userId) {
177151 }
178152 }
179153
180- // 방의 사용자 목록 조회
181154 public Set <Long > getRoomUsers (Long roomId ) {
182155 try {
183- String roomUsersKey = buildRoomUsersKey (roomId );
156+ String roomUsersKey = WebSocketConstants . buildRoomUsersKey (roomId );
184157 Set <Object > userIds = redisTemplate .opsForSet ().members (roomUsersKey );
185158
186159 if (userIds != null ) {
@@ -196,10 +169,9 @@ public Set<Long> getRoomUsers(Long roomId) {
196169 }
197170 }
198171
199- // 방의 사용자 수 조회
200172 public long getRoomUserCount (Long roomId ) {
201173 try {
202- String roomUsersKey = buildRoomUsersKey (roomId );
174+ String roomUsersKey = WebSocketConstants . buildRoomUsersKey (roomId );
203175 Long count = redisTemplate .opsForSet ().size (roomUsersKey );
204176 return count != null ? count : 0 ;
205177 } catch (Exception e ) {
@@ -208,36 +180,16 @@ public long getRoomUserCount(Long roomId) {
208180 }
209181 }
210182
211- // ============= 전체 통계 =============
212-
213- // 전체 온라인 사용자 수 조회
214183 public long getTotalOnlineUserCount () {
215184 try {
216- Set <String > userKeys = redisTemplate .keys (buildUserSessionKey ( "*" ));
185+ Set <String > userKeys = redisTemplate .keys (WebSocketConstants . buildUserSessionKeyPattern ( ));
217186 return userKeys != null ? userKeys .size () : 0 ;
218187 } catch (Exception e ) {
219188 log .error ("전체 온라인 사용자 수 조회 실패" , e );
220189 return 0 ;
221190 }
222191 }
223192
224- // ============= Key 생성 헬퍼 =============
225-
226- private String buildUserSessionKey (Object userId ) {
227- return USER_SESSION_KEY .replace ("{}" , userId .toString ());
228- }
229-
230- private String buildSessionUserKey (String sessionId ) {
231- return SESSION_USER_KEY .replace ("{}" , sessionId );
232- }
233-
234- private String buildRoomUsersKey (Long roomId ) {
235- return ROOM_USERS_KEY .replace ("{}" , roomId .toString ());
236- }
237-
238- // ============= 타입 변환 헬퍼 =============
239-
240- // Object를 Long으로 안전하게 변환
241193 private Long convertToLong (Object obj ) {
242194 if (obj instanceof Long ) {
243195 return (Long ) obj ;
0 commit comments