diff --git a/src/main/java/com/market/saessag/domain/chat/controller/ChatMessageController.java b/src/main/java/com/market/saessag/domain/chat/controller/ChatMessageController.java index 59f7d2a..fa72fd6 100644 --- a/src/main/java/com/market/saessag/domain/chat/controller/ChatMessageController.java +++ b/src/main/java/com/market/saessag/domain/chat/controller/ChatMessageController.java @@ -2,9 +2,11 @@ import com.market.saessag.domain.chat.dto.ChatMessageResponse; import com.market.saessag.domain.chat.service.ChatMessageService; +import com.market.saessag.domain.user.dto.SignInResponse; import com.market.saessag.global.response.ApiResponse; import com.market.saessag.global.response.SuccessCode; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -39,8 +41,12 @@ public ApiResponse> searchMessages( // 읽음 처리 @PostMapping("/{roomId}/mark-read") - public ApiResponse markMessageAsRead(@PathVariable Long roomId, HttpServletRequest request) { //user 세션으로 바꿀 것 - chatMessageService.markMessagesAsRead(roomId, request); + public ApiResponse markMessageAsRead(@PathVariable Long roomId, HttpServletRequest request) { + // 변경된 세션 처리 통합한 후 수정하겠습니다! + HttpSession session = request.getSession(); + SignInResponse userSession = (SignInResponse) session.getAttribute("userProfile"); + + chatMessageService.markMessagesAsRead(roomId, userSession); return ApiResponse.success(); } diff --git a/src/main/java/com/market/saessag/domain/chat/controller/ChatMessageWebSocketController.java b/src/main/java/com/market/saessag/domain/chat/controller/ChatMessageWebSocketController.java index 62a01c3..9b25850 100644 --- a/src/main/java/com/market/saessag/domain/chat/controller/ChatMessageWebSocketController.java +++ b/src/main/java/com/market/saessag/domain/chat/controller/ChatMessageWebSocketController.java @@ -7,13 +7,12 @@ import com.market.saessag.domain.chat.service.ChatFileService; import com.market.saessag.domain.chat.service.ChatMessageService; import com.market.saessag.domain.chat.service.ChatSubscriptionService; +import com.market.saessag.domain.user.dto.SignInResponse; import com.market.saessag.global.response.ApiResponse; import com.market.saessag.global.response.SuccessCode; import lombok.RequiredArgsConstructor; -import org.springframework.messaging.handler.annotation.DestinationVariable; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.messaging.handler.annotation.*; +import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.stereotype.Controller; import java.util.List; @@ -28,11 +27,14 @@ public class ChatMessageWebSocketController { // 메시지 전송 @MessageMapping("/chat/{roomId}/sendMessage") @SendTo("/topic/chat/{roomId}") - public ApiResponse sendMessage(@DestinationVariable Long roomId, @Payload ChatMessageRequest message) { - ChatMessageResponse savedMessage = chatMessageService.saveMessage(roomId, message); + public ApiResponse sendMessage(@DestinationVariable Long roomId, @Payload ChatMessageRequest message, + SimpMessageHeaderAccessor headerAccessor) { - // 추후에 세션 정보로 읽음 처리 꼭 할 것 - // 현재 임시로 메시지 보낸 후 메시지 읽음 API 사용하여 자신의 메시지 읽음 처리하도록 하는 중 + ChatMessageResponse savedMessage = chatMessageService.saveMessage(roomId, message, headerAccessor); + + //메시지 읽음 처리 + SignInResponse user = (SignInResponse) headerAccessor.getSessionAttributes().get("userProfile"); + chatMessageService.markMessagesAsRead(roomId, user); //오프라인 구독자들에게 메시지 전송 chatSubscriptionService.sendToOffSubscriber(savedMessage, roomId); diff --git a/src/main/java/com/market/saessag/domain/chat/controller/ChatRoomController.java b/src/main/java/com/market/saessag/domain/chat/controller/ChatRoomController.java index 6b3edbe..199ca5e 100644 --- a/src/main/java/com/market/saessag/domain/chat/controller/ChatRoomController.java +++ b/src/main/java/com/market/saessag/domain/chat/controller/ChatRoomController.java @@ -1,5 +1,6 @@ package com.market.saessag.domain.chat.controller; +import com.market.saessag.domain.chat.dto.ChatRoomCreateResponse; import com.market.saessag.domain.chat.dto.ChatRoomRequest; import com.market.saessag.domain.chat.dto.ChatRoomResponse; import com.market.saessag.domain.chat.service.ChatRoomService; @@ -7,6 +8,7 @@ import com.market.saessag.global.response.SuccessCode; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; +import org.springframework.data.util.Pair; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -19,10 +21,10 @@ public class ChatRoomController { // 방 생성 @PostMapping() - public ApiResponse createChatRoom(@RequestBody ChatRoomRequest request) { - ChatRoomResponse chatRoom = chatRoomService.createOrGetChatRoom(request.getProductId(), request.getBuyerId(), request.getSellerId()); + public ApiResponse createChatRoom(@RequestBody ChatRoomRequest request) { + ChatRoomCreateResponse chatRoom = chatRoomService.createOrGetChatRoom(request.getProductId(), request.getBuyerId(), request.getSellerId()); - return ApiResponse.success(SuccessCode.ROOM_CREATED, chatRoom); + return ApiResponse.success(chatRoom); } // 특정 유저가 현재 속해있는 채팅방 리스트 반환 diff --git a/src/main/java/com/market/saessag/domain/chat/dto/ChatMessageRequest.java b/src/main/java/com/market/saessag/domain/chat/dto/ChatMessageRequest.java index cff4f93..fdb4e16 100644 --- a/src/main/java/com/market/saessag/domain/chat/dto/ChatMessageRequest.java +++ b/src/main/java/com/market/saessag/domain/chat/dto/ChatMessageRequest.java @@ -12,7 +12,6 @@ @Getter @Builder public class ChatMessageRequest { - private Long senderId; private String content; private LocalDateTime timeStamp; } diff --git a/src/main/java/com/market/saessag/domain/chat/dto/ChatRoomCreateResponse.java b/src/main/java/com/market/saessag/domain/chat/dto/ChatRoomCreateResponse.java new file mode 100644 index 0000000..5e13195 --- /dev/null +++ b/src/main/java/com/market/saessag/domain/chat/dto/ChatRoomCreateResponse.java @@ -0,0 +1,12 @@ +package com.market.saessag.domain.chat.dto; + +import com.market.saessag.global.response.SuccessCode; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ChatRoomCreateResponse { + private SuccessCode successCode; + private ChatRoomResponse chatRoomResponse; +} diff --git a/src/main/java/com/market/saessag/domain/chat/service/ChatMessageService.java b/src/main/java/com/market/saessag/domain/chat/service/ChatMessageService.java index 4947869..e6c85de 100644 --- a/src/main/java/com/market/saessag/domain/chat/service/ChatMessageService.java +++ b/src/main/java/com/market/saessag/domain/chat/service/ChatMessageService.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -35,11 +36,12 @@ public class ChatMessageService { private final UserRepository userRepository; private final ChatMessageReadRepository chatMessageReadRepository; - public ChatMessageResponse saveMessage(Long roomId, ChatMessageRequest message) { + public ChatMessageResponse saveMessage(Long roomId, ChatMessageRequest message, SimpMessageHeaderAccessor headerAccessor) { ChatRoom chatRoom = chatRoomRepository.findById(roomId) .orElseThrow(() -> new CustomException(ErrorCode.ROOM_NOT_FOUND)); - User sender = userRepository.findById(message.getSenderId()) //세션으로 변경 + SignInResponse user = (SignInResponse) headerAccessor.getSessionAttributes().get("userProfile"); + User sender = userRepository.findById(user.getId()) .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); ChatMessage newMessage = ChatMessage.builder() @@ -110,11 +112,17 @@ public List searchMessages(Long roomId, String keyword, Htt // 읽음 처리 @Transactional - public void markMessagesAsRead(Long roomId, HttpServletRequest httpRequest) { + public void markMessagesAsRead(Long roomId, SignInResponse userSession) { ChatRoom chatRoom = chatRoomRepository.findById(roomId) .orElseThrow(() -> new CustomException(ErrorCode.ROOM_NOT_FOUND)); - User user = getUserFromSession(httpRequest); + + if (userSession == null) { + throw new CustomException(ErrorCode.UNAUTHORIZED); + } + + User user = userRepository.findById(userSession.getId()) + .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); List unreadMessages = chatMessageRepository.findByChatRoom(chatRoom); diff --git a/src/main/java/com/market/saessag/domain/chat/service/ChatRoomService.java b/src/main/java/com/market/saessag/domain/chat/service/ChatRoomService.java index f816acc..a58b0c1 100644 --- a/src/main/java/com/market/saessag/domain/chat/service/ChatRoomService.java +++ b/src/main/java/com/market/saessag/domain/chat/service/ChatRoomService.java @@ -1,5 +1,6 @@ package com.market.saessag.domain.chat.service; +import com.market.saessag.domain.chat.dto.ChatRoomCreateResponse; import com.market.saessag.domain.chat.dto.ChatRoomResponse; import com.market.saessag.domain.chat.entity.ChatMessage; import com.market.saessag.domain.chat.entity.ChatRoom; @@ -14,14 +15,17 @@ import com.market.saessag.domain.user.repository.UserRepository; import com.market.saessag.global.exception.CustomException; import com.market.saessag.global.exception.ErrorCode; +import com.market.saessag.global.response.SuccessCode; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @Service @@ -34,7 +38,7 @@ public class ChatRoomService { private final ChatSubscriptionRepository chatSubscriptionRepository; //방 생성 (방 존재 시 채팅방 반환) - public ChatRoomResponse createOrGetChatRoom(Long productId, Long buyerId, Long sellerId) { + public ChatRoomCreateResponse createOrGetChatRoom(Long productId, Long buyerId, Long sellerId) { Product product = productRepository.findById(productId) .orElseThrow(() -> new CustomException(ErrorCode.PRODUCT_NOT_FOUND)); @@ -44,21 +48,34 @@ public ChatRoomResponse createOrGetChatRoom(Long productId, Long buyerId, Long s User seller = userRepository.findById(sellerId) .orElseThrow(() -> new IllegalArgumentException("판매자를 찾을 수 없습니다.")); - ChatRoom chatRoom = chatRoomRepository.findByProductAndBuyerAndSeller(product, buyer, seller) - .orElseGet(() -> { - ChatRoom newRoom = ChatRoom.builder() - .product(product) - .buyer(buyer) - .seller(seller) - .build(); + Optional chatRoom = chatRoomRepository.findByProductAndBuyerAndSeller(product, buyer, seller); - return chatRoomRepository.save(newRoom); - }); + // 새로운 채팅방 생성하는 경우 + if (chatRoom.isEmpty()) { + ChatRoom newRoom = ChatRoom.builder() + .product(product) + .buyer(buyer) + .seller(seller) + .build(); + + chatRoomRepository.save(newRoom); + + // 구독 목록에 추가 + subscribeUserToChatRoom(buyer, newRoom); + subscribeUserToChatRoom(seller, newRoom); + + return ChatRoomCreateResponse.builder() + .successCode(SuccessCode.ROOM_CREATED) + .chatRoomResponse(chatRoomResponseEntity(newRoom)) + .build(); + } - subscribeUserToChatRoom(buyer, chatRoom); - subscribeUserToChatRoom(seller, chatRoom); + // 기존 채팅방 불러오는 경우 - return chatRoomResponseEntity(chatRoom); + return ChatRoomCreateResponse.builder() + .successCode(SuccessCode.ROOM_FETCHED) + .chatRoomResponse(chatRoomResponseEntity(chatRoom.get())) + .build(); } // 유저의 모든 채팅방 반환 diff --git a/src/main/java/com/market/saessag/global/config/WebSocketConfig.java b/src/main/java/com/market/saessag/global/config/WebSocketConfig.java index c60249b..6ddf21a 100644 --- a/src/main/java/com/market/saessag/global/config/WebSocketConfig.java +++ b/src/main/java/com/market/saessag/global/config/WebSocketConfig.java @@ -1,10 +1,12 @@ package com.market.saessag.global.config; +import com.market.saessag.global.interceptor.WebSocketHandShakeInterceptor; 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; +import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; @Configuration @EnableWebSocketMessageBroker @@ -13,7 +15,8 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { public void registerStompEndpoints(StompEndpointRegistry registry) { //WebSocket 엔드포인트 설정 (ws://localhost:8080/ws) registry.addEndpoint("/ws") - .setAllowedOrigins("*"); + .setAllowedOrigins("*") + .addInterceptors(new WebSocketHandShakeInterceptor()); } @Override diff --git a/src/main/java/com/market/saessag/global/interceptor/WebSocketHandShakeInterceptor.java b/src/main/java/com/market/saessag/global/interceptor/WebSocketHandShakeInterceptor.java new file mode 100644 index 0000000..dc6398a --- /dev/null +++ b/src/main/java/com/market/saessag/global/interceptor/WebSocketHandShakeInterceptor.java @@ -0,0 +1,33 @@ +package com.market.saessag.global.interceptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.HandshakeInterceptor; + +import java.util.Map; + +public class WebSocketHandShakeInterceptor implements HandshakeInterceptor { + + @Override + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception { + if (request instanceof ServletServerHttpRequest) { + ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; + HttpSession session = servletRequest.getServletRequest().getSession(false); + + if (session != null) { + // 세션 정보 유저 정보에서 가져와 WebSocket 세션에 저장 + attributes.put("userProfile", session.getAttribute("userProfile")); + } + } + return true; + } + + @Override + public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { + + } +} diff --git a/src/main/java/com/market/saessag/global/response/SuccessCode.java b/src/main/java/com/market/saessag/global/response/SuccessCode.java index de72605..7d5cee4 100644 --- a/src/main/java/com/market/saessag/global/response/SuccessCode.java +++ b/src/main/java/com/market/saessag/global/response/SuccessCode.java @@ -27,6 +27,7 @@ public enum SuccessCode { EMAIL_VERIFIED(HttpStatus.OK, "이메일 인증이 완료되었습니다"), SIGNUP_COMPLETED(HttpStatus.CREATED, "회원가입이 완료되었습니다"), ROOM_CREATED(HttpStatus.OK,"방이 성공적으로 생성되었습니다."), + ROOM_FETCHED(HttpStatus.OK,"기존 채팅방을 불러왔습니다."), DATA_FETCHED(HttpStatus.OK, "데이터 조회에 성공했습니다."); private final HttpStatus httpStatus;