독학으로 공부한 거라서 정확하지 않을 수 있습니다.
참고만 하시길 바랍니다!
스프링부트와 웹소켓 stomp를 이용해서 제가 실시간 채팅 서비스를 구현했던 과정을 써 보려고 합니다.
저는 gradle을 사용해서 아래 방법으로 적용시켰습니다.
implementation 'org.springframework.boot:spring-boot-starter-websocket'
@Configuration
@EnableWebSocketMessageBroker
public class StompConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws/chat")
.setAllowedOriginPatterns("*")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/pub");
registry.enableSimpleBroker("/sub");
}
}
구현 내용에 대한 설명은 다음과 같습니다.
소켓의 연결과 관련된 설정을 합니다.
Stomp 사용을 위한 Message Broker 설정을 해주는 메소드입니다.
@Entity
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Getter
@Table(name = "chat_tbl")
public class Chat {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long chatId;
@ManyToOne
@JoinColumn(name = "sender_member_id")
private Member sender;
@ManyToOne
@JoinColumn(name = "receiver_member_id")
private Member receiver;
private String message;
private LocalDateTime chatTime;
** 여기서 Member타입은 회원의 정보를 가진 엔티티 클래스 입니다.
public interface ChatRepository extends JpaRepository<Chat,Long> {
List<Chat> findAllBySenderAndReceiverOrderByChatTimeAsc(Member sender, Member receiver)
}
@Service
@RequiredArgsConstructor
@Transactional // 오류 발생시 롤백 위해서
public class ChatService{
private final ChatRepository chatRepository;
public void chatSave(Chat chat) {
chatRepository.save(chat);
return chat;
}
public List<Chat> getChatHistory(Long chatRoomId) {
List<Chat> chatHistory = chatRepository.findAllBySenderAndReceiverOrderByChatTimeAsc(ChatRoom.builder().chatroomId(chatRoomId).build());
return chatHistory;
}
}
** 저는 타임리프를 사용하고 있기 때문에 model에 값을 넣고, html 파일을 반환해 주고 있습니다.
@Controller
@RequiredArgsConstructor
public class ChatController {
private final ChatService chatService;
private final SimpMessagingTemplate template;
@GetMapping("/chat/{chatMember}")
public String chatRoomDetail(@AuthenticationPrincipal Member member, @PathVariable String chatMember,Model model) {
List<ChatDto> history = chatService.getChatHistory(roomId);
model.addAttribute("history", history);
return "chat/chatDetail";
}
@MessageMapping("/sendMessage/{senderId}")
public void message(@RequestBody Chat chat, @DestinationVariable String senderId) {
ChatDto receive = chatService.chatSave(chatDto);
template.convertAndSend("/sub/chat/receive/chatroom",receive);
}
}
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
$(function(){
connectStomp();
});
let sock = new SockJS(`https://${location.host}/ws/chat`);
let ws = Stomp.over(sock);
function connectStomp(){
ws.connect(headers,function(){
ws.subscribe("/sub/chat/receive/"+roomId,function(event){
let data = JSON.parse(event.body);
let sender = data.senderId;
let message = data.message;
let time = data.chatTime;
if(sender != myId){
receiveMessage(sender,message,time);
}
});
});
};
function sendMessage(){
let message = $('#sendMessage').val();
ws.send("/pub/sendMessage/"+myId,{},JSON.stringify({
"senderId" : myId,
"receiverId" : otherId,
"message" : message,
"chatTime" : getTimeAndSec()
}));
;}
let sock = new SockJS(`https://${location.host}/ws/chat`);
let ws = Stomp.over(sock);
function connectStomp(){
ws.connect({},function(){
ws.subscribe("/sub/chat/receive/"+roomId,function(event){
let data = JSON.parse(event.body);
let sender = data.senderId;
let message = data.message;
let time = data.chatTime;
...
});
});
};
function sendMessage(){
ws.send("/pub/sendMessage/"+myId,{},JSON.stringify({
"senderId" : myId,
"receiverId" : otherId,
"message" : message,
"chatTime" : getTimeAndSec()
}));
;}