본문 바로가기
PROJECT

[SOCKET] 웹소켓 댓글 작성 시 실시간 알림 기능

by 아이엠제니 2024. 7. 12.

 

💾 spring boot: 3.3.0

💾 jdk: 17

 


 

 

 

 

 

0. start


사용자 나나랑 모모가 같은 게시물을 보고 있을 때, 나나가 댓글을 적을 경우!

나나가 댓글을 추가했다는 알림이 다른 사용자에게도 가는 기능을 구현해 봤다.

다 구현한 건 아니고, 일단은 지금까지 구현한 것만 정리를 하고!

추후에 내가 원하게 수정해서 다시 기록으로 남겨야겠다.

 

일단 알림을 받기 위해서는 크롬이나 웨일의 '알림'기능이 허용되어 있어야 한다.

 

  • 크롬: 설정 -> 개인 정보 보호 및 보안 -> 사이트 설정 -> 알림 -> 알림 전송이 허용됨 (여기에 도메인 등 추가)
  • 웨일: 설정 -> 개인정보 보호 -> 사이트 설정 -> 알림 -> 알림 전송이 허용됨 (여기에 도메인 등 추가)

 

이렇게 추가를 해야, 알림을 받을 수 있다.

 

 

 

1. build.gradle


.
.
dependencies {
	    implementation 'org.springframework.boot:spring-boot-starter-websocket'
}
.
.
  • `websocket` 의존성 추가

 

실시간으로 알림을 받는 것을 구현하기 위해 `websocket` 의존성을 build.gradle 에 추가한다.

 

  • WebSocket
    • 클라이언트와 서버 간 실시간 양방향 통신을 가능하게 하는 프로토콜
      1. 양방향 통신: 클라이언트와 서버 간 양방향 실시간 통신을 제공함
      2. 이벤트 기반 메시징: 서버는 클라이언트에게 메시지를 즉각적으로 전달할 수 있음. 실시간 업데이트, 실시간 채팅, 실시간 게임 등 다양한 실시간 애플리케이션을 구현할 수 있음

 

 

 

 

 

2. WebSocketConfig.java


package com.crud.home.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) {
        registry.addEndpoint("/websocket")
                .setAllowedOriginPatterns("http://localhost:9090")
                .withSockJS(); // STOMP over WebSocket 엔드포인트를 등록
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic"); // /topic으로 시작하는 주소로 메시지를 클라이언트에게 전달하는 간단한 메모리 기반 브로커를 활성화
        config.setApplicationDestinationPrefixes("/app"); // @MessageMapping으로 지정된 메서드가 기본적으로 바인딩되는 /app에 메시지 브로커를 설정
    }

}

 

웹소켓 관련 설정을 정의하는 클래스

 

  • annotation
    • @Configuration
      • 해당 클래스가 Spring의 구성 클래스임을 나타냄
    • @EnableWebSocketMessageBroker
      • WebSocket 기능을 활성화하고, 메시지 브로커를 사용하도록 설정함
  • `registerStompEndpoints` method
    • registerStompEndpoints
      • 클라이언트에서 WebSocket 연결을 활성화하는 메서드
      • 클라이언트는 `/websocket` 엔드포인트를 사용하여 서버에 WebSocket 연결을 요청할 수 있음
    • setAllowedOriginPatterns("http://localhost:9090")
      • 클라이언트의 origin을 설정함
      • `http://localhost:9090` 에서 실행되는 클라이언트에서만 WebSocket 연결을 허용하도록 설정함
    • withSockJS()
      • WebSocket이 지원되지 않는 브라우저에서도 대체 수단으로 연결을 제공함
  • `configureMessageBroker` method
    • configureMessageBroker
      • 메시지 브로커 구성
        메시지 브로커는 클라이언트 간에 메시지를 라우팅하고 전달하는 역할을 함
    • `enableSimpleBroker("/topic")`
      • `/topic`으로 시작하는 주체를 구독하는 클라이언트에게 메시지를 전달하는 간단한 메모리 기반의 메시지 브로커를 활성화
    • `setApplicationDestinationPrefixes("/app")`
      • 클라이언트에서 수신된 메시지를 처리할 메서드가 바인딩될 기본 prefix를 설정함

 

 

 

3. CommentController.java


@RequiredArgsConstructor
@RestController
@Controller
@Slf4j
@RequestMapping("/comments")
public class CommentsController {

    private final CommentsService commentsService;
    private final SimpMessagingTemplate messagingTemplate;
    
    // 댓글 작성
    @PostMapping( "/add")
    public Comments commentCreate(@RequestParam(name="boardId") Long boardId, CommentReqDto dto
            , @AuthenticationPrincipal PrincipalDetails principalDetails) {

        Long memberId = principalDetails.getMember().getId();
        Comments comments = commentsService.insertComments(boardId, dto, memberId);
        log.info("commentReqDto = {} " + dto);
        log.info("memberId = {} " + memberId);

        // WebSocket
        String notificationMessage = comments.getBoard().getId() + "번 게시물에 "
                + principalDetails.getMember().getNickname() + " 작성자가 새로운 댓글을 추가했습니다.";
        messagingTemplate.convertAndSend("/topic/comments", notificationMessage);

        return comments;
    }
}
  • `private final SimpMessagingTemplate messagingTemplate;` 추가
    • WebSocket 및 STOMP 프로토콜을 사용하여 메시지를 전송하는 데 사용됨
  • 기존에 댓글 작성하는 controller에서 `commentCreate`에 WebSocket 부분을 추가함
    • 현재 로그인한 상태인 멤버의 닉네임과 메시지를 String에 담음
    • `messagingTemplate.convertAndSend()` 메소드를 사용하여 `/topin/comments`라는 topic으로 `notificationMessage`를 클라이언트에게 전송함
    • 이 topic을 구독하고 있는 모든 클라이언트에게 `notificationMessage`가 전달됨

 

 

 

4. detail.html


<script th:inline="javascript">
    /*<![CDATA[*/

    // WebSocket 연결 설정
    let socket = new SockJS('/websocket');
    let stompClient = Stomp.over(socket);

    stompClient.connect({}, function (frame) {
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/comments', function (message) {
            let notification = message.body; // 일반 텍스트 메시지 그대로 사용
            showNotification(notification);
        });
    });
    
    // 알림을 표시하는 함수
    function showNotification(message) {

        // 크롬 웹 알림 표시
        if (Notification.permission === "granted") {
            let notification = new Notification('새로운 댓글 알림', {
                body: message,
                requireInteraction: true // X 버튼을 끄기 전까지 유지
            });
        }
    }

    /*]]>*/

</script>
  • WebSocket 관련된 부분만 추가함
  • view는 thymeleaf 사용 중

 

  • `SockJS`로 `/websocket` 엔드포인트에 WebSocket 연결 설정함
  • `stomp.over(socket)`을 사용해 Stomp 클라이언트를 WebSocket에 연결함
  • `stopmpClient.connetct() {..}`
    • 서버와 연결 후 연결에 성공하면 콜백 함수 실행됨
    • `stompClient.subscribe() {...}`
      • `/topic/comments` 채널 구독함
      • 새로운 댓글 알림을 받는 데 사용함
      • 새로운 메시지가 도착하면 콜백 함수가 실행되고, `sohwNotification` 함수를 호출함
  • function showNotification() {...}
    • 브라우저의 알림 표시
    • `Notification.permission === "granted"`
      • 사용자가 알림을 허용했는지 확인함
      • `new Notification()
        • 새로운 알림 객체 생성함
        • 제목은 '새로운 댓글 알림', 내용은 'message' 변수의 값을 사용함
        • `requireInteraction: true`를 설정하여 사용자가 알림을 닫기 전까지 알림을 유지함

 

 

 

 

 

5. 결과


구현하고 싶었던 게 이게 맞긴 하다.

다만 자신이 작성한 댓글은 자신에게 알림이 오지 않아야 하는데, 현재는 작성자 본인과 상대방에게도 알림이 가고 있다.

크롬이랑 웨일 2개로 테스트를 하고 있는데, 이렇게 댓글 하나를 작성하면 알림이 2개씩 온다.

이 부분은 조금 수정이 필요할 것 같아서, 수정 후 다시 올리도록 해야겠다.

 

  • 내가 적은 댓글은 알림을 받지 않기
  • 목록에서도 댓글 알림을 받을 수 있도록 할 수 있는지? 가능하다면 이 부분도 구현하고 싶다.

 

추가 수정을 하면 다시 기록으로 남겨야겠다.

 

 

 

참고
OpenAi. (2024) ChatGPT (version 3.5)[Large Language model]. https://chat.openai.com

 

 

 

300x250