From ceb3b94c41d6bb3e062900062358464dc233fe99 Mon Sep 17 00:00:00 2001 From: loseminho Date: Wed, 1 Oct 2025 19:51:43 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20RoomMember=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=EC=84=9C=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0,?= =?UTF-8?q?=20redis=EB=A1=9C=20=EC=9D=B4=EA=B4=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../studyroom/dto/RoomMemberResponse.java | 8 +- .../domain/studyroom/entity/RoomMember.java | 88 ++++-------- .../repository/RoomMemberRepository.java | 5 +- .../RoomMemberRepositoryCustom.java | 12 ++ .../repository/RoomMemberRepositoryImpl.java | 125 ++++-------------- .../studyroom/repository/RoomRepository.java | 4 +- .../repository/RoomRepositoryImpl.java | 11 +- .../domain/studyroom/service/RoomService.java | 22 ++- .../webrtc/service/WebRTCSignalValidator.java | 9 +- .../studyroom/service/RoomServiceTest.java | 6 +- .../service/WebRTCSignalValidatorTest.java | 87 ++---------- 11 files changed, 103 insertions(+), 274 deletions(-) diff --git a/src/main/java/com/back/domain/studyroom/dto/RoomMemberResponse.java b/src/main/java/com/back/domain/studyroom/dto/RoomMemberResponse.java index 2d12c824..a6f0aa21 100644 --- a/src/main/java/com/back/domain/studyroom/dto/RoomMemberResponse.java +++ b/src/main/java/com/back/domain/studyroom/dto/RoomMemberResponse.java @@ -13,18 +13,18 @@ public class RoomMemberResponse { private Long userId; private String nickname; private RoomRole role; - private boolean isOnline; private LocalDateTime joinedAt; - private LocalDateTime lastActiveAt; + private LocalDateTime promotedAt; + + // TODO: isOnline은 Redis에서 조회하여 추가 예정 public static RoomMemberResponse from(RoomMember member) { return RoomMemberResponse.builder() .userId(member.getUser().getId()) .nickname(member.getUser().getNickname()) .role(member.getRole()) - .isOnline(member.isOnline()) .joinedAt(member.getJoinedAt()) - .lastActiveAt(member.getLastActiveAt() != null ? member.getLastActiveAt() : member.getJoinedAt()) + .promotedAt(member.getPromotedAt()) .build(); } } diff --git a/src/main/java/com/back/domain/studyroom/entity/RoomMember.java b/src/main/java/com/back/domain/studyroom/entity/RoomMember.java index e442c692..929425e9 100644 --- a/src/main/java/com/back/domain/studyroom/entity/RoomMember.java +++ b/src/main/java/com/back/domain/studyroom/entity/RoomMember.java @@ -41,16 +41,9 @@ public class RoomMember extends BaseEntity { // 멤버십 기본 정보 @Column(nullable = false) - private LocalDateTime joinedAt; // 방에 처음 입장한 시간 - private LocalDateTime lastActiveAt; // 마지막으로 활동한 시간 - - // 실시간 상태 관리 필드들 - @Column(nullable = false) - private boolean isOnline = false; // 현재 방에 온라인 상태인지 - - private String connectionId; // WebSocket 연결 ID (실시간 통신용) - - private LocalDateTime lastHeartbeat; // 마지막 heartbeat 시간 (연결 상태 확인용) + private LocalDateTime joinedAt; // MEMBER 이상으로 승격된 시간 + + private LocalDateTime promotedAt; // 권한이 변경된 시간 // 💡 권한 확인 메서드들 (RoomRole enum의 메서드를 위임) @@ -95,18 +88,20 @@ public boolean isMember() { } /** - * 현재 활성 상태인지 확인 - 온라인 멤버 목록 표시, 비활성 사용자 정리 등 - 온라인 상태이고 최근 설정된 시간 이내에 heartbeat가 있었던 경우 + * 현재 활성 상태인지 확인 (Redis 기반으로 변경 예정) + * 임시로 항상 true 반환 + * TODO: Redis에서 실시간 상태 확인하도록 변경 */ + @Deprecated public boolean isActive(int timeoutMinutes) { - return isOnline && lastHeartbeat != null && - lastHeartbeat.isAfter(LocalDateTime.now().minusMinutes(timeoutMinutes)); + // 실시간 상태는 Redis에서 관리 + return true; } /** - 기본 멤버 생성 메서드, 처음 입장 시 사용 + 기본 멤버 생성 메서드 + MEMBER 이상 등급 생성 시 사용 (DB 저장용) */ public static RoomMember create(Room room, User user, RoomRole role) { RoomMember member = new RoomMember(); @@ -114,9 +109,7 @@ public static RoomMember create(Room room, User user, RoomRole role) { member.user = user; member.role = role; member.joinedAt = LocalDateTime.now(); - member.lastActiveAt = LocalDateTime.now(); - member.isOnline = true; // 생성 시 온라인 상태 - member.lastHeartbeat = LocalDateTime.now(); + member.promotedAt = LocalDateTime.now(); return member; } @@ -128,18 +121,24 @@ public static RoomMember createHost(Room room, User user) { /** * 일반 멤버 생성, 권한 자동 변경 - - 비공개 방에서 초대받은 사용자를 정식 멤버로 등록할 때 (로직 검토 중) + * 비공개 방에서 초대받은 사용자를 정식 멤버로 등록할 때 */ public static RoomMember createMember(Room room, User user) { return create(room, user, RoomRole.MEMBER); } /** - * 방문객 생성 - * 사용 상황: 공개 방에 처음 입장하는 사용자를 임시 방문객으로 등록 + * 방문객 생성 (메모리상으로만 존재, DB 저장 안함) + * 공개 방에 처음 입장하는 사용자용 + * Redis에서 실시간 상태 관리 */ public static RoomMember createVisitor(Room room, User user) { - return create(room, user, RoomRole.VISITOR); + RoomMember member = new RoomMember(); + member.room = room; + member.user = user; + member.role = RoomRole.VISITOR; + member.joinedAt = LocalDateTime.now(); + return member; } /** @@ -148,47 +147,6 @@ public static RoomMember createVisitor(Room room, User user) { */ public void updateRole(RoomRole newRole) { this.role = newRole; - } - - /** - * 온라인 상태 변경 - * 사용 상황: 멤버가 방에 입장하거나 퇴장할 때 - 활동 시간도 함께 업데이트, 온라인이 되면 heartbeat도 갱신 - */ - public void updateOnlineStatus(boolean online) { - this.isOnline = online; - this.lastActiveAt = LocalDateTime.now(); - if (online) { - this.lastHeartbeat = LocalDateTime.now(); - } - } - - /** - * WebSocket 연결 ID 업데이트 - * 사용 상황: 멤버가 웹소켓으로 방에 연결될 때 - + heartbeat도 함께 갱신 - */ - public void updateConnectionId(String connectionId) { - this.connectionId = connectionId; - this.lastHeartbeat = LocalDateTime.now(); - } - - /** - * 사용 : 클라이언트에서 주기적으로 서버에 연결 상태를 알릴 때 - * 목적: 연결이 끊어진 멤버를 자동으로 감지하기 위해 사용, 별도의 다른 것으로 변경 가능 - */ - public void heartbeat() { - this.lastHeartbeat = LocalDateTime.now(); - this.lastActiveAt = LocalDateTime.now(); - this.isOnline = true; - } - - /** - * 방 퇴장 처리 (명시적 퇴장과 연결 끊김 상태 로직 분할 예정임.. 일단은 임시로 통합 상태) - 멤버가 방을 나가거나 연결이 끊어졌을 때, 오프라인 상태로 변경하고 연결 ID 제거 - */ - public void leave() { - this.isOnline = false; - this.connectionId = null; + this.promotedAt = LocalDateTime.now(); } } diff --git a/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepository.java b/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepository.java index 213f3890..367d8d9d 100644 --- a/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepository.java +++ b/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepository.java @@ -8,8 +8,5 @@ @Repository public interface RoomMemberRepository extends JpaRepository, RoomMemberRepositoryCustom { - /** - * WebSocket 연결 ID로 멤버 조회 - */ - Optional findByConnectionId(String connectionId); + // 모든 메서드는 RoomMemberRepositoryCustom 인터페이스로 이동 } diff --git a/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepositoryCustom.java b/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepositoryCustom.java index 7740e0d1..a6732eb6 100644 --- a/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepositoryCustom.java +++ b/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepositoryCustom.java @@ -20,16 +20,22 @@ public interface RoomMemberRepositoryCustom { /** * 방의 온라인 멤버 조회 + * TODO: Redis 기반으로 변경 예정 + * 현재는 DB에 저장된 모든 멤버 반환 (임시) */ + @Deprecated List findOnlineMembersByRoomId(Long roomId); /** * 방의 활성 멤버 수 조회 + * TODO: Redis 기반으로 변경 예정 */ + @Deprecated int countActiveMembersByRoomId(Long roomId); /** * 사용자가 참여 중인 모든 방의 멤버십 조회 + * DB에 저장된 멤버십만 조회 (MEMBER 이상) */ List findActiveByUserId(Long userId); @@ -60,16 +66,22 @@ public interface RoomMemberRepositoryCustom { /** * 특정 역할의 멤버 수 조회 + * TODO: Redis 기반으로 변경 예정 */ + @Deprecated int countByRoomIdAndRole(Long roomId, RoomRole role); /** * 방 퇴장 처리 (벌크 업데이트) + * TODO: Redis로 이관 예정, DB에는 멤버십만 유지 */ + @Deprecated void leaveRoom(Long roomId, Long userId); /** * 방의 모든 멤버를 오프라인 처리 (방 종료 시) + * TODO: Redis로 이관 예정 */ + @Deprecated void disconnectAllMembers(Long roomId); } diff --git a/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepositoryImpl.java b/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepositoryImpl.java index 81bd17a7..8c8761e4 100644 --- a/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepositoryImpl.java +++ b/src/main/java/com/back/domain/studyroom/repository/RoomMemberRepositoryImpl.java @@ -74,48 +74,39 @@ public List findByRoomIdOrderByRole(Long roomId) { /** * 방의 온라인 멤버 조회 - * - 현재 온라인 상태인 멤버만 (isOnline = true) - * - 1순위: 역할 (HOST > SUB_HOST > MEMBER > VISITOR) - * - 2순위: 마지막 활동 시간 (최근 활동 순) - * - 방 상세 페이지에서 현재 접속 중인 멤버 표시 - * - 실시간 멤버 목록 업데이트 + * TODO: Redis 기반으로 변경 예정 + * 현재는 방의 모든 멤버 반환 (임시) * @param roomId 방 ID - * @return 온라인 멤버 목록 + * @return 멤버 목록 (역할순, 입장순 정렬) */ @Override + @Deprecated public List findOnlineMembersByRoomId(Long roomId) { return queryFactory .selectFrom(roomMember) .leftJoin(roomMember.user, user).fetchJoin() // N+1 방지 - .where( - roomMember.room.id.eq(roomId), - roomMember.isOnline.eq(true) - ) + .where(roomMember.room.id.eq(roomId)) .orderBy( - roomMember.role.asc(), // 역할순 - roomMember.lastActiveAt.desc() // 최근 활동순 + roomMember.role.asc(), // 역할순 + roomMember.joinedAt.asc() // 입장 시간순 ) .fetch(); } /** * 방의 활성 멤버 수 조회 - * - 현재 온라인 상태인 멤버 (isOnline = true) - * - 방 목록에서 현재 참가자 수 표시 - * - 정원 체크 (현재 참가자 vs 최대 참가자) - * - 통계 데이터 수집 로직 구현 시 연결 해야함.. + * TODO: Redis 기반으로 변경 예정 + * 현재는 방의 모든 멤버 수 반환 (임시) * @param roomId 방 ID - * @return 활성 멤버 수 + * @return 멤버 수 */ @Override + @Deprecated public int countActiveMembersByRoomId(Long roomId) { Long count = queryFactory .select(roomMember.count()) .from(roomMember) - .where( - roomMember.room.id.eq(roomId), - roomMember.isOnline.eq(true) - ) + .where(roomMember.room.id.eq(roomId)) .fetchOne(); return count != null ? count.intValue() : 0; @@ -123,17 +114,15 @@ public int countActiveMembersByRoomId(Long roomId) { /** * 사용자가 참여 중인 모든 방의 멤버십 조회 + * DB에 저장된 멤버십만 조회 (MEMBER 이상) * @param userId 사용자 ID - * @return 참여 중인 방의 멤버십 목록 + * @return 멤버십 목록 */ @Override public List findActiveByUserId(Long userId) { return queryFactory .selectFrom(roomMember) - .where( - roomMember.user.id.eq(userId), - roomMember.isOnline.eq(true) - ) + .where(roomMember.user.id.eq(userId)) .fetch(); } @@ -267,29 +256,21 @@ public boolean existsByRoomIdAndUserId(Long roomId, Long userId) { } /** - * 특정 역할의 온라인 멤버 수 조회 - * - 특정 역할의 멤버 - * - 현재 온라인 상태만 - * 예시: - * ```java - * int hostCount = countByRoomIdAndRole(roomId, RoomRole.HOST); - * if (hostCount == 0) { - * - * } - * ``` + * 특정 역할의 멤버 수 조회 + * TODO: Redis 기반으로 변경 예정 * @param roomId 방 ID * @param role 역할 - * @return 해당 역할의 온라인 멤버 수 + * @return 해당 역할의 멤버 수 */ @Override + @Deprecated public int countByRoomIdAndRole(Long roomId, RoomRole role) { Long count = queryFactory .select(roomMember.count()) .from(roomMember) .where( roomMember.room.id.eq(roomId), - roomMember.role.eq(role), - roomMember.isOnline.eq(true) + roomMember.role.eq(role) ) .fetchOne(); @@ -298,75 +279,27 @@ public int countByRoomIdAndRole(Long roomId, RoomRole role) { /** * 방 퇴장 처리 (벌크 업데이트) - * - isOnline을 false로 변경 - * - connectionId를 null로 초기화 - * - * ai 코드 리뷰 결과 : - * - 한 번의 쿼리로 처리하여 성능 최적화 상태 - * - 벌크 연산은 영속성 컨텍스트를 무시 - * - 이후 해당 엔티티를 조회하면 DB와 불일치 가능 - * - 필요시 em.clear() 또는 em.refresh() 사용 - * ( 추후 기초 기능 개발 완료 후 개선 예정) - * - * - 사용자가 명시적으로 방을 나갈 때 - * - WebSocket 연결 끊김 감지 시 - * - 타임아웃으로 자동 퇴장 처리 시 + * TODO: Redis로 이관 예정 + * 현재는 아무 동작 안함 (DB에는 멤버십 유지) * @param roomId 방 ID * @param userId 사용자 ID */ @Override + @Deprecated public void leaveRoom(Long roomId, Long userId) { - queryFactory - .update(roomMember) - .set(roomMember.isOnline, false) - .setNull(roomMember.connectionId) - .where( - roomMember.room.id.eq(roomId), - roomMember.user.id.eq(userId) - ) - .execute(); + // Redis로 이관 예정 - 현재는 아무 동작 안함 + // DB의 멤버십은 유지됨 } /** * 방의 모든 멤버를 오프라인 처리 (방 종료 시) - * - 해당 방의 모든 멤버를 오프라인으로 변경 - * - 모든 멤버의 connectionId 제거 - * - * - 방장이 방을 종료할 때 - * - 방이 자동으로 종료될 때 (참가자 0명 + 일정 시간 경과) - * - 긴급 상황으로 방을 강제 종료할 때 - * - * ai 코드 리뷰 결과 : - * 해당 부분도 쿼리 한번으로 동작되는 내용이기 때문에, - * 그렇게 동작 시에는 웹소켓에 미리 종료 알림을 주는 형식으로 변경하라고 함. - * 이 작업 후 방 상태를 TERMINATED로 변경해야 함 - * - * 사용 예시: - * ```java - * @Transactional - * public void terminateRoom(Long roomId) { - * Room room = roomRepository.findById(roomId)...; - * - * // 모든 멤버 오프라인 처리 - * roomMemberRepository.disconnectAllMembers(roomId); - * - * // 방 종료 - * room.terminate(); - * - * // WebSocket으로 종료 알림 - * notifyRoomTermination(roomId); - * } - * ``` - * + * TODO: Redis로 이관 예정 + * 현재는 아무 동작 안함 * @param roomId 방 ID */ @Override + @Deprecated public void disconnectAllMembers(Long roomId) { - queryFactory - .update(roomMember) - .set(roomMember.isOnline, false) - .setNull(roomMember.connectionId) - .where(roomMember.room.id.eq(roomId)) - .execute(); + // Redis로 이관 예정 - 현재는 아무 동작 안함 } } diff --git a/src/main/java/com/back/domain/studyroom/repository/RoomRepository.java b/src/main/java/com/back/domain/studyroom/repository/RoomRepository.java index dea97567..9c11e14f 100644 --- a/src/main/java/com/back/domain/studyroom/repository/RoomRepository.java +++ b/src/main/java/com/back/domain/studyroom/repository/RoomRepository.java @@ -39,9 +39,11 @@ public interface RoomRepository extends JpaRepository, RoomRepositor Optional findByIdAndPassword(@Param("roomId") Long roomId, @Param("password") String password); // 참가자 수 업데이트 + // TODO: Redis 기반으로 변경 예정 - 현재는 사용하지 않음 + @Deprecated @Modifying @Query("UPDATE Room r SET r.currentParticipants = " + - "(SELECT COUNT(rm) FROM RoomMember rm WHERE rm.room.id = r.id AND rm.isOnline = true) " + + "(SELECT COUNT(rm) FROM RoomMember rm WHERE rm.room.id = r.id) " + "WHERE r.id = :roomId") void updateCurrentParticipants(@Param("roomId") Long roomId); } diff --git a/src/main/java/com/back/domain/studyroom/repository/RoomRepositoryImpl.java b/src/main/java/com/back/domain/studyroom/repository/RoomRepositoryImpl.java index bf0e81c2..d9ab9ac0 100644 --- a/src/main/java/com/back/domain/studyroom/repository/RoomRepositoryImpl.java +++ b/src/main/java/com/back/domain/studyroom/repository/RoomRepositoryImpl.java @@ -76,9 +76,9 @@ public Page findJoinablePublicRooms(Pageable pageable) { /** * 사용자가 참여 중인 방 조회 - * 조회 조건: - * - 특정 사용자가 멤버로 등록된 방 - * - 현재 온라인 상태인 방만 + * 조회 조건: + * - 특정 사용자가 멤버로 등록된 방 (DB에 저장된 멤버십) + * TODO: Redis에서 온라인 상태 확인하도록 변경 * @param userId 사용자 ID * @return 참여 중인 방 목록 */ @@ -88,10 +88,7 @@ public List findRoomsByUserId(Long userId) { .selectFrom(room) .leftJoin(room.createdBy, user).fetchJoin() // N+1 방지 .join(room.roomMembers, roomMember) // 멤버 조인 - .where( - roomMember.user.id.eq(userId), - roomMember.isOnline.eq(true) - ) + .where(roomMember.user.id.eq(userId)) .fetch(); } diff --git a/src/main/java/com/back/domain/studyroom/service/RoomService.java b/src/main/java/com/back/domain/studyroom/service/RoomService.java index c7d021e4..b6cfb956 100644 --- a/src/main/java/com/back/domain/studyroom/service/RoomService.java +++ b/src/main/java/com/back/domain/studyroom/service/RoomService.java @@ -123,10 +123,8 @@ public RoomMember joinRoom(Long roomId, String password, Long userId) { Optional existingMember = roomMemberRepository.findByRoomIdAndUserId(roomId, userId); if (existingMember.isPresent()) { RoomMember member = existingMember.get(); - if (member.isOnline()) { - throw new CustomException(ErrorCode.ALREADY_JOINED_ROOM); - } - member.updateOnlineStatus(true); + // TODO: Redis에서 온라인 여부 확인하도록 변경 + // 현재는 기존 멤버 재입장 허용 room.incrementParticipant(); return member; } @@ -162,14 +160,12 @@ public void leaveRoom(Long roomId, Long userId) { RoomMember member = roomMemberRepository.findByRoomIdAndUserId(roomId, userId) .orElseThrow(() -> new CustomException(ErrorCode.NOT_ROOM_MEMBER)); - if (!member.isOnline()) { - return; - } + // TODO: Redis에서 온라인 상태 확인하도록 변경 if (member.isHost()) { handleHostLeaving(room, member); } else { - member.leave(); + // TODO: Redis에서 제거하도록 변경 room.decrementParticipant(); } @@ -177,6 +173,7 @@ public void leaveRoom(Long roomId, Long userId) { } private void handleHostLeaving(Room room, RoomMember hostMember) { + // TODO: Redis에서 온라인 멤버 조회하도록 변경 List onlineMembers = roomMemberRepository.findOnlineMembersByRoomId(room.getId()); List otherOnlineMembers = onlineMembers.stream() @@ -185,7 +182,7 @@ private void handleHostLeaving(Room room, RoomMember hostMember) { if (otherOnlineMembers.isEmpty()) { room.terminate(); - hostMember.leave(); + // TODO: Redis에서 제거하도록 변경 room.decrementParticipant(); } else { RoomMember newHost = otherOnlineMembers.stream() @@ -197,7 +194,7 @@ private void handleHostLeaving(Room room, RoomMember hostMember) { if (newHost != null) { newHost.updateRole(RoomRole.HOST); - hostMember.leave(); + // TODO: Redis에서 제거하도록 변경 room.decrementParticipant(); log.info("새 방장 지정 - RoomId: {}, NewHostId: {}", @@ -261,7 +258,8 @@ public void terminateRoom(Long roomId, Long userId) { } room.terminate(); - roomMemberRepository.disconnectAllMembers(roomId); + // TODO: Redis에서 모든 멤버 제거하도록 변경 + // roomMemberRepository.disconnectAllMembers(roomId); log.info("방 종료 완료 - RoomId: {}, UserId: {}", roomId, userId); } @@ -337,7 +335,7 @@ public void kickMember(Long roomId, Long targetUserId, Long requesterId) { throw new CustomException(ErrorCode.CANNOT_KICK_HOST); } - targetMember.leave(); + // TODO: Redis에서 제거하도록 변경 Room room = roomRepository.findById(roomId) .orElseThrow(() -> new CustomException(ErrorCode.ROOM_NOT_FOUND)); diff --git a/src/main/java/com/back/global/websocket/webrtc/service/WebRTCSignalValidator.java b/src/main/java/com/back/global/websocket/webrtc/service/WebRTCSignalValidator.java index 6a8dfd96..e150cdc0 100644 --- a/src/main/java/com/back/global/websocket/webrtc/service/WebRTCSignalValidator.java +++ b/src/main/java/com/back/global/websocket/webrtc/service/WebRTCSignalValidator.java @@ -32,15 +32,17 @@ public void validateSignal(Long roomId, Long fromUserId, Long targetUserId) { } // 2. 발신자가 방에 속해있는지 확인 + // TODO: Redis에서 온라인 상태 확인하도록 변경 Optional fromMember = roomMemberRepository.findByRoomIdAndUserId(roomId, fromUserId); - if (fromMember.isEmpty() || !fromMember.get().isOnline()) { + if (fromMember.isEmpty()) { log.warn("방에 속하지 않은 사용자의 시그널 전송 시도 - roomId: {}, userId: {}", roomId, fromUserId); throw new CustomException(ErrorCode.NOT_ROOM_MEMBER); } // 3. 수신자가 같은 방에 속해있는지 확인 + // TODO: Redis에서 온라인 상태 확인하도록 변경 Optional targetMember = roomMemberRepository.findByRoomIdAndUserId(roomId, targetUserId); - if (targetMember.isEmpty() || !targetMember.get().isOnline()) { + if (targetMember.isEmpty()) { log.warn("수신자가 방에 없거나 오프라인 상태 - roomId: {}, targetUserId: {}", roomId, targetUserId); throw new CustomException(ErrorCode.NOT_ROOM_MEMBER); } @@ -50,9 +52,10 @@ public void validateSignal(Long roomId, Long fromUserId, Long targetUserId) { // 미디어 상태 변경 검증 public void validateMediaStateChange(Long roomId, Long userId) { + // TODO: Redis에서 온라인 상태 확인하도록 변경 Optional member = roomMemberRepository.findByRoomIdAndUserId(roomId, userId); - if (member.isEmpty() || !member.get().isOnline()) { + if (member.isEmpty()) { log.warn("방에 속하지 않은 사용자의 미디어 상태 변경 시도 - roomId: {}, userId: {}", roomId, userId); throw new CustomException(ErrorCode.NOT_ROOM_MEMBER); } diff --git a/src/test/java/com/back/domain/studyroom/service/RoomServiceTest.java b/src/test/java/com/back/domain/studyroom/service/RoomServiceTest.java index 4ccd73dd..d7262668 100644 --- a/src/test/java/com/back/domain/studyroom/service/RoomServiceTest.java +++ b/src/test/java/com/back/domain/studyroom/service/RoomServiceTest.java @@ -185,7 +185,7 @@ void joinRoom_WrongPassword() { @DisplayName("방 나가기 - 성공") void leaveRoom_Success() { // given - testMember.updateOnlineStatus(true); + // TODO: Redis 통합 후 온라인 상태 체크 추가 예정 given(roomRepository.findById(1L)).willReturn(Optional.of(testRoom)); given(roomMemberRepository.findByRoomIdAndUserId(1L, 1L)).willReturn(Optional.of(testMember)); @@ -303,7 +303,7 @@ void updateRoomSettings_NotOwner() { void terminateRoom_Success() { // given given(roomRepository.findById(1L)).willReturn(Optional.of(testRoom)); - willDoNothing().given(roomMemberRepository).disconnectAllMembers(1L); + // disconnectAllMembers는 더 이상 호출되지 않음 (Redis로 이관 예정) // when roomService.terminateRoom(1L, 1L); @@ -311,7 +311,7 @@ void terminateRoom_Success() { // then assertThat(testRoom.getStatus()).isEqualTo(RoomStatus.TERMINATED); assertThat(testRoom.isActive()).isFalse(); - verify(roomMemberRepository, times(1)).disconnectAllMembers(1L); + // verify 제거: disconnectAllMembers는 더 이상 호출되지 않음 } @Test diff --git a/src/test/java/com/back/global/websocket/webrtc/service/WebRTCSignalValidatorTest.java b/src/test/java/com/back/global/websocket/webrtc/service/WebRTCSignalValidatorTest.java index fef7bda8..f14b02e8 100644 --- a/src/test/java/com/back/global/websocket/webrtc/service/WebRTCSignalValidatorTest.java +++ b/src/test/java/com/back/global/websocket/webrtc/service/WebRTCSignalValidatorTest.java @@ -51,19 +51,12 @@ void setUp() { User fromUser = mock(User.class); User targetUser = mock(User.class); - // 온라인 멤버들 + // 온라인/오프라인 구분은 Redis로 이관 예정 + // 현재는 멤버 존재 여부만 체크 onlineFromMember = RoomMember.createMember(mockRoom, fromUser); - onlineFromMember.updateOnlineStatus(true); - onlineTargetMember = RoomMember.createMember(mockRoom, targetUser); - onlineTargetMember.updateOnlineStatus(true); - - // 오프라인 멤버들 offlineFromMember = RoomMember.createMember(mockRoom, fromUser); - offlineFromMember.updateOnlineStatus(false); - offlineTargetMember = RoomMember.createMember(mockRoom, targetUser); - offlineTargetMember.updateOnlineStatus(false); } @Nested @@ -114,24 +107,9 @@ void t3() { verify(roomMemberRepository).findByRoomIdAndUserId(roomId, fromUserId); } - @Test - @DisplayName("실패 - 발신자가 오프라인") - void t4() { - // given - given(roomMemberRepository.findByRoomIdAndUserId(roomId, fromUserId)) - .willReturn(Optional.of(offlineFromMember)); - - // when & then - assertThatThrownBy(() -> validator.validateSignal(roomId, fromUserId, targetUserId)) - .isInstanceOf(CustomException.class) - .hasFieldOrPropertyWithValue("errorCode", ErrorCode.NOT_ROOM_MEMBER); - - verify(roomMemberRepository).findByRoomIdAndUserId(roomId, fromUserId); - } - @Test @DisplayName("실패 - 수신자가 방에 없음") - void t5() { + void t4() { // given given(roomMemberRepository.findByRoomIdAndUserId(roomId, fromUserId)) .willReturn(Optional.of(onlineFromMember)); @@ -146,39 +124,6 @@ void t5() { verify(roomMemberRepository).findByRoomIdAndUserId(roomId, fromUserId); verify(roomMemberRepository).findByRoomIdAndUserId(roomId, targetUserId); } - - @Test - @DisplayName("실패 - 수신자가 오프라인") - void t6() { - // given - given(roomMemberRepository.findByRoomIdAndUserId(roomId, fromUserId)) - .willReturn(Optional.of(onlineFromMember)); - given(roomMemberRepository.findByRoomIdAndUserId(roomId, targetUserId)) - .willReturn(Optional.of(offlineTargetMember)); - - // when & then - assertThatThrownBy(() -> validator.validateSignal(roomId, fromUserId, targetUserId)) - .isInstanceOf(CustomException.class) - .hasFieldOrPropertyWithValue("errorCode", ErrorCode.NOT_ROOM_MEMBER); - - verify(roomMemberRepository).findByRoomIdAndUserId(roomId, fromUserId); - verify(roomMemberRepository).findByRoomIdAndUserId(roomId, targetUserId); - } - - @Test - @DisplayName("실패 - 발신자와 수신자 모두 오프라인") - void t7() { - // given - given(roomMemberRepository.findByRoomIdAndUserId(roomId, fromUserId)) - .willReturn(Optional.of(offlineFromMember)); - - // when & then - assertThatThrownBy(() -> validator.validateSignal(roomId, fromUserId, targetUserId)) - .isInstanceOf(CustomException.class) - .hasFieldOrPropertyWithValue("errorCode", ErrorCode.NOT_ROOM_MEMBER); - - verify(roomMemberRepository).findByRoomIdAndUserId(roomId, fromUserId); - } } @Nested @@ -186,8 +131,8 @@ void t7() { class ValidateMediaStateChangeTest { @Test - @DisplayName("정상 - 온라인 멤버") - void t8() { + @DisplayName("정상 - 멤버 존재") + void t5() { // given given(roomMemberRepository.findByRoomIdAndUserId(roomId, fromUserId)) .willReturn(Optional.of(onlineFromMember)); @@ -201,7 +146,7 @@ void t8() { @Test @DisplayName("실패 - 방에 없는 사용자") - void t9() { + void t6() { // given given(roomMemberRepository.findByRoomIdAndUserId(roomId, fromUserId)) .willReturn(Optional.empty()); @@ -215,30 +160,14 @@ void t9() { } @Test - @DisplayName("실패 - 오프라인 사용자") - void t10() { - // given - given(roomMemberRepository.findByRoomIdAndUserId(roomId, fromUserId)) - .willReturn(Optional.of(offlineFromMember)); - - // when & then - assertThatThrownBy(() -> validator.validateMediaStateChange(roomId, fromUserId)) - .isInstanceOf(CustomException.class) - .hasFieldOrPropertyWithValue("errorCode", ErrorCode.NOT_ROOM_MEMBER); - - verify(roomMemberRepository).findByRoomIdAndUserId(roomId, fromUserId); - } - - @Test - @DisplayName("정상 - 다른 방의 온라인 멤버") - void t11() { + @DisplayName("정상 - 다른 방의 멤버") + void t7() { // given Long differentRoomId = 999L; Room differentRoom = mock(Room.class); User user = mock(User.class); RoomMember memberInDifferentRoom = RoomMember.createMember(differentRoom, user); - memberInDifferentRoom.updateOnlineStatus(true); given(roomMemberRepository.findByRoomIdAndUserId(differentRoomId, fromUserId)) .willReturn(Optional.of(memberInDifferentRoom));