Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat #95] 채팅 전송 웹소켓 API #96

Merged
merged 9 commits into from
Sep 1, 2024
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ out/
.DS_Store
/src/test/resources/application.yml
/src/main/generated/
/src/main/resources/application-local.yml
/src/main/resources/application-prod.yml
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ dependencies {

// MongoDB
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb:3.1.8'

// WebSocket
implementation 'org.springframework.boot:spring-boot-starter-websocket'
}

// Querydsl
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.dnd.gongmuin.chat.controller;

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.stereotype.Controller;

import com.dnd.gongmuin.chat.dto.ChatMessageRequest;
import com.dnd.gongmuin.chat.dto.ChatMessageResponse;
import com.dnd.gongmuin.chat.service.ChatMessageService;

import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
public class ChatMessageController {

private final ChatMessageService chatMessageService;

@MessageMapping("/chat-rooms/{chatRoomId}")
@SendTo("/sub/chat-rooms/{chatRoomId}")
public ChatMessageResponse sendMessage(
@DestinationVariable("chatRoomId") Long chatRoomId,
@Payload ChatMessageRequest request
) {
return chatMessageService.saveChatMessage(request, chatRoomId);
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
package com.dnd.gongmuin.chat.controller;

import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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.RestController;

import com.dnd.gongmuin.chat.dto.ChatMessageRequest;
import com.dnd.gongmuin.chat.dto.ChatMessageResponse;
import com.dnd.gongmuin.chat.service.ChatRoomService;
import com.dnd.gongmuin.common.dto.PageResponse;
import com.dnd.gongmuin.member.domain.Member;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@Tag(name = "채팅방 API")
Expand All @@ -28,17 +21,6 @@ public class ChatRoomController {

private final ChatRoomService chatRoomService;

@Operation(summary = "채팅방 메시지 등록 임시 API", description = "웹소켓 연결 전 임시 API")
@PostMapping("/api/chat-messages/{chatRoomId}")
public ResponseEntity<Void> registerChatMessage(
@PathVariable Long chatRoomId,
@Valid @RequestBody ChatMessageRequest request,
@AuthenticationPrincipal Member member
) {
chatRoomService.saveChatMessage(request, chatRoomId, member.getId());
return new ResponseEntity<>(HttpStatus.CREATED);
}

@Operation(summary = "채팅방 메시지 조회 API", description = "채팅방 메시지를 최신순으로 페이징한다.")
@GetMapping("/api/chat-messages/{chatRoomId}")
public ResponseEntity<PageResponse<ChatMessageResponse>> getChatMessages(
Expand Down
7 changes: 1 addition & 6 deletions src/main/java/com/dnd/gongmuin/chat/domain/ChatMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ public class ChatMessage {

private Boolean isRead;

private String mediaUrl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mediaUrl(사진, 동영상) 값을 content에 넣겠다는 말씀으로 이해했는데 맞을까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 맞습니다!


private MessageType type;

private LocalDateTime createdAt;
Expand All @@ -36,13 +34,11 @@ private ChatMessage(
String content,
long chatRoomId,
long memberId,
String mediaUrl,
MessageType type
) {
this.content = content;
this.chatRoomId = chatRoomId;
this.memberId = memberId;
this.mediaUrl = mediaUrl;
this.type = type;
this.isRead = false;
this.createdAt = LocalDateTime.now();
Expand All @@ -52,9 +48,8 @@ public static ChatMessage of(
String content,
long chatRoomId,
long memberId,
String mediaUrl,
MessageType type
) {
return new ChatMessage(content, chatRoomId, memberId, mediaUrl, type);
return new ChatMessage(content, chatRoomId, memberId, type);
}
}
4 changes: 1 addition & 3 deletions src/main/java/com/dnd/gongmuin/chat/dto/ChatMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ public static ChatMessageResponse toChatMessageResponse(
chatMessage.getMemberId(),
chatMessage.getChatRoomId(),
chatMessage.getContent(),
chatMessage.getType().getLabel(),
chatMessage.getMediaUrl()
chatMessage.getType().getLabel()
);
}

Expand All @@ -30,7 +29,6 @@ public static ChatMessage toChatMessage(
request.content(),
chatRoomId,
memberId,
request.mediaUrl(),
MessageType.of(request.type())
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.dnd.gongmuin.chat.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record ChatMessageRequest(
@NotBlank(message = "채팅 내용을 입력해주세요.")
String content,
@NotBlank(message = "채팅 타입을 입력해주세요.")
String type,
String mediaUrl

@NotNull(message = "회원 아이디를 입력해주세요.")
Long memberId
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ public record ChatMessageResponse(
Long memberId,
Long chatRoomId,
String content,
String type,
String mediaUrl
String type
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.dnd.gongmuin.chat.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.dnd.gongmuin.chat.domain.ChatMessage;
import com.dnd.gongmuin.chat.dto.ChatMapper;
import com.dnd.gongmuin.chat.dto.ChatMessageRequest;
import com.dnd.gongmuin.chat.dto.ChatMessageResponse;
import com.dnd.gongmuin.chat.repository.ChatMessageRepository;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class ChatMessageService {

private final ChatMessageRepository chatMessageRepository;

@Transactional
public ChatMessageResponse saveChatMessage(
ChatMessageRequest request,
Long chatRoomId
) {
Long memberId = request.memberId();
ChatMessage chatMessage = chatMessageRepository.save(ChatMapper.toChatMessage(request, chatRoomId, memberId));
log.info("chatRoomId = {}, memberId= {}, chatMessageId= {}", chatRoomId, memberId, chatMessage.getId());
return ChatMapper.toChatMessageResponse(chatMessage);
}
}
11 changes: 0 additions & 11 deletions src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
import org.springframework.transaction.annotation.Transactional;

import com.dnd.gongmuin.chat.dto.ChatMapper;
import com.dnd.gongmuin.chat.dto.ChatMessageRequest;
import com.dnd.gongmuin.chat.dto.ChatMessageResponse;
import com.dnd.gongmuin.chat.repository.ChatMessageRepository;
import com.dnd.gongmuin.common.dto.PageMapper;
import com.dnd.gongmuin.common.dto.PageResponse;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@Service
Expand All @@ -21,15 +19,6 @@ public class ChatRoomService {

private final ChatMessageRepository chatMessageRepository;

@Transactional
public void saveChatMessage(
@Valid ChatMessageRequest request,
Long chatRoomId,
Long memberId
) {
chatMessageRepository.save(ChatMapper.toChatMessage(request, chatRoomId, memberId));
}

@Transactional(readOnly = true)
public PageResponse<ChatMessageResponse> getChatMessages(Long chatRoomId, Pageable pageable) {
Slice<ChatMessageResponse> responsePage = chatMessageRepository
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/com/dnd/gongmuin/common/config/WebSocketConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.dnd.gongmuin.common.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 configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/sub"); // 구독 경로
registry.setApplicationDestinationPrefixes("/pub"); // 발행 경로
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws") // 웹소켓 연결 엔드포인트
.setAllowedOriginPatterns("*")
.withSockJS();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring()
.requestMatchers(
"/error", "/favicon.ico", "/api/auth/temp-signup", "/api/auth/temp-signin",
"/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html"
"/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html", "/ws/**"
);
}

Expand All @@ -91,7 +91,7 @@ public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();

configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000", "https://gongmuin.netlify.app",
"https://gongmuin.site/", "http://localhost:8080"));
"https://gongmuin.site/", "http://localhost:8080", "/ws/**"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setExposedHeaders(Arrays.asList("Set-Cookie", "Authorization"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ void getChatMessages() throws Exception {
.andExpect(jsonPath("$.content[0].memberId").value(chatMessages.get(0).getMemberId()))
.andExpect(jsonPath("$.content[0].chatRoomId").value(chatMessages.get(0).getChatRoomId()))
.andExpect(jsonPath("$.content[0].content").value(chatMessages.get(0).getContent()))
.andExpect(jsonPath("$.content[0].type").value(chatMessages.get(0).getType().getLabel()))
.andExpect(jsonPath("$.content[0].mediaUrl").value(chatMessages.get(0).getMediaUrl()));
.andExpect(jsonPath("$.content[0].type").value(chatMessages.get(0).getType().getLabel()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public static ChatMessage chatMessage() {
"하하",
1L,
1L,
null,
MessageType.TEXT
);
}
Expand Down
Loading