Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public ResponseEntity<RsData<RoomResponse>> createRoom(
currentUserId
);

RoomResponse response = RoomResponse.from(room);
RoomResponse response = roomService.toRoomResponse(room);

return ResponseEntity
.status(HttpStatus.CREATED)
Expand Down Expand Up @@ -139,9 +139,7 @@ public ResponseEntity<RsData<Map<String, Object>>> getRooms(
Pageable pageable = PageRequest.of(page, size);
Page<Room> rooms = roomService.getJoinableRooms(pageable);

List<RoomResponse> roomList = rooms.getContent().stream()
.map(RoomResponse::from)
.collect(Collectors.toList());
List<RoomResponse> roomList = roomService.toRoomResponseList(rooms.getContent());

Map<String, Object> response = new HashMap<>();
response.put("rooms", roomList);
Expand Down Expand Up @@ -175,11 +173,7 @@ public ResponseEntity<RsData<RoomDetailResponse>> getRoomDetail(
Room room = roomService.getRoomDetail(roomId, currentUserId);
List<RoomMember> members = roomService.getRoomMembers(roomId, currentUserId);

List<RoomMemberResponse> memberResponses = members.stream()
.map(RoomMemberResponse::from)
.collect(Collectors.toList());

RoomDetailResponse response = RoomDetailResponse.of(room, memberResponses);
RoomDetailResponse response = roomService.toRoomDetailResponse(room, members);

return ResponseEntity
.status(HttpStatus.OK)
Expand All @@ -201,12 +195,7 @@ public ResponseEntity<RsData<List<MyRoomResponse>>> getMyRooms() {

List<Room> rooms = roomService.getUserRooms(currentUserId);

List<MyRoomResponse> roomList = rooms.stream()
.map(room -> MyRoomResponse.of(
room,
roomService.getUserRoomRole(room.getId(), currentUserId)
))
.collect(Collectors.toList());
List<MyRoomResponse> roomList = roomService.toMyRoomResponseList(rooms, currentUserId);

return ResponseEntity
.status(HttpStatus.OK)
Expand Down Expand Up @@ -313,9 +302,7 @@ public ResponseEntity<RsData<Map<String, Object>>> getPopularRooms(
Pageable pageable = PageRequest.of(page, size);
Page<Room> rooms = roomService.getPopularRooms(pageable);

List<RoomResponse> roomList = rooms.getContent().stream()
.map(RoomResponse::from)
.collect(Collectors.toList());
List<RoomResponse> roomList = roomService.toRoomResponseList(rooms.getContent());

Map<String, Object> response = new HashMap<>();
response.put("rooms", roomList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ public class MyRoomResponse {
private RoomRole myRole;
private LocalDateTime createdAt;

public static MyRoomResponse of(Room room, RoomRole myRole) {
public static MyRoomResponse of(Room room, long currentParticipants, RoomRole myRole) {
return MyRoomResponse.builder()
.roomId(room.getId())
.title(room.getTitle())
.description(room.getDescription() != null ? room.getDescription() : "")
.currentParticipants(room.getCurrentParticipants())
.currentParticipants((int) currentParticipants) // Redis에서 조회한 실시간 값
.maxParticipants(room.getMaxParticipants())
.status(room.getStatus())
.myRole(myRole)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ public class RoomDetailResponse {
private LocalDateTime createdAt;
private List<RoomMemberResponse> members;

public static RoomDetailResponse of(Room room, List<RoomMemberResponse> members) {
public static RoomDetailResponse of(Room room, long currentParticipants, List<RoomMemberResponse> members) {
return RoomDetailResponse.builder()
.roomId(room.getId())
.title(room.getTitle())
.description(room.getDescription() != null ? room.getDescription() : "")
.isPrivate(room.isPrivate())
.maxParticipants(room.getMaxParticipants())
.currentParticipants(room.getCurrentParticipants())
.currentParticipants((int) currentParticipants) // Redis에서 조회한 실시간 값
.status(room.getStatus())
.allowCamera(room.isAllowCamera())
.allowAudio(room.isAllowAudio())
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/back/domain/studyroom/dto/RoomResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ public class RoomResponse {
private String createdBy;
private LocalDateTime createdAt;

public static RoomResponse from(Room room) {
public static RoomResponse from(Room room, long currentParticipants) {
return RoomResponse.builder()
.roomId(room.getId())
.title(room.getTitle())
.description(room.getDescription() != null ? room.getDescription() : "")
.currentParticipants(room.getCurrentParticipants())
.currentParticipants((int) currentParticipants) // Redis에서 조회한 실시간 값
.maxParticipants(room.getMaxParticipants())
.status(room.getStatus())
.createdBy(room.getCreatedBy().getNickname())
Expand Down
86 changes: 80 additions & 6 deletions src/main/java/com/back/domain/studyroom/service/RoomService.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class RoomService {
private final RoomMemberRepository roomMemberRepository;
private final UserRepository userRepository;
private final StudyRoomProperties properties;
private final com.back.global.websocket.service.WebSocketSessionManager sessionManager;

/**
* 방 생성 메서드
Expand Down Expand Up @@ -67,7 +68,7 @@ public Room createRoom(String title, String description, boolean isPrivate,
RoomMember hostMember = RoomMember.createHost(savedRoom, creator);
roomMemberRepository.save(hostMember);

savedRoom.incrementParticipant();
// savedRoom.incrementParticipant(); // Redis로 이관 - DB 업데이트 제거

log.info("방 생성 완료 - RoomId: {}, Title: {}, CreatorId: {}",
savedRoom.getId(), title, creatorId);
Expand Down Expand Up @@ -125,14 +126,15 @@ public RoomMember joinRoom(Long roomId, String password, Long userId) {
RoomMember member = existingMember.get();
// TODO: Redis에서 온라인 여부 확인하도록 변경
// 현재는 기존 멤버 재입장 허용
// room.incrementParticipant(); // Redis로 이관 - DB 업데이트 제거
room.incrementParticipant();
return member;
}

RoomMember newMember = RoomMember.createVisitor(room, user);
RoomMember savedMember = roomMemberRepository.save(newMember);

room.incrementParticipant();
// room.incrementParticipant(); // Redis로 이관 - DB 업데이트 제거

log.info("방 입장 완료 - RoomId: {}, UserId: {}, Role: {}",
roomId, userId, newMember.getRole());
Expand Down Expand Up @@ -166,7 +168,7 @@ public void leaveRoom(Long roomId, Long userId) {
handleHostLeaving(room, member);
} else {
// TODO: Redis에서 제거하도록 변경
room.decrementParticipant();
// room.decrementParticipant(); // Redis로 이관 - DB 업데이트 제거
}

log.info("방 퇴장 완료 - RoomId: {}, UserId: {}", roomId, userId);
Expand All @@ -183,7 +185,7 @@ private void handleHostLeaving(Room room, RoomMember hostMember) {
if (otherOnlineMembers.isEmpty()) {
room.terminate();
// TODO: Redis에서 제거하도록 변경
room.decrementParticipant();
// room.decrementParticipant(); // Redis로 이관 - DB 업데이트 제거
} else {
RoomMember newHost = otherOnlineMembers.stream()
.filter(m -> m.getRole() == RoomRole.SUB_HOST)
Expand All @@ -195,7 +197,7 @@ private void handleHostLeaving(Room room, RoomMember hostMember) {
if (newHost != null) {
newHost.updateRole(RoomRole.HOST);
// TODO: Redis에서 제거하도록 변경
room.decrementParticipant();
// room.decrementParticipant(); // Redis로 이관 - DB 업데이트 제거

log.info("새 방장 지정 - RoomId: {}, NewHostId: {}",
room.getId(), newHost.getUser().getId());
Expand Down Expand Up @@ -339,9 +341,81 @@ public void kickMember(Long roomId, Long targetUserId, Long requesterId) {

Room room = roomRepository.findById(roomId)
.orElseThrow(() -> new CustomException(ErrorCode.ROOM_NOT_FOUND));
room.decrementParticipant();
// room.decrementParticipant(); // Redis로 이관 - DB 업데이트 제거

log.info("멤버 추방 완료 - RoomId: {}, TargetUserId: {}, RequesterId: {}",
roomId, targetUserId, requesterId);
}

// ==================== DTO 생성 헬퍼 메서드 ====================

/**
* RoomResponse 생성 (Redis에서 실시간 참가자 수 조회)
*/
public com.back.domain.studyroom.dto.RoomResponse toRoomResponse(Room room) {
long onlineCount = sessionManager.getRoomOnlineUserCount(room.getId());
return com.back.domain.studyroom.dto.RoomResponse.from(room, onlineCount);
}

/**
* RoomResponse 리스트 생성 (일괄 조회로 N+1 방지)
*/
public java.util.List<com.back.domain.studyroom.dto.RoomResponse> toRoomResponseList(java.util.List<Room> rooms) {
java.util.List<Long> roomIds = rooms.stream()
.map(Room::getId)
.collect(java.util.stream.Collectors.toList());

java.util.Map<Long, Long> participantCounts = sessionManager.getBulkRoomOnlineUserCounts(roomIds);

return rooms.stream()
.map(room -> com.back.domain.studyroom.dto.RoomResponse.from(
room,
participantCounts.getOrDefault(room.getId(), 0L)
))
.collect(java.util.stream.Collectors.toList());
}

/**
* RoomDetailResponse 생성 (Redis에서 실시간 참가자 수 조회)
*/
public com.back.domain.studyroom.dto.RoomDetailResponse toRoomDetailResponse(
Room room,
java.util.List<com.back.domain.studyroom.entity.RoomMember> members) {
long onlineCount = sessionManager.getRoomOnlineUserCount(room.getId());

java.util.List<com.back.domain.studyroom.dto.RoomMemberResponse> memberResponses = members.stream()
.map(com.back.domain.studyroom.dto.RoomMemberResponse::from)
.collect(java.util.stream.Collectors.toList());

return com.back.domain.studyroom.dto.RoomDetailResponse.of(room, onlineCount, memberResponses);
}

/**
* MyRoomResponse 생성 (Redis에서 실시간 참가자 수 조회)
*/
public com.back.domain.studyroom.dto.MyRoomResponse toMyRoomResponse(Room room, RoomRole myRole) {
long onlineCount = sessionManager.getRoomOnlineUserCount(room.getId());
return com.back.domain.studyroom.dto.MyRoomResponse.of(room, onlineCount, myRole);
}

/**
* MyRoomResponse 리스트 생성 (일괄 조회로 N+1 방지)
*/
public java.util.List<com.back.domain.studyroom.dto.MyRoomResponse> toMyRoomResponseList(
java.util.List<Room> rooms,
Long userId) {
java.util.List<Long> roomIds = rooms.stream()
.map(Room::getId)
.collect(java.util.stream.Collectors.toList());

java.util.Map<Long, Long> participantCounts = sessionManager.getBulkRoomOnlineUserCounts(roomIds);

return rooms.stream()
.map(room -> {
RoomRole role = getUserRoomRole(room.getId(), userId);
long count = participantCounts.getOrDefault(room.getId(), 0L);
return com.back.domain.studyroom.dto.MyRoomResponse.of(room, count, role);
})
.collect(java.util.stream.Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,13 @@ public Long getUserCurrentRoomId(Long userId) {
public boolean isUserInRoom(Long userId, Long roomId) {
return roomParticipantService.isUserInRoom(userId, roomId);
}

// 여러 방의 온라인 사용자 수 일괄 조회 (N+1 방지)
public java.util.Map<Long, Long> getBulkRoomOnlineUserCounts(java.util.List<Long> roomIds) {
return roomIds.stream()
.collect(java.util.stream.Collectors.toMap(
roomId -> roomId,
this::getRoomOnlineUserCount
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,13 @@ void createRoom() {
anyInt(),
eq(1L)
)).willReturn(testRoom);

RoomResponse roomResponse = RoomResponse.from(testRoom, 1);
given(roomService.toRoomResponse(any(Room.class))).willReturn(roomResponse);

// when
ResponseEntity<RsData<RoomResponse>> response = roomController.createRoom(request);

// then
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(response.getBody()).isNotNull();
Expand All @@ -123,6 +127,7 @@ void createRoom() {
anyInt(),
eq(1L)
);
verify(roomService, times(1)).toRoomResponse(any(Room.class));
}

@Test
Expand Down Expand Up @@ -174,6 +179,9 @@ void getRooms() {
1
);
given(roomService.getJoinableRooms(any())).willReturn(roomPage);

List<RoomResponse> roomResponses = Arrays.asList(RoomResponse.from(testRoom, 1));
given(roomService.toRoomResponseList(anyList())).willReturn(roomResponses);

// when
ResponseEntity<RsData<Map<String, Object>>> response = roomController.getRooms(0, 20);
Expand All @@ -185,6 +193,7 @@ void getRooms() {
assertThat(response.getBody().getData().get("rooms")).isNotNull();

verify(roomService, times(1)).getJoinableRooms(any());
verify(roomService, times(1)).toRoomResponseList(anyList());
}

@Test
Expand All @@ -195,6 +204,13 @@ void getRoomDetail() {

given(roomService.getRoomDetail(eq(1L), eq(1L))).willReturn(testRoom);
given(roomService.getRoomMembers(eq(1L), eq(1L))).willReturn(Arrays.asList(testMember));

RoomDetailResponse roomDetailResponse = RoomDetailResponse.of(
testRoom,
1,
Arrays.asList(RoomMemberResponse.from(testMember))
);
given(roomService.toRoomDetailResponse(any(Room.class), anyList())).willReturn(roomDetailResponse);

// when
ResponseEntity<RsData<RoomDetailResponse>> response = roomController.getRoomDetail(1L);
Expand All @@ -208,6 +224,7 @@ void getRoomDetail() {
verify(currentUser, times(1)).getUserId();
verify(roomService, times(1)).getRoomDetail(eq(1L), eq(1L));
verify(roomService, times(1)).getRoomMembers(eq(1L), eq(1L));
verify(roomService, times(1)).toRoomDetailResponse(any(Room.class), anyList());
}

@Test
Expand All @@ -226,7 +243,11 @@ void getMyRooms() {
}

given(roomService.getUserRooms(eq(1L))).willReturn(Arrays.asList(testRoom));
given(roomService.getUserRoomRole(eq(1L), eq(1L))).willReturn(RoomRole.HOST);

List<MyRoomResponse> myRoomResponses = Arrays.asList(
MyRoomResponse.of(testRoom, 1, RoomRole.HOST)
);
given(roomService.toMyRoomResponseList(anyList(), eq(1L))).willReturn(myRoomResponses);

// when
ResponseEntity<RsData<List<MyRoomResponse>>> response = roomController.getMyRooms();
Expand All @@ -240,6 +261,7 @@ void getMyRooms() {

verify(currentUser, times(1)).getUserId();
verify(roomService, times(1)).getUserRooms(eq(1L));
verify(roomService, times(1)).toMyRoomResponseList(anyList(), eq(1L));
}

@Test
Expand Down Expand Up @@ -328,6 +350,9 @@ void getPopularRooms() {
1
);
given(roomService.getPopularRooms(any())).willReturn(roomPage);

List<RoomResponse> roomResponses = Arrays.asList(RoomResponse.from(testRoom, 1));
given(roomService.toRoomResponseList(anyList())).willReturn(roomResponses);

// when
ResponseEntity<RsData<Map<String, Object>>> response = roomController.getPopularRooms(0, 20);
Expand All @@ -339,5 +364,6 @@ void getPopularRooms() {
assertThat(response.getBody().getData().get("rooms")).isNotNull();

verify(roomService, times(1)).getPopularRooms(any());
verify(roomService, times(1)).toRoomResponseList(anyList());
}
}