Skip to content

Commit 26c0526

Browse files
authored
Merge pull request #232 from prgrms-web-devcourse-final-project/Refactor/228
Refactor: 채팅 도메인 리팩토링 (#228)
2 parents 48a1138 + fd7faed commit 26c0526

File tree

11 files changed

+306
-1200
lines changed

11 files changed

+306
-1200
lines changed

src/main/java/com/back/domain/chat/room/controller/RoomChatApiController.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import org.springframework.format.annotation.DateTimeFormat;
1515
import org.springframework.http.HttpStatus;
1616
import org.springframework.http.ResponseEntity;
17-
import org.springframework.messaging.simp.SimpMessagingTemplate;
1817
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1918
import org.springframework.web.bind.annotation.*;
2019

@@ -27,7 +26,6 @@
2726
public class RoomChatApiController {
2827

2928
private final RoomChatService roomChatService;
30-
private final SimpMessagingTemplate messagingTemplate;
3129

3230
// 방 채팅 메시지 조회 (페이징, 특정 시간 이전 메시지)
3331
@GetMapping
@@ -73,18 +71,6 @@ public ResponseEntity<RsData<ChatClearResponse>> clearRoomMessages(
7371
clearedByInfo.role()
7472
);
7573

76-
// WebSocket을 통해 실시간 알림 전송
77-
ChatClearedNotification notification = ChatClearedNotification.create(
78-
roomId,
79-
messageCount,
80-
clearedByInfo.userId(),
81-
clearedByInfo.nickname(),
82-
clearedByInfo.profileImageUrl(),
83-
clearedByInfo.role()
84-
);
85-
86-
messagingTemplate.convertAndSend("/topic/room/" + roomId + "/chat-cleared", notification);
87-
8874
return ResponseEntity
8975
.status(HttpStatus.OK)
9076
.body(RsData.success("채팅 메시지 일괄 삭제 완료", responseData));
Lines changed: 23 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,33 @@
11
package com.back.domain.chat.room.controller;
22

3-
import com.back.domain.chat.room.dto.ChatClearRequest;
4-
import com.back.domain.chat.room.dto.ChatClearedNotification;
3+
import com.back.domain.chat.room.dto.RoomChatMessageRequest;
4+
import com.back.domain.chat.room.dto.RoomChatMessageResponse;
55
import com.back.domain.studyroom.entity.RoomChatMessage;
6-
import com.back.domain.chat.room.dto.RoomChatMessageDto;
76
import com.back.global.exception.CustomException;
7+
import com.back.global.exception.ErrorCode;
88
import com.back.global.security.user.CustomUserDetails;
99
import com.back.domain.chat.room.service.RoomChatService;
1010
import com.back.global.websocket.util.WebSocketAuthHelper;
1111
import com.back.global.websocket.util.WebSocketErrorHelper;
12-
import io.swagger.v3.oas.annotations.tags.Tag;
1312
import lombok.RequiredArgsConstructor;
1413
import lombok.extern.slf4j.Slf4j;
1514
import org.springframework.messaging.handler.annotation.DestinationVariable;
15+
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
1616
import org.springframework.messaging.handler.annotation.MessageMapping;
1717
import org.springframework.messaging.handler.annotation.Payload;
1818
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
1919
import org.springframework.messaging.simp.SimpMessagingTemplate;
20-
import org.springframework.security.core.Authentication;
2120
import org.springframework.stereotype.Controller;
2221

2322
import java.security.Principal;
2423

2524
@Slf4j
2625
@Controller
2726
@RequiredArgsConstructor
28-
@Tag(name = "RoomChat WebSocket", description = "STOMP를 이용한 실시간 채팅 WebSocket 컨트롤러 (Swagger에서 직접 테스트 불가)")
2927
public class RoomChatWebSocketController {
3028

3129
private final RoomChatService roomChatService;
3230
private final SimpMessagingTemplate messagingTemplate;
33-
private final WebSocketAuthHelper authHelper;
3431
private final WebSocketErrorHelper errorHelper;
3532

3633
/**
@@ -39,117 +36,30 @@ public class RoomChatWebSocketController {
3936
*/
4037
@MessageMapping("/chat/room/{roomId}")
4138
public void handleRoomChat(@DestinationVariable Long roomId,
42-
RoomChatMessageDto chatMessage,
43-
SimpMessageHeaderAccessor headerAccessor,
39+
@Payload RoomChatMessageRequest request,
4440
Principal principal) {
4541

46-
try {
47-
// WebSocket에서 인증된 사용자 정보 추출
48-
CustomUserDetails userDetails = authHelper.extractUserDetails(principal);
49-
50-
if (userDetails == null) {
51-
errorHelper.sendUnauthorizedError(headerAccessor.getSessionId());
52-
return;
53-
}
54-
55-
Long currentUserId = userDetails.getUserId();
56-
String currentUserNickname = userDetails.getUsername();
57-
58-
// 메시지 정보 보완
59-
RoomChatMessageDto enrichedMessage = chatMessage
60-
.withRoomId(roomId)
61-
.withUserId(currentUserId)
62-
.withNickname(currentUserNickname);
63-
64-
// DB에 메시지 저장
65-
RoomChatMessage savedMessage = roomChatService.saveRoomChatMessage(enrichedMessage);
66-
67-
// 저장된 메시지 정보로 응답 DTO 생성
68-
RoomChatMessageDto responseMessage = RoomChatMessageDto.createResponse(
69-
savedMessage.getId(),
70-
roomId,
71-
savedMessage.getUser().getId(),
72-
savedMessage.getUser().getNickname(),
73-
savedMessage.getUser().getProfileImageUrl(),
74-
savedMessage.getContent(),
75-
chatMessage.messageType(),
76-
null, // 텍스트 채팅에서는 null
77-
savedMessage.getCreatedAt()
78-
);
79-
80-
// 해당 방의 모든 구독자에게 브로드캐스트
81-
messagingTemplate.convertAndSend("/topic/room/" + roomId, responseMessage);
82-
83-
} catch (CustomException e) {
84-
log.warn("채팅 메시지 처리 실패 - roomId: {}, error: {}", roomId, e.getMessage());
85-
errorHelper.sendCustomExceptionToUser(headerAccessor.getSessionId(), e);
86-
87-
} catch (Exception e) {
88-
log.error("채팅 메시지 처리 중 예상치 못한 오류 발생 - roomId: {}", roomId, e);
89-
errorHelper.sendGenericErrorToUser(headerAccessor.getSessionId(), e, "메시지 전송 중 오류가 발생했습니다");
42+
CustomUserDetails userDetails = WebSocketAuthHelper.extractUserDetails(principal);
43+
if (userDetails == null) {
44+
// 인증 정보가 없는 경우 예외 처리
45+
throw new CustomException(ErrorCode.UNAUTHORIZED);
9046
}
91-
}
92-
93-
/**
94-
* 스터디룸 채팅 일괄 삭제 처리
95-
* 클라이언트가 /app/chat/room/{roomId}/clear로 삭제 요청 시 호출
96-
*/
97-
@MessageMapping("/chat/room/{roomId}/clear")
98-
public void clearRoomChat(@DestinationVariable Long roomId,
99-
@Payload ChatClearRequest request,
100-
SimpMessageHeaderAccessor headerAccessor,
101-
Principal principal) {
10247

103-
try {
104-
log.info("WebSocket 채팅 일괄 삭제 요청 - roomId: {}", roomId);
48+
RoomChatMessage savedMessage = roomChatService.saveRoomChatMessage(
49+
roomId,
50+
userDetails.getUserId(),
51+
request
52+
);
10553

106-
// 사용자 인증 확인
107-
CustomUserDetails userDetails = authHelper.extractUserDetails(principal);
108-
109-
if (userDetails == null) {
110-
errorHelper.sendUnauthorizedError(headerAccessor.getSessionId());
111-
return;
112-
}
113-
114-
// 삭제 확인 메시지 검증
115-
if (!request.isValidConfirmMessage()) {
116-
errorHelper.sendErrorToUser(headerAccessor.getSessionId(), "WS_011",
117-
"삭제 확인 메시지가 일치하지 않습니다");
118-
return;
119-
}
120-
121-
Long currentUserId = userDetails.getUserId();
122-
123-
// 삭제 전에 메시지 수 먼저 조회 (삭제 후에는 0이 되므로)
124-
int deletedCount = roomChatService.getRoomChatCount(roomId);
125-
126-
// 채팅 일괄 삭제 실행
127-
ChatClearedNotification.ClearedByDto clearedByInfo = roomChatService.clearRoomChat(roomId, currentUserId);
128-
129-
// 알림 생성
130-
ChatClearedNotification notification = ChatClearedNotification.create(
131-
roomId,
132-
deletedCount, // 삭제 전에 조회한 수 사용
133-
clearedByInfo.userId(),
134-
clearedByInfo.nickname(),
135-
clearedByInfo.profileImageUrl(),
136-
clearedByInfo.role()
137-
);
138-
139-
// 해당 방의 모든 구독자에게 브로드캐스트
140-
messagingTemplate.convertAndSend("/topic/room/" + roomId + "/chat-cleared", notification);
141-
142-
log.info("WebSocket 채팅 일괄 삭제 완료 - roomId: {}, deletedCount: {}, userId: {}",
143-
roomId, deletedCount, currentUserId);
144-
145-
} catch (CustomException e) {
146-
log.warn("채팅 삭제 실패 - roomId: {}, error: {}", roomId, e.getMessage());
147-
errorHelper.sendCustomExceptionToUser(headerAccessor.getSessionId(), e);
148-
149-
} catch (Exception e) {
150-
log.error("채팅 일괄 삭제 중 예상치 못한 오류 발생 - roomId: {}", roomId, e);
151-
errorHelper.sendGenericErrorToUser(headerAccessor.getSessionId(), e, "채팅 삭제 중 오류가 발생했습니다");
152-
}
54+
// 응답 DTO 생성 및 브로드캐스트
55+
RoomChatMessageResponse responseMessage = RoomChatMessageResponse.from(savedMessage);
56+
messagingTemplate.convertAndSend("/topic/room/" + roomId, responseMessage);
15357
}
15458

59+
// 채팅 메시지 처리 중 발생하는 예외 중앙 처리
60+
@MessageExceptionHandler(CustomException.class)
61+
public void handleChatException(CustomException e, SimpMessageHeaderAccessor headerAccessor) {
62+
log.warn("채팅 메시지 처리 실패 - SessionId: {}, Error: {}", headerAccessor.getSessionId(), e.getMessage());
63+
errorHelper.sendCustomExceptionToUser(headerAccessor.getSessionId(), e);
64+
}
15565
}

src/main/java/com/back/domain/chat/room/dto/RoomChatMessageDto.java

Lines changed: 0 additions & 69 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.back.domain.chat.room.dto;
2+
3+
public record RoomChatMessageRequest(
4+
String content
5+
) {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.back.domain.chat.room.dto;
2+
3+
import com.back.domain.studyroom.entity.RoomChatMessage;
4+
5+
import java.time.LocalDateTime;
6+
7+
public record RoomChatMessageResponse(
8+
Long messageId,
9+
Long roomId,
10+
Long userId,
11+
String nickname,
12+
String profileImageUrl,
13+
String content,
14+
LocalDateTime createdAt
15+
) {
16+
public static RoomChatMessageResponse from(RoomChatMessage entity) {
17+
return new RoomChatMessageResponse(
18+
entity.getId(),
19+
entity.getRoom().getId(),
20+
entity.getUser().getId(),
21+
entity.getUser().getNickname(),
22+
entity.getUser().getProfileImageUrl(),
23+
entity.getContent(),
24+
entity.getCreatedAt()
25+
);
26+
}
27+
}

src/main/java/com/back/domain/chat/room/dto/RoomChatPageResponse.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.util.List;
44

55
public record RoomChatPageResponse(
6-
List<RoomChatMessageDto> content,
6+
List<RoomChatMessageResponse> content,
77
PageableDto pageable,
88
long totalElements
99
) {
@@ -18,7 +18,7 @@ public record PageableDto(
1818
// 페이지 응답 생성
1919
public static RoomChatPageResponse from(
2020
org.springframework.data.domain.Page<?> page,
21-
List<RoomChatMessageDto> convertedContent) {
21+
List<RoomChatMessageResponse> convertedContent) {
2222

2323
return new RoomChatPageResponse(
2424
convertedContent,
@@ -41,7 +41,7 @@ public static RoomChatPageResponse empty(int page, int size) {
4141
}
4242

4343
// 단일 페이지 응답 생성 (테스트용)
44-
public static RoomChatPageResponse of(List<RoomChatMessageDto> content,
44+
public static RoomChatPageResponse of(List<RoomChatMessageResponse> content,
4545
int page,
4646
int size,
4747
boolean hasNext,

0 commit comments

Comments
 (0)