본문 바로가기
반응형

이전 포스팅 마지막 jsp (list.jsp) 를 보면 레이아웃 수정이 있었는데 다른페이지들도 그렇게 맞추면된다.

 

그럼 댓글을 달았을 때 웹소켓을 사용하여 알람이 나오도록 한번 만들어 본다.

 

1. build.gradle 에 아래 내용을 추가한다.

 


  implementation 'org.springframework.boot:spring-boot-starter-websocket'
//웹소켓용
 
    implementation 'org.springframework.boot:spring-boot-starter-aop'
    implementation 'org.aspectj:aspectjweaver:1.9.19'
//AOP 용
 

 

알람을 보낼 websocket 과, 특정 로직을 처리할때 (댓글이 달릴때) 사용하기 위한 AOP 를 위해 디펜던시를 추가해준다.

 

 

2. WebSocket 설정

 

com.example.post.config 아래에 새로운 Config 클래스를 작성한다.

 

package com.example.post.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 WebSocketBrokerConfig implements WebSocketMessageBrokerConfigurer{
   
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config){
        config.enableSimpleBroker("/user");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry){
        registry.addEndpoint("/ws").setAllowedOrigins("http://localhost:8082").withSockJS();
    }
}

 

어떤 url 이 올때 받을것인지 prefix 를 설정해준다.

 

 

3. JS 설정

webapp 아래 script 라는 폴더를 만들어서 이곳에 알람관련한 JS 를 만들어주었다.

 

webapp/script/notification.js 에 위치하게끔 해주었다.

이유는 사용하는 페이지에 JS를 삽입하기 위해서다.

 

    var stompClient = null;
    var privateStompClient = null;
    var username = "";

    function getUsernameFromServer() {
        $.ajax({
            url: '/getUsername',  // 서버의 /api/username 엔드포인트
            type: 'GET',  // HTTP GET 요청
            success: function(data) {
                username = data;
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.error('Error fetching current user:', errorThrown);
            }
        });
    }
    getUsernameFromServer();
   

    priSocket = new SockJS('/ws');
    privateStompClient = Stomp.over(priSocket);
    privateStompClient.connect({}, function(frame){
        console.log(frame + "pri");
            privateStompClient.subscribe('/user/'+username+'/comments', function(result){
            console.log(result.body)
            show(JSON.parse(result.body))
        });
    });
 
    function show(message){
        var container = document.getElementById('notification-container');

        let alarm = "<a href='/content?num="+message.postId+"'>"+message.message+"</a>";
     
        // 알림 메시지 설정
        container.innerHTML = `
        <span class="close-button" onclick="hideNotification()">X</span>
          <span>${alarm}</span>
        `;
     
        container.classList.add('show');
     
        setTimeout(function() {
          hideNotification();
        }, 10000);

    }
   
    function hideNotification() {
        var container = document.getElementById('notification-container');
        container.classList.remove('show');
      }

 

priSocket 부터 웹소켓관련 코드이다.

소켓을 생성하고, 아래 subscribe 를 통해 어떤 URL 의 내용을 구독할지 지정한다.

여기의 경우 /user/${username}/comments 로 오는 내용들을 웹소켓이 받아서 처리할 예정이다.

result 로 받은값은 console.log 를 통해 한번 찍어보고 (테스트용), show 함수를 통해 알람창을 띄워준다.

 

알람 메시지안에 가공한 alarm 메시지와 X 버튼을 통해 알람창을 구현해준다. X버튼안누르면 10초뒤 사라짐

 

그럼 이 /user/${username{/comments 로는 누가 어떻게 보낼까?

 

4. AOP 설정

 

저 메시지를 보내기전에 댓글이 달릴때마다 메시지를 보낼 수 있도록 AOP설정부터 진행해본다.

 

com.example.post.aop 아래 관련 클래스를 하나 만들어준다.

 

package com.example.post.aop;

import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

import com.example.post.model.Comment;
import com.example.post.model.Post;
import com.example.post.service.PostService;


@Aspect
@Component
public class CommentNotificationAspect {

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    @Autowired
    private PostService postService;
   
    // @Before("execution(* com.example.post.service.CommentService.writeComment(..)) && args(comment)")
    // public void beforeWriteComment(Comment comment) {
    //     System.out.println("### Before writing comment: " + comment.getContents());
    // }
   
    @AfterReturning("execution(* com.example.post.service.CommentService.writeComment(..)) && args(comment)")
    public void afterWriteComment(Comment comment) {
       
        Post post = postService.getPost(comment.getPostId());
        // 댓글 작성 후 알림 보내기
        String notificationMessage =  post.getTitle() +" 게시글에 "+ comment.getAuthor() + "님이 댓글을 남기셨습니다.";

        System.out.println(notificationMessage + " " + post.getAuthor());

        Map<String, String> notification = new HashMap<>();
        notification.put("message", notificationMessage);
        notification.put("postId", post.getNum());

        simpMessagingTemplate.convertAndSendToUser(post.getAuthor(), "/comments", notification);
    }
}

 

Aspect 로 AOP 설정을 해주고, Component 로 등록한다.

 

어노테이션으로 메서드위에 언제 수행될지 표시를 해주는데 나는 writeComment 실행 후에 해당 메서드가 수행되게 만들어주었다. 

인자값으로 받는 Comment comment 는 실제 writeComment 의 args 를 가져올 수 있어 해당 내용으로 보낼 수 있다.

 


        
simpMessagingTemplate.convertAndSendToUser(post.getAuthor(), "/comments", notification);

마지막 이 코드를 통해 아까 구독중인 /user/${username}/comments 로 내용을 보낼 수 있다. 

 

/user/${username} 은 위 convertAndSendToUser 가 user를 지정해서 보내는거라 받을때 꼭 추가해주어야한다.

 

 

추가로 config 에 AppConfig 클래스를 작성한다.

package com.example.post.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
   
}

 

728x90
반응형

한걸음 한걸음

개인적인 기록