From 566ab4a77e50ef19e8d59f4281abc56f1ed19543 Mon Sep 17 00:00:00 2001 From: Sukyeong Sung Date: Wed, 26 Feb 2025 10:19:52 +0900 Subject: [PATCH 1/5] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=A1=B0=ED=9A=8C,=20=EB=B0=A9=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20=EC=B1=84=ED=8C=85=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ChatController.java | 110 +++++++++--------- .../controller/ChatMessageController.java | 71 +++++++++++ .../controller/request/ChatMsgRequest.java | 17 +++ .../controller/response/ChatMsgResponse.java | 18 +++ .../chat/chatMessage/dto/ChatMsgDto.java | 20 ++++ .../entity/{ChatMessage.java => ChatMsg.java} | 2 +- .../ChatMessageCustomRepository.java | 11 ++ .../ChatMessageCustomRepositoryImpl.java | 39 +++++++ .../repository/ChatMessageRepository.java | 5 +- .../service/ChatMessageService.java | 13 +++ .../service/ChatMessageServiceImpl.java | 73 ++++++++++++ .../domain/chat/chatRoom/entity/ChatRoom.java | 4 +- .../repository/ChatRoomRepository.java | 3 + .../chatRoom/service/ChatRoomServiceImpl.java | 6 +- .../commitField/domain/user/entity/User.java | 4 +- .../commitField/global/error/ErrorCode.java | 7 +- 16 files changed, 338 insertions(+), 65 deletions(-) create mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatMessageController.java create mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/controller/request/ChatMsgRequest.java create mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/controller/response/ChatMsgResponse.java create mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/dto/ChatMsgDto.java rename src/main/java/cmf/commitField/domain/chat/chatMessage/entity/{ChatMessage.java => ChatMsg.java} (94%) create mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageCustomRepository.java create mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageCustomRepositoryImpl.java create mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/service/ChatMessageService.java create mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/service/ChatMessageServiceImpl.java diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatController.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatController.java index ef511f2..9c10788 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatController.java +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatController.java @@ -1,55 +1,55 @@ -package cmf.commitField.domain.chat.chatMessage.controller; - -import cmf.commitField.domain.chat.chatMessage.entity.ChatMessage; -import cmf.commitField.domain.chat.chatMessage.repository.ChatMessageRepository; -import cmf.commitField.domain.chat.chatRoom.entity.ChatRoom; -import cmf.commitField.domain.chat.chatRoom.repository.ChatRoomRepository; -import cmf.commitField.domain.user.entity.CustomOAuth2User; -import cmf.commitField.domain.user.entity.User; -import cmf.commitField.domain.user.repository.UserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Controller; - -import java.util.Optional; - -@Controller -@RequiredArgsConstructor -public class ChatController { - - private final ChatMessageRepository chatMessageRepository; - private final ChatRoomRepository chatRoomRepository; - private final UserRepository userRepository; - private final SimpMessagingTemplate messagingTemplate; - - @MessageMapping("/chat.sendMessage") // 클라이언트에서 "/app/chat.sendMessage"로 보낼 때 처리됨 - public void sendMessage(ChatMessage chatMessage) { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication != null && authentication.isAuthenticated() && - authentication.getPrincipal() instanceof CustomOAuth2User) { - - CustomOAuth2User oauth2User = (CustomOAuth2User) authentication.getPrincipal(); - Optional userOptional = userRepository.findByUsername(oauth2User.getName()); - - if (userOptional.isPresent()) { - User user = userOptional.get(); - Optional chatRoomOptional = chatRoomRepository.findById(chatMessage.getChatRoom().getId()); - - if (chatRoomOptional.isPresent()) { - ChatRoom chatRoom = chatRoomOptional.get(); - chatMessage.setUser(user); - chatMessage.setChatRoom(chatRoom); - - ChatMessage savedMessage = chatMessageRepository.save(chatMessage); - - // 채팅방 구독자들에게 메시지 전송 - messagingTemplate.convertAndSend("/sub/chat/room/" + chatRoom.getId(), savedMessage); - } - } - } - } -} +//package cmf.commitField.domain.chat.chatMessage.controller; +// +//import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg; +//import cmf.commitField.domain.chat.chatMessage.repository.ChatMessageCustomRepository; +//import cmf.commitField.domain.chat.chatRoom.entity.ChatRoom; +//import cmf.commitField.domain.chat.chatRoom.repository.ChatRoomRepository; +//import cmf.commitField.domain.user.entity.CustomOAuth2User; +//import cmf.commitField.domain.user.entity.User; +//import cmf.commitField.domain.user.repository.UserRepository; +//import lombok.RequiredArgsConstructor; +//import org.springframework.messaging.handler.annotation.MessageMapping; +//import org.springframework.messaging.simp.SimpMessagingTemplate; +//import org.springframework.security.core.Authentication; +//import org.springframework.security.core.context.SecurityContextHolder; +//import org.springframework.stereotype.Controller; +// +//import java.util.Optional; +// +//@Controller +//@RequiredArgsConstructor +//public class ChatController { +// +// private final ChatMessageCustomRepository chatMessageRepository; +// private final ChatRoomRepository chatRoomRepository; +// private final UserRepository userRepository; +// private final SimpMessagingTemplate messagingTemplate; +// +// @MessageMapping("/chat.sendMessage") // 클라이언트에서 "/app/chat.sendMessage"로 보낼 때 처리됨 +// public void sendMessage(ChatMsg chatMsg) { +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// +// if (authentication != null && authentication.isAuthenticated() && +// authentication.getPrincipal() instanceof CustomOAuth2User) { +// +// CustomOAuth2User oauth2User = (CustomOAuth2User) authentication.getPrincipal(); +// Optional userOptional = userRepository.findByUsername(oauth2User.getName()); +// +// if (userOptional.isPresent()) { +// User user = userOptional.get(); +// Optional chatRoomOptional = chatRoomRepository.findById(chatMsg.getChatRoom().getId()); +// +// if (chatRoomOptional.isPresent()) { +// ChatRoom chatRoom = chatRoomOptional.get(); +// chatMsg.setUser(user); +// chatMsg.setChatRoom(chatRoom); +// +// ChatMsg savedMessage = chatMessageRepository.save(chatMsg); +// +// // 채팅방 구독자들에게 메시지 전송 +// messagingTemplate.convertAndSend("/sub/chat/room/" + chatRoom.getId(), savedMessage); +// } +// } +// } +// } +//} diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatMessageController.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatMessageController.java new file mode 100644 index 0000000..4dd64b7 --- /dev/null +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatMessageController.java @@ -0,0 +1,71 @@ +package cmf.commitField.domain.chat.chatMessage.controller; + +import cmf.commitField.domain.chat.chatMessage.controller.request.ChatMsgRequest; +import cmf.commitField.domain.chat.chatMessage.controller.response.ChatMsgResponse; +import cmf.commitField.domain.chat.chatMessage.dto.ChatMsgDto; +import cmf.commitField.domain.chat.chatMessage.service.ChatMessageService; +import cmf.commitField.domain.user.entity.CustomOAuth2User; +import cmf.commitField.global.error.ErrorCode; +import cmf.commitField.global.globalDto.GlobalResponse; +import cmf.commitField.global.security.LoginCheck; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/chat") +public class ChatMessageController { + + private final ChatMessageService chatMessageService; + + @PostMapping("/msg/{roomId}") + @LoginCheck + public GlobalResponse sendChat( + @PathVariable Long roomId, + @RequestBody @Valid ChatMsgRequest message) { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (!(authentication instanceof OAuth2AuthenticationToken)) { + return GlobalResponse.error(ErrorCode.NOT_AUTHENTICATED); + } + + CustomOAuth2User principal = (CustomOAuth2User) authentication.getPrincipal(); + Long userId = principal.getId(); // OAuth2 로그인한 유저의 ID 가져오기 + + if (message == null || message.getMessage().trim().isEmpty()) { + return GlobalResponse.error(ErrorCode.EMPTY_MESSAGE); + } + + ChatMsgResponse response = chatMessageService.sendMessage(message, userId, roomId); + return GlobalResponse.success("채팅 메시지 보내기 성공", response); + } + + @GetMapping("/msg/{roomId}") + @LoginCheck + public GlobalResponse getChatList( + @PathVariable Long roomId, + @RequestParam(required = false) Long lastId) { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (!(authentication instanceof OAuth2AuthenticationToken)) { + return GlobalResponse.error(ErrorCode.NOT_AUTHENTICATED); + } + + CustomOAuth2User principal = (CustomOAuth2User) authentication.getPrincipal(); + Long userId = principal.getId(); // OAuth2 로그인한 유저의 ID 가져오기 + + List roomChatMsgList = chatMessageService.getRoomChatMsgList(roomId, userId, lastId); + if (roomChatMsgList == null || roomChatMsgList.isEmpty()) { + return GlobalResponse.error(ErrorCode.CHAT_NOT_FOUND); + } + + return GlobalResponse.success("해당 채팅방의 메시지들을 조회하였습니다.", roomChatMsgList); + } +} + diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/request/ChatMsgRequest.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/request/ChatMsgRequest.java new file mode 100644 index 0000000..9ac1995 --- /dev/null +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/request/ChatMsgRequest.java @@ -0,0 +1,17 @@ +package cmf.commitField.domain.chat.chatMessage.controller.request; + +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class ChatMsgRequest { + @NotEmpty + @Length(max = 300) + private String message; + +} \ No newline at end of file diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/response/ChatMsgResponse.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/response/ChatMsgResponse.java new file mode 100644 index 0000000..1c93f4f --- /dev/null +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/response/ChatMsgResponse.java @@ -0,0 +1,18 @@ +package cmf.commitField.domain.chat.chatMessage.controller.response; + +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ChatMsgResponse { + private Long roomId; + //사용자(user) + private String from; + private String message; + private LocalDateTime sendAt; +} diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/dto/ChatMsgDto.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/dto/ChatMsgDto.java new file mode 100644 index 0000000..0720d97 --- /dev/null +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/dto/ChatMsgDto.java @@ -0,0 +1,20 @@ +package cmf.commitField.domain.chat.chatMessage.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class ChatMsgDto { + private Long chatMsgId; + private Long userId; + private String nickname; + private String message; + private LocalDateTime sendAt; +} diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/entity/ChatMessage.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/entity/ChatMsg.java similarity index 94% rename from src/main/java/cmf/commitField/domain/chat/chatMessage/entity/ChatMessage.java rename to src/main/java/cmf/commitField/domain/chat/chatMessage/entity/ChatMsg.java index 8d6f21d..8e5c1c8 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatMessage/entity/ChatMessage.java +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/entity/ChatMsg.java @@ -16,7 +16,7 @@ @Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PROTECTED) -public class ChatMessage extends BaseEntity { +public class ChatMsg extends BaseEntity { private String message; diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageCustomRepository.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageCustomRepository.java new file mode 100644 index 0000000..b26be6e --- /dev/null +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageCustomRepository.java @@ -0,0 +1,11 @@ +package cmf.commitField.domain.chat.chatMessage.repository; + +import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ChatMessageCustomRepository { + List findChatRoomIdByChatMsg(Long chatMsg, Long lastId); +} diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageCustomRepositoryImpl.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageCustomRepositoryImpl.java new file mode 100644 index 0000000..65b3dda --- /dev/null +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageCustomRepositoryImpl.java @@ -0,0 +1,39 @@ +package cmf.commitField.domain.chat.chatMessage.repository; + +import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class ChatMessageCustomRepositoryImpl implements ChatMessageCustomRepository { + + private final EntityManager entityManager; + + @Override + public List findChatRoomIdByChatMsg(Long chatMsg, Long lastId) { + String first = "select c from ChatMsg c where c.chatRoom.id =: chatMsg order by c.id asc"; + String paging = "select c from ChatMsg c where c.chatRoom.id =: chatMsg and c.id > :lastId order by c.id asc"; + + TypedQuery query = null; + + if (lastId == null) { + query = entityManager + .createQuery(first, ChatMsg.class) + .setParameter("chatMsg", chatMsg); + } else { + query = entityManager + .createQuery(paging, ChatMsg.class) + .setParameter("chatMsg", chatMsg) + .setParameter("lastId", lastId); + } + return query + .setMaxResults(10) + .getResultList(); + } + +} diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageRepository.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageRepository.java index 98ea869..09397e3 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageRepository.java +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/repository/ChatMessageRepository.java @@ -1,9 +1,10 @@ package cmf.commitField.domain.chat.chatMessage.repository; -import cmf.commitField.domain.chat.chatMessage.entity.ChatMessage; +import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface ChatMessageRepository extends JpaRepository { +public interface ChatMessageRepository extends JpaRepository { + void deleteChatMsgByChatRoom_Id(Long chatRoomId); } diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/service/ChatMessageService.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/service/ChatMessageService.java new file mode 100644 index 0000000..8429b8d --- /dev/null +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/service/ChatMessageService.java @@ -0,0 +1,13 @@ +package cmf.commitField.domain.chat.chatMessage.service; + +import cmf.commitField.domain.chat.chatMessage.controller.request.ChatMsgRequest; +import cmf.commitField.domain.chat.chatMessage.controller.response.ChatMsgResponse; +import cmf.commitField.domain.chat.chatMessage.dto.ChatMsgDto; + +import java.util.List; + +public interface ChatMessageService { + ChatMsgResponse sendMessage(ChatMsgRequest message, Long userId, Long roomId); + + List getRoomChatMsgList(Long roomId, Long userId, Long lastId); +} diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/service/ChatMessageServiceImpl.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/service/ChatMessageServiceImpl.java new file mode 100644 index 0000000..a75aa1a --- /dev/null +++ b/src/main/java/cmf/commitField/domain/chat/chatMessage/service/ChatMessageServiceImpl.java @@ -0,0 +1,73 @@ +package cmf.commitField.domain.chat.chatMessage.service; + +import cmf.commitField.domain.chat.chatMessage.controller.request.ChatMsgRequest; +import cmf.commitField.domain.chat.chatMessage.controller.response.ChatMsgResponse; +import cmf.commitField.domain.chat.chatMessage.dto.ChatMsgDto; +import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg; +import cmf.commitField.domain.chat.chatMessage.repository.ChatMessageCustomRepository; +import cmf.commitField.domain.chat.chatMessage.repository.ChatMessageRepository; +import cmf.commitField.domain.chat.chatRoom.entity.ChatRoom; +import cmf.commitField.domain.chat.chatRoom.repository.ChatRoomRepository; +import cmf.commitField.domain.user.entity.User; +import cmf.commitField.domain.user.repository.UserRepository; +import cmf.commitField.global.error.ErrorCode; +import cmf.commitField.global.exception.CustomException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class ChatMessageServiceImpl implements ChatMessageService { + + private final ChatMessageRepository chatMessageRepository; + private final UserRepository userRepository; + private final ChatRoomRepository chatRoomRepository; + private final ChatMessageCustomRepository chatMessageCustomRepository; + + @Override + public ChatMsgResponse sendMessage(ChatMsgRequest message, Long userId, Long roomId) { + User findUser = userRepository.findById(userId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER)); + + ChatRoom chatRoom = chatRoomRepository.findChatRoomById(roomId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROOM)); + // 채팅 메시지 생성 + ChatMsg chatMsg = ChatMsg.builder() + .message(message.getMessage()) + .createdAt(LocalDateTime.now()) + .user(findUser) + .chatRoom(chatRoom) + .build(); + // Response + ChatMsgResponse response = ChatMsgResponse.builder() + .roomId(roomId) + .from(findUser.getNickname()) + .message(message.getMessage()) + .sendAt(chatMsg.getCreatedAt()) + .build(); + chatMessageRepository.save(chatMsg); + return response; + } + + @Transactional(readOnly = true) + @Override + public List getRoomChatMsgList(Long roomId, Long userId, Long lastId) { + userRepository.findById(userId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER)); + + List chatMsgsList = chatMessageCustomRepository.findChatRoomIdByChatMsg(roomId, lastId); + return chatMsgsList.stream().map(chatMsg -> new ChatMsgDto( + chatMsg.getId(), + chatMsg.getUser().getId(), + chatMsg.getUser().getNickname(), + chatMsg.getMessage(), + chatMsg.getCreatedAt() + )) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/entity/ChatRoom.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/entity/ChatRoom.java index de7ebfb..a420b41 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/entity/ChatRoom.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/entity/ChatRoom.java @@ -1,6 +1,6 @@ package cmf.commitField.domain.chat.chatRoom.entity; -import cmf.commitField.domain.chat.chatMessage.entity.ChatMessage; +import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg; import cmf.commitField.domain.chat.userChatRoom.entity.UserChatRoom; import cmf.commitField.domain.user.entity.User; import cmf.commitField.global.jpa.BaseEntity; @@ -34,7 +34,7 @@ public class ChatRoom extends BaseEntity { private List userChatRooms; @OneToMany(mappedBy = "chatRoom", fetch = FetchType.LAZY) - private List chatMessages; + private List chatMsgs; @Override public String toString() { diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java index 9185d73..be54772 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java @@ -21,6 +21,8 @@ public interface ChatRoomRepository extends JpaRepository { @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="1000")}) Optional findById(Long aLong); + Optional findChatRoomById(Long id); + @Query("select c from ChatRoom c where c.roomCreator=:userId") Page findAllByUserId(@Param("userId")Long userId,Pageable pageable); @@ -28,4 +30,5 @@ public interface ChatRoomRepository extends JpaRepository { @Query(value = "SELECT ROOM_CREATOR FROM chat_room WHERE ID = ?", nativeQuery = true) Long findChatRoomByRoomCreator(Long roomId); + } \ No newline at end of file diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java index e108c08..ee84f88 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java @@ -146,9 +146,10 @@ public void outRoom(Long userId, Long roomId) { return; } // 방장이라면 방 삭제 + chatMessageRepository.deleteChatMsgByChatRoom_Id(roomId); //방 삭제 시 채팅도 다 삭제(필요 시) userChatRoomRepository.deleteUserChatRoomByChatRoom_Id(roomId); chatRoomRepository.deleteById(roomId); - // chatMsgRepository.deleteById(roomId); 방 삭제 시 채팅도 다 삭제(필요 시) + } @@ -162,9 +163,10 @@ public void deleteRoom(Long userId, Long roomId) { throw new CustomException(ErrorCode.NOT_ROOM_CREATOR); } //모든 사용자 제거 후 방 삭제 + chatMessageRepository.deleteChatMsgByChatRoom_Id(roomId); userChatRoomRepository.deleteUserChatRoomByChatRoom_Id(roomId); chatRoomRepository.deleteById(roomId); -// chatMessageRepository.deleteId(roomId); + } diff --git a/src/main/java/cmf/commitField/domain/user/entity/User.java b/src/main/java/cmf/commitField/domain/user/entity/User.java index 929e1bb..f0e5749 100644 --- a/src/main/java/cmf/commitField/domain/user/entity/User.java +++ b/src/main/java/cmf/commitField/domain/user/entity/User.java @@ -1,6 +1,6 @@ package cmf.commitField.domain.user.entity; -import cmf.commitField.domain.chat.chatMessage.entity.ChatMessage; +import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg; import cmf.commitField.domain.chat.chatRoom.entity.ChatRoom; import cmf.commitField.domain.chat.userChatRoom.entity.UserChatRoom; import cmf.commitField.global.jpa.BaseEntity; @@ -40,6 +40,6 @@ public enum Role { private List userChatRooms = new ArrayList<>(); @OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) - private List chatMessages = new ArrayList<>(); + private List chatMsgs = new ArrayList<>(); } diff --git a/src/main/java/cmf/commitField/global/error/ErrorCode.java b/src/main/java/cmf/commitField/global/error/ErrorCode.java index 23d8bea..8bb876f 100644 --- a/src/main/java/cmf/commitField/global/error/ErrorCode.java +++ b/src/main/java/cmf/commitField/global/error/ErrorCode.java @@ -21,6 +21,7 @@ public enum ErrorCode { // User NOT_FOUND_USER(HttpStatus.BAD_REQUEST, "해당 유저가 존재하지 않습니다"), PASSWORD_MISMATCH(HttpStatus.BAD_REQUEST, "비밀번호가 일치하지 않습니다."), + NOT_AUTHENTICATED(HttpStatus.UNAUTHORIZED, "사용자가 인증되지 않았습니다."), // Auth LOGIN_REQUIRED(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다."), @@ -45,7 +46,11 @@ public enum ErrorCode { NOT_ROOM_CREATOR(HttpStatus.FORBIDDEN, "방 생성자가 아닙니다."), USER_CREATED_ROOM_NOT_FOUND(HttpStatus.NOT_FOUND, "사용자가 생성한 방이 없습니다."), ALREADY_JOIN_ROOM(HttpStatus.BAD_REQUEST, "사용자는 이미 해당 방에 참여하고 있습니다."), - NOT_EXIST_CLIENT(HttpStatus.NOT_FOUND, "채팅방에 사용자가 존재하지 않습니다."); + NOT_EXIST_CLIENT(HttpStatus.NOT_FOUND, "채팅방에 사용자가 존재하지 않습니다."), + + //chatMessage + EMPTY_MESSAGE(HttpStatus.BAD_REQUEST, "채팅 메시지는 공백으로 보낼 수 없습니다."), + CHAT_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 채팅방의 메시지들을 찾지 못했습니다."); private final HttpStatus httpStatus; private final String message; From 18338fd4245d9f7ec5375d7e456443d44bd28127 Mon Sep 17 00:00:00 2001 From: Sukyeong Sung Date: Wed, 26 Feb 2025 11:15:16 +0900 Subject: [PATCH 2/5] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chatRoom/controller/ChatRoomController.java | 2 +- .../chatRoom/repository/ChatRoomRepository.java | 4 ++-- .../chat/chatRoom/service/ChatRoomService.java | 2 +- .../chatRoom/service/ChatRoomServiceImpl.java | 16 +++++++++------- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/controller/ChatRoomController.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/controller/ChatRoomController.java index 30a10da..7768de9 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/controller/ChatRoomController.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/controller/ChatRoomController.java @@ -78,7 +78,7 @@ public GlobalResponse getByUserRoomList(Pageable pageable) { CustomOAuth2User principal = (CustomOAuth2User) authentication.getPrincipal(); Long userId = principal.getId(); // getId()를 통해 userId를 추출 - List userByRoomList = chatRoomService.getUserByRoomList(userId, pageable); + List userByRoomList = chatRoomService.roomsByCreatorUser(userId, pageable); // 방 리스트가 비어 있으면 notFound 응답 반환 if (userByRoomList.isEmpty()) { diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java index be54772..febddf4 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java @@ -28,7 +28,7 @@ public interface ChatRoomRepository extends JpaRepository { Page findAllByUserChatRoomsUserId(Long userId,Pageable pageable); - @Query(value = "SELECT ROOM_CREATOR FROM chat_room WHERE ID = ?", nativeQuery = true) - Long findChatRoomByRoomCreator(Long roomId); + @Query(value = "SELECT ROOM_CREATOR FROM chat_room WHERE CHAT_ROOM_ID = ?", nativeQuery = true) + Optional findChatRoomIdByRoomId(@Param("roomId") Long roomId); } \ No newline at end of file diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomService.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomService.java index 7d92dfc..2b4d8f1 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomService.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomService.java @@ -16,7 +16,7 @@ public interface ChatRoomService { List getRoomList(Pageable pageable); // 자신이 생성한 방 리스트 조회 - List getUserByRoomList(Long userId, Pageable pageable); + List roomsByCreatorUser(Long userId, Pageable pageable); List getUserByRoomPartList(Long userId, Pageable pageable); diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java index ee84f88..2fe8d39 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java @@ -121,7 +121,7 @@ public List getRoomList(Pageable pageable) { // 자신이 생성한 방 리스트 조회 @Override @Transactional(readOnly = true) - public List getUserByRoomList(Long userId, Pageable pageable) { + public List roomsByCreatorUser(Long userId, Pageable pageable) { Page all = chatRoomRepository.findAllByUserId(userId, pageable); return getChatRoomDtos(all); } @@ -138,10 +138,11 @@ public List getUserByRoomPartList(Long userId, Pageable pageable) { @Override @Transactional public void outRoom(Long userId, Long roomId) { - Long roomCreatorId = chatRoomRepository - .findChatRoomByRoomCreator(roomId); + ChatRoom room = chatRoomRepository + .findChatRoomById(roomId) + .orElseThrow(() -> new CustomException(ErrorCode.NO_ROOM_FOUND)); // 방장이 아니라면 - if (!Objects.equals(roomCreatorId, userId)) { + if (!Objects.equals(room.getRoomCreator(), userId)) { userChatRoomRepository.deleteUserChatRoomByChatRoom_IdAndUserId(roomId, userId); return; } @@ -156,10 +157,11 @@ public void outRoom(Long userId, Long roomId) { @Override @Transactional public void deleteRoom(Long userId, Long roomId) { - Long roomCreatorId = chatRoomRepository - .findChatRoomByRoomCreator(roomId); + ChatRoom room = chatRoomRepository + .findChatRoomById(roomId) + .orElseThrow(() -> new CustomException(ErrorCode.NONE_ROOM)); //방장이 아닐 경우, 삭제 불가 - if (!Objects.equals(roomCreatorId, userId)) { + if (!Objects.equals(room.getRoomCreator(), userId)) { throw new CustomException(ErrorCode.NOT_ROOM_CREATOR); } //모든 사용자 제거 후 방 삭제 From 161e407bba03ec8602aac8f99f7101205bec09d6 Mon Sep 17 00:00:00 2001 From: Sukyeong Sung Date: Wed, 26 Feb 2025 11:22:20 +0900 Subject: [PATCH 3/5] =?UTF-8?q?Feat:=20NPE=20=EB=8C=80=EB=B9=84=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/chat/chatRoom/service/ChatRoomServiceImpl.java | 2 +- src/main/java/cmf/commitField/global/error/ErrorCode.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java index 2fe8d39..28ccff7 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java @@ -159,7 +159,7 @@ public void outRoom(Long userId, Long roomId) { public void deleteRoom(Long userId, Long roomId) { ChatRoom room = chatRoomRepository .findChatRoomById(roomId) - .orElseThrow(() -> new CustomException(ErrorCode.NONE_ROOM)); + .orElseThrow(() -> new CustomException(ErrorCode.NO_ROOM)); //방장이 아닐 경우, 삭제 불가 if (!Objects.equals(room.getRoomCreator(), userId)) { throw new CustomException(ErrorCode.NOT_ROOM_CREATOR); diff --git a/src/main/java/cmf/commitField/global/error/ErrorCode.java b/src/main/java/cmf/commitField/global/error/ErrorCode.java index 8bb876f..fa9c437 100644 --- a/src/main/java/cmf/commitField/global/error/ErrorCode.java +++ b/src/main/java/cmf/commitField/global/error/ErrorCode.java @@ -40,6 +40,7 @@ public enum ErrorCode { ROOM_USER_FULL(HttpStatus.BAD_REQUEST, "방에 사용자가 다 차 있습니다."), CHAT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "채팅 전송에 오류가 있습니다."), NO_ROOM_FOUND(HttpStatus.NOT_FOUND, "채팅방이 없습니다."), + NO_ROOM(HttpStatus.NOT_FOUND, "존재하지 않는 채팅방입니다."), //user_chatroom NONE_ROOM(HttpStatus.NOT_FOUND, "사용자가 들어가 있는 방이 없습니다."), From 8f19ae26fcc7670d45f452252dc663a42d3f5703 Mon Sep 17 00:00:00 2001 From: Sukyeong Sung Date: Wed, 26 Feb 2025 11:48:22 +0900 Subject: [PATCH 4/5] Feat: redis session storage --- .../chatRoom/service/ChatRoomServiceImpl.java | 4 +- .../global/config/RedisConfig.java | 38 +++++++++++++++++++ .../websocket/ChatWebSocketHandler.java | 13 +++++-- 3 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 src/main/java/cmf/commitField/global/config/RedisConfig.java diff --git a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java index 28ccff7..f772367 100644 --- a/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java +++ b/src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java @@ -122,8 +122,8 @@ public List getRoomList(Pageable pageable) { @Override @Transactional(readOnly = true) public List roomsByCreatorUser(Long userId, Pageable pageable) { - Page all = chatRoomRepository.findAllByUserId(userId, pageable); - return getChatRoomDtos(all); + Page userCreateAll = chatRoomRepository.findAllByUserId(userId, pageable); + return getChatRoomDtos(userCreateAll); } // 자신이 참여한 방 리스트 조회 diff --git a/src/main/java/cmf/commitField/global/config/RedisConfig.java b/src/main/java/cmf/commitField/global/config/RedisConfig.java new file mode 100644 index 0000000..f7128f5 --- /dev/null +++ b/src/main/java/cmf/commitField/global/config/RedisConfig.java @@ -0,0 +1,38 @@ +package cmf.commitField.global.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + @Value("${spring.redis.host}") + public String host; + + @Value("${spring.redis.port}") + public int port; + + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); // Redis 키를 문자열로 직렬화 + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // 값의 직렬화를 위해 -> Redis (JSON) + redisTemplate.setConnectionFactory(connectionFactory); // 연결 다 된 Redis -> Factory와 연결 + return redisTemplate; + } + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(); + configuration.setHostName(host); + configuration.setPort(port); + return new LettuceConnectionFactory(configuration); + } +} diff --git a/src/main/java/cmf/commitField/global/websocket/ChatWebSocketHandler.java b/src/main/java/cmf/commitField/global/websocket/ChatWebSocketHandler.java index 1a39ace..2565f47 100644 --- a/src/main/java/cmf/commitField/global/websocket/ChatWebSocketHandler.java +++ b/src/main/java/cmf/commitField/global/websocket/ChatWebSocketHandler.java @@ -16,7 +16,7 @@ @Slf4j public class ChatWebSocketHandler implements WebSocketHandler { - private Map> chatRooms = new HashMap<>(); + private final Map> chatRooms = new HashMap<>(); // 방의 키값 @@ -92,8 +92,15 @@ private Long extractRoomId(WebSocketSession session) { String[] uriParts = uri.split("/"); // EX_URL) /chat/room/{roomId} 일 때 roomId 추출 // 늘어난다면 수 변경해주면.. (일단 임시로 설정) - if (uriParts.length >= 3 && uriParts[2].equals("room")) { - roomId = Long.valueOf(uriParts[3]); +// if (uriParts.length >= 3 && uriParts[2].equals("room")) { +// roomId = Long.valueOf(uriParts[3]); + if (uriParts.length >= 4 && uriParts[2].equals("msg")) { + return Long.valueOf(uriParts[3]); + } + // /chat/room/join/{roomId}, /chat/room/out/{roomId}, /chat/room/delete/{roomId} 일 때 roomId 추출 + if (uriParts.length >= 5 && uriParts[2].equals("room") && + (uriParts[3].equals("join") || uriParts[3].equals("out") || uriParts[3].equals("delete"))) { + roomId = Long.valueOf(uriParts[4]); } return roomId; } From cf4a2f846b00257e36c89e6b921432c50aef5790 Mon Sep 17 00:00:00 2001 From: Sukyeong Sung Date: Wed, 26 Feb 2025 11:58:14 +0900 Subject: [PATCH 5/5] =?UTF-8?q?Fix:=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ChatController.java | 55 ------------------- 1 file changed, 55 deletions(-) delete mode 100644 src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatController.java diff --git a/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatController.java b/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatController.java deleted file mode 100644 index 9c10788..0000000 --- a/src/main/java/cmf/commitField/domain/chat/chatMessage/controller/ChatController.java +++ /dev/null @@ -1,55 +0,0 @@ -//package cmf.commitField.domain.chat.chatMessage.controller; -// -//import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg; -//import cmf.commitField.domain.chat.chatMessage.repository.ChatMessageCustomRepository; -//import cmf.commitField.domain.chat.chatRoom.entity.ChatRoom; -//import cmf.commitField.domain.chat.chatRoom.repository.ChatRoomRepository; -//import cmf.commitField.domain.user.entity.CustomOAuth2User; -//import cmf.commitField.domain.user.entity.User; -//import cmf.commitField.domain.user.repository.UserRepository; -//import lombok.RequiredArgsConstructor; -//import org.springframework.messaging.handler.annotation.MessageMapping; -//import org.springframework.messaging.simp.SimpMessagingTemplate; -//import org.springframework.security.core.Authentication; -//import org.springframework.security.core.context.SecurityContextHolder; -//import org.springframework.stereotype.Controller; -// -//import java.util.Optional; -// -//@Controller -//@RequiredArgsConstructor -//public class ChatController { -// -// private final ChatMessageCustomRepository chatMessageRepository; -// private final ChatRoomRepository chatRoomRepository; -// private final UserRepository userRepository; -// private final SimpMessagingTemplate messagingTemplate; -// -// @MessageMapping("/chat.sendMessage") // 클라이언트에서 "/app/chat.sendMessage"로 보낼 때 처리됨 -// public void sendMessage(ChatMsg chatMsg) { -// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); -// -// if (authentication != null && authentication.isAuthenticated() && -// authentication.getPrincipal() instanceof CustomOAuth2User) { -// -// CustomOAuth2User oauth2User = (CustomOAuth2User) authentication.getPrincipal(); -// Optional userOptional = userRepository.findByUsername(oauth2User.getName()); -// -// if (userOptional.isPresent()) { -// User user = userOptional.get(); -// Optional chatRoomOptional = chatRoomRepository.findById(chatMsg.getChatRoom().getId()); -// -// if (chatRoomOptional.isPresent()) { -// ChatRoom chatRoom = chatRoomOptional.get(); -// chatMsg.setUser(user); -// chatMsg.setChatRoom(chatRoom); -// -// ChatMsg savedMessage = chatMessageRepository.save(chatMsg); -// -// // 채팅방 구독자들에게 메시지 전송 -// messagingTemplate.convertAndSend("/sub/chat/room/" + chatRoom.getId(), savedMessage); -// } -// } -// } -// } -//}