오늘의 기능 : 오늘은 직접 코드로 WebSocket을 사용해서 1:1 메세지 기능을 만들어 볼것이다
build.gradle 에 dependencies 추가해주기
아마 의존성을 넣어주면 reload 되면서 plugin을 추가 해주라고 할 것이다 그냥하면된다
// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-websocket'
}
@Configuration
- 설정파일을 만들기 우한 애노테이션 or Bean을 등록하기 위한 애노테이션
- 싱글톤 패턴은 객체 지향 프로그래밍에서 특정 클래스가 단 하나만의 인스턴스를 생성해준다
@EnableWebSocketMessageBroker- WebSocket 메시징 기능(STOMP 포함)을 활성화 한다는 뜻이야
registerStompEndpoints()
setApplicationDestinationPrefixes("/app")
registry.enableSimpleBroker("/topic")
@Configuration // 이 클래스는 Spring 설정 파일이라는 뜻이다
@EnableWebSocketMessageBroker // WebSocket 메세지 기능(STOMP 포함)을 활성화 한다는 뜻
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//연결할 endpoint ("/ws")
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*") //CORS 허용
.withSockJS(); // SockJs 지원
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 클라이언트에서 구득할 경로
registry.enableSimpleBroker("/topic");
// 클라이너트에서 메시지 보낼 때 사용하는 prefix
registry.setApplicationDestinationPrefixes("/app");
}
}
✅ 1:1 쪽지 기능을 위한 테이블 구조
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class MessageDTO {
private Long senderIdx;
private Long receiverIdx;
private String content;
private String imagesContent;
private LocalDateTime sendAt;
private boolean isRead;
}
connectSocket()
sendMessage()
function connectSocket() {
const socket = new SockJS('/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, function () {
console.log('Connected to WebSocket');
// 메시지 수신 구독
stompClient.subscribe('/topic/messages', function (message) {
const msg = JSON.parse(message.body);
console.log('메세지 도착:', msg);
showMessage(msg);
});
});
}
// 직접 메세지를 chat/send 로 보내준다
function sendMessage() {
const senderIdx = document.getElementById("loginIdx").value;
const receiverIdx = document.getElementById("targetIdx").value;
const message = document.getElementById("msgInput").value;
const now = new Date();
const sendAt = now.toISOString(); // ISO 8601 형식 (서버, JS 모두 호환)
stompClient.send("/app/chat/send", {}, JSON.stringify({
senderIdx: senderIdx,
receiverIdx: receiverIdx,
content: message,
sendAt: sendAt,
imagesContent: ''
}));
// 입력창 초기화
document.getElementById("msgInput").value = '';
document.getElementById("sendMessage").classList.add("deactive");
}
public class ChatController {
private final MessageService messageService;
// app/chat/send로 전송된 메시지를 처리
@MessageMapping("/chat/send")
@SendTo("/topic/messages")
public MessageDTO sendMessage(MessageDTO messageDTO) {
messageService.saveMessage(messageDTO);
return messageDTO;
}
}
html 에서 받아온 메세지 내용을 DTO -> 보내서 DB에 저장한다
public void saveMessage(MessageDTO dto) {
Message message = new Message();
System.out.println("💬 저장된 메시지 내용: " + message.getContent());
message.setSenderIdx(dto.getSenderIdx());
message.setReceiverIdx(dto.getReceiverIdx());
message.setContent(dto.getContent());
message.setImagesContent(dto.getImagesContent());
message.setSendAt(LocalDateTime.now());
dto.setSendAt(LocalDateTime.now());
message.setRead(false);
messageRepository.save(message);
}
List<Message> senderUser = messageRepository.findBySenderIdx(loginUser.getIdx());
for (Message message : senderUser) {
if (!addedIds.contains(message.getReceiverIdx())) {
Member member = memberRepository.findByIdx(message.getReceiverIdx())
.orElseThrow(() -> new RuntimeException("User not found"));
if (keyword == null || member.getUserName().contains(keyword)) {
SendUserList.add(member);
addedIds.add(message.getReceiverIdx());
// 제일 최근 메세지 들고오기
Optional<Message> recently = messageRepository
.findTopBySenderIdxAndReceiverIdxOrderBySendAtDesc(loginUser.getIdx(), message.getReceiverIdx());
recently.ifPresent(msg -> RecentlyMessage.put(message.getReceiverIdx(), msg));
}
}
}
List<Message> receiverUser = messageRepository.findByReceiverIdx(loginUser.getIdx()); // 나에게 메세지를 보낸 유저
for (Message message : receiverUser) {
if (!addedIds.contains(message.getSenderIdx())) {
Member member = memberRepository.findByIdx(message.getSenderIdx())
.orElseThrow(() -> new RuntimeException("User not found"));
if (keyword == null || member.getUserName().contains(keyword)) {
SendUserList.add(member);
addedIds.add(message.getSenderIdx());
// 제일 최근 메세지 들고오기
Optional<Message> recently = messageRepository
.findTopBySenderIdxAndReceiverIdxOrderBySendAtDesc(message.getSenderIdx(), loginUser.getIdx());
recently.ifPresent(msg -> RecentlyMessage.put(message.getSenderIdx(), msg));
}
}
// 상대가 나에게 보낸 메시지 중 안 읽은 것만 필터링
List<Message> unreadMessages = messageRepository.findBySenderIdxAndReceiverIdxAndIsReadFalse(message.getSenderIdx(), loginUser.getIdx());
if (!unreadMessages.isEmpty()) {
unreadMessageCountMap.put(message.getSenderIdx(), unreadMessages.size());
}
}
- 메세지 보내기
- 메세지 입력 완료
- 메세지 DB에서 확인
- 메세지 목록 리스트