From 9294ab4236eec24020ca765a90b811a1072fb5a6 Mon Sep 17 00:00:00 2001 From: Dongha Date: Mon, 5 Feb 2024 03:08:31 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Choir:=20=EC=9B=B9=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 62e6355..30b47ae 100644 --- a/build.gradle +++ b/build.gradle @@ -51,6 +51,9 @@ dependencies { runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' + //WebSocket + implementation 'org.springframework.boot:spring-boot-starter-websocket' + testImplementation 'org.springframework.boot:spring-boot-starter-test' } From 230960bb52c0b9705a0ae99f01c3bb9ec782fd10 Mon Sep 17 00:00:00 2001 From: Dongha Date: Mon, 5 Feb 2024 03:11:26 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feature:=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/category/data/MajorCategory.java | 3 +- .../controller/ChatMessageController.java | 17 ---------- .../controller/SocketController.java | 29 ++++++++++++++++ .../dto/ChatMessageRequestDTO.java | 4 +++ .../dto/ChatMessageResponseDTO.java | 10 +++--- .../service/ChatMessageService.java | 21 ++++++++---- .../global/config/WebSecurityConfig.java | 1 + .../hhive/global/config/WebSocketConfig.java | 33 +++++++++++++++++++ 8 files changed, 87 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/HHive/hhive/domain/chatmessage/controller/SocketController.java create mode 100644 src/main/java/com/HHive/hhive/global/config/WebSocketConfig.java diff --git a/src/main/java/com/HHive/hhive/domain/category/data/MajorCategory.java b/src/main/java/com/HHive/hhive/domain/category/data/MajorCategory.java index b75d301..7a3543a 100644 --- a/src/main/java/com/HHive/hhive/domain/category/data/MajorCategory.java +++ b/src/main/java/com/HHive/hhive/domain/category/data/MajorCategory.java @@ -11,7 +11,8 @@ public enum MajorCategory { DANCE("댄스/무용"), SOCIAL("사교/인맥"), MEDIA("사진/영상"), - PET("반려동물"); + PET("반려동물"), + ETC("기타"); private final String title; diff --git a/src/main/java/com/HHive/hhive/domain/chatmessage/controller/ChatMessageController.java b/src/main/java/com/HHive/hhive/domain/chatmessage/controller/ChatMessageController.java index 9336218..e54e85f 100644 --- a/src/main/java/com/HHive/hhive/domain/chatmessage/controller/ChatMessageController.java +++ b/src/main/java/com/HHive/hhive/domain/chatmessage/controller/ChatMessageController.java @@ -1,21 +1,16 @@ package com.HHive.hhive.domain.chatmessage.controller; -import com.HHive.hhive.domain.chatmessage.dto.ChatMessageRequestDTO; import com.HHive.hhive.domain.chatmessage.dto.ChatMessageResponseDTO; import com.HHive.hhive.domain.chatmessage.service.ChatMessageService; import com.HHive.hhive.domain.user.UserDetailsImpl; import com.HHive.hhive.global.common.CommonResponse; -import jakarta.validation.Valid; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -26,18 +21,6 @@ public class ChatMessageController { private final ChatMessageService chatMessageService; - @PostMapping("/hives/{hiveId}") - public ResponseEntity> sendChatMessage( - @PathVariable Long hiveId, - @RequestBody @Valid ChatMessageRequestDTO requestDTO, - @AuthenticationPrincipal UserDetailsImpl userDetails) { - - chatMessageService.sendChatMessages(hiveId, requestDTO, userDetails.getUser()); - - return ResponseEntity.status(HttpStatus.CREATED).body( - CommonResponse.of("메시지 전송 성공", null)); - } - @GetMapping("/hives/{hiveId}") public ResponseEntity>> getChatMessages( @PathVariable Long hiveId, diff --git a/src/main/java/com/HHive/hhive/domain/chatmessage/controller/SocketController.java b/src/main/java/com/HHive/hhive/domain/chatmessage/controller/SocketController.java new file mode 100644 index 0000000..a578799 --- /dev/null +++ b/src/main/java/com/HHive/hhive/domain/chatmessage/controller/SocketController.java @@ -0,0 +1,29 @@ +package com.HHive.hhive.domain.chatmessage.controller; + +import com.HHive.hhive.domain.chatmessage.dto.ChatMessageRequestDTO; +import com.HHive.hhive.domain.chatmessage.dto.ChatMessageResponseDTO; +import com.HHive.hhive.domain.chatmessage.entity.ChatMessage; +import com.HHive.hhive.domain.chatmessage.service.ChatMessageService; +import lombok.RequiredArgsConstructor; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.simp.SimpMessageSendingOperations; +import org.springframework.stereotype.Controller; + +@Controller +@RequiredArgsConstructor +public class SocketController { + + private final SimpMessageSendingOperations sendingOperations; + + private final ChatMessageService chatMessageService; + + @MessageMapping("/chat") + public void socketHandler(ChatMessageRequestDTO requestDTO) { + + ChatMessage chatMessage = chatMessageService.receiveAndSaveMessage(requestDTO); + + sendingOperations.convertAndSend("/topic/chat/" + requestDTO.getHiveId(), + ChatMessageResponseDTO.from(chatMessage)); + } + +} diff --git a/src/main/java/com/HHive/hhive/domain/chatmessage/dto/ChatMessageRequestDTO.java b/src/main/java/com/HHive/hhive/domain/chatmessage/dto/ChatMessageRequestDTO.java index 48b6d9f..91beacf 100644 --- a/src/main/java/com/HHive/hhive/domain/chatmessage/dto/ChatMessageRequestDTO.java +++ b/src/main/java/com/HHive/hhive/domain/chatmessage/dto/ChatMessageRequestDTO.java @@ -6,6 +6,10 @@ @Getter public class ChatMessageRequestDTO { + private Long hiveId; + + private String username; + @Size(min = 1, max = 500) private String message; } diff --git a/src/main/java/com/HHive/hhive/domain/chatmessage/dto/ChatMessageResponseDTO.java b/src/main/java/com/HHive/hhive/domain/chatmessage/dto/ChatMessageResponseDTO.java index 95a1b49..a91e690 100644 --- a/src/main/java/com/HHive/hhive/domain/chatmessage/dto/ChatMessageResponseDTO.java +++ b/src/main/java/com/HHive/hhive/domain/chatmessage/dto/ChatMessageResponseDTO.java @@ -1,7 +1,7 @@ package com.HHive.hhive.domain.chatmessage.dto; import com.HHive.hhive.domain.chatmessage.entity.ChatMessage; -import com.HHive.hhive.domain.user.entity.User; +import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; @@ -12,18 +12,16 @@ public class ChatMessageResponseDTO { private String message; - private Long senderId; - - private String senderName; + private String username; + @JsonFormat(pattern = "MM월 dd일 / HH시 mm분") private LocalDateTime createdAt; public static ChatMessageResponseDTO from(ChatMessage chatMessage) { return ChatMessageResponseDTO.builder() .message(chatMessage.getMessage()) - .senderId(chatMessage.getSenderId()) - .senderName(chatMessage.getSenderName()) + .username(chatMessage.getSenderName()) .createdAt(chatMessage.getCreatedAt()) .build(); } diff --git a/src/main/java/com/HHive/hhive/domain/chatmessage/service/ChatMessageService.java b/src/main/java/com/HHive/hhive/domain/chatmessage/service/ChatMessageService.java index 3baae30..be25e20 100644 --- a/src/main/java/com/HHive/hhive/domain/chatmessage/service/ChatMessageService.java +++ b/src/main/java/com/HHive/hhive/domain/chatmessage/service/ChatMessageService.java @@ -8,8 +8,10 @@ import com.HHive.hhive.domain.hive.service.HiveService; import com.HHive.hhive.domain.relationship.hiveuser.validator.HiveUserValidator; import com.HHive.hhive.domain.user.entity.User; +import com.HHive.hhive.domain.user.repository.UserRepository; import com.HHive.hhive.global.exception.chatmessage.NotFoundChatMessageException; import com.HHive.hhive.global.exception.chatmessage.NotSenderOfChatMessageException; +import com.HHive.hhive.global.exception.user.NotFoundUserException; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -21,19 +23,24 @@ public class ChatMessageService { private final ChatMessageRepository chatMessageRepository; + private final UserRepository userRepository; + private final HiveService hiveService; private final HiveUserValidator hiveUserValidator; - public void sendChatMessages(Long hiveId, ChatMessageRequestDTO requestDTO, User user) { + public ChatMessage receiveAndSaveMessage(ChatMessageRequestDTO requestDTO) { - Hive hive = hiveService.findHiveById(hiveId); + User user = userRepository.findByUsername(requestDTO.getUsername()).orElseThrow( + NotFoundUserException::new); - hiveUserValidator.validateHiveUser(hive, user); + Hive hive = hiveService.findHiveById(requestDTO.getHiveId()); - ChatMessage chatMessage = makeChatMessage(hive, requestDTO.getMessage(), user); + ChatMessage chatMessage = makeChatMessage(hive, user, requestDTO.getMessage()); chatMessageRepository.save(chatMessage); + + return chatMessage; } public List getChatMessages(Long hiveId, User user) { @@ -58,14 +65,14 @@ public void deleteChatMessage(Long chatMessageId, User user) { chatMessage.updateDeletedAt(); } - public void validateLoginUserEqualsMessageSender(ChatMessage chatMessage, User user) { + private void validateLoginUserEqualsMessageSender(ChatMessage chatMessage, User user) { - if(!chatMessage.getSenderId().equals(user.getId())) { + if (!chatMessage.getSenderId().equals(user.getId())) { throw new NotSenderOfChatMessageException(); } } - private ChatMessage makeChatMessage(Hive hive, String message, User sender) { + private ChatMessage makeChatMessage(Hive hive, User sender, String message) { return ChatMessage.builder() .hive(hive) diff --git a/src/main/java/com/HHive/hhive/global/config/WebSecurityConfig.java b/src/main/java/com/HHive/hhive/global/config/WebSecurityConfig.java index 5f9328e..0b4f39c 100644 --- a/src/main/java/com/HHive/hhive/global/config/WebSecurityConfig.java +++ b/src/main/java/com/HHive/hhive/global/config/WebSecurityConfig.java @@ -78,6 +78,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .permitAll() // resource 접근 허용 설정 .requestMatchers("/api/users/**").permitAll() // '/api/users/'로 시작하는 요청 모두 접근 허가 .requestMatchers("/api/notifications/**").permitAll() + .requestMatchers("/ws/**").permitAll() .anyRequest().authenticated() // 그 외 모든 요청 인증처리 ); diff --git a/src/main/java/com/HHive/hhive/global/config/WebSocketConfig.java b/src/main/java/com/HHive/hhive/global/config/WebSocketConfig.java new file mode 100644 index 0000000..ec829a3 --- /dev/null +++ b/src/main/java/com/HHive/hhive/global/config/WebSocketConfig.java @@ -0,0 +1,33 @@ +package com.HHive.hhive.global.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + + //handshake를 위한 설정 + registry.addEndpoint("/ws") + .setAllowedOriginPatterns("*") + //웹소켓 지원하지 않는 브라우저를 도움 + .withSockJS(); + } + + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + + //SimpleBroker로 바로 이동할 경로설정. 스프링에서 제공하는 내장 브로커를 사용하겠다 + registry.enableSimpleBroker("/topic", "/queue"); + + //SimpAnnotationMethod 로 가는 경로. 메시지 처리 후 SimpleBroker로 이동할 + registry.setApplicationDestinationPrefixes("/pub"); + } + +}