개발 기록

Server-Sent Events를 이용한 실시간 댓글 알림 본문

TIL

Server-Sent Events를 이용한 실시간 댓글 알림

수염차 2022. 1. 7. 00:45

https://velog.io/@max9106/Spring-SSE-Server-Sent-Events%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%95%8C%EB%A6%BC

 

[Spring + SSE] Server-Sent Events를 이용한 실시간 알림

코드리뷰 매칭 플랫폼 개발 중 알림 기능이 필요했다. 리뷰어 입장에서는 새로운 리뷰 요청이 생겼을 때 모든 리뷰가 끝나고 리뷰이의 피드백이 도착했을 때 리뷰이 입장에서는 리뷰 요청이 거

velog.io

https://velog.io/@dhk22/TIL-Day

 

🔥 TIL - Day 64 SSE를 이용한 실시간 알림

Springboot - SSE를 이용한 실시간 댓글알림 구현

velog.io

 

참고한 블로그


다 썼는데 날아감;

 

피드에 댓글을 달면 피드 작성자에게 댓글 알림이 가는 기능을 처음에는 웹소켓으로 했었다. 알림만 가면 되므로 양방향 통신이 필요없을 것 같아 sse로 코드 변경함.

 

sse : 이벤트가 [서버 -> 클라이언트] 방향으로만 흐르는 단방향 통신

 

 

 

프론트 - 클라이언트에서 서버로 연결

$(document).ready(function () {

//로그인이 되어있다면 sse 연결
                if (localStorage.getItem('token') != null) {
                    let eventSource = new EventSource(`http://localhost:8080/sub/` + userOwn);

//sse 연결 이벤트
                    eventSource.addEventListener("sse", function (event) {
                        console.log(event.data);
                    })

//댓글을 작성했을때 실행되는 이벤트
                    eventSource.addEventListener("addComment", function (event) {
                        let message = event.data;
                        alert(message);
                    })

                    eventSource.addEventListener("error", function (event) {
                        eventSource.close()
                    })
                }

 

백엔드

: 컨트롤러 - 서버에서는 EventSource를 통해 날아오는 요청을 처리할 컨트롤러가 필요

@RequiredArgsConstructor
@Slf4j
@RestController
public class SseApiController {

    private final NotificationService notificationService;

    @CrossOrigin
    @GetMapping(value = "/sub/{userId}")
    public SseEmitter subscribe(@PathVariable String userId) {
        return notificationService.subscribe(userId);
    }
}

 

:서비스 

1. sse 연결

@RequiredArgsConstructor
@Service
public class NotificationService {

    public static Map<String, SseEmitter> sseEmitters = new ConcurrentHashMap<>();
    public final FeedRepository feedRepository;

    public SseEmitter subscribe(String userId) {

        SseEmitter sseEmitter = new SseEmitter();
        try {
            // 연결!!
            sseEmitter.send(SseEmitter.event().id(userId).name("sse").data("연결완료!"));
        } catch (IOException e) {
            throw new ApiRequestException("연결 오류!");
        }

        // userId값을 key값으로 해서 SseEmitter를 저장
        sseEmitters.put(userId, sseEmitter);

        sseEmitter.onTimeout(() -> sseEmitters.remove(userId));
        sseEmitter.onCompletion(() -> sseEmitters.remove(userId));
        sseEmitter.onError((e) -> sseEmitters.remove(userId));

        return sseEmitter;
    }

-> 피드페이지에 접속하게되면 보내준 data내용이 콘솔창에 뜨게됨 ! 

 

2. 데이터 전송

   public class NotificationService {
    // ...
    
   //댓글이 달리면 피드 작성자에게 알림 생성해서 보내기.
    @Transactional
    public void send(Long feedId, String contents, User user) {
        //해당 피드 작성자 id 찾기
        Feed feed = feedRepository.findById(feedId).orElseThrow(
                () -> new ApiRequestException("해당하는 피드가 없습니다.")
        );
        String userId = feed.getUser().getKakaoId();
        String commentUsername = user.getUsername();

        if (sseEmitters.containsKey(userId)) {
            SseEmitter sseEmitter = sseEmitters.get(userId);
            try {
                //데이터 전송
                sseEmitter.send(SseEmitter.event().name("addComment").data( commentUsername + "님이 작성하신 피드에 댓글을 달았습니다 " + ": "+ contents));
            } catch (Exception e) {
                sseEmitters.remove(userId);
            }
        }
    }
}

이 메소드를 댓글 생성 로직에 넣어주면 댓글 생성시 같이 실행됨 !

 

완성 !

Comments