[Photogram] 실시간 알림

2023. 3. 2. 19:22·🚗 Backend Toy Project/Photogram
  • 이번에는 자신이 구독한 사용자가 새로운 글을 업로드할 경우 실시간으로 알림이 보내지도록 구현해보았습니다.
  • 해당 기능은 서버 -> 클라이언트 방향으로만 이루어지는 단방향 통신만으로도 충분히 구현 가능했기 때문에 SSE(Server-Sent-Events)를 이용하였습니다.

📝 js

  • 우선 클라이언트 쪽 구현 코드입니다.
function storyLoad() {
	$.ajax({
		url: `/api/image?page=${page}`,
		dataType: "json"
	}).done(resp => {
		console.log(resp);
		resp.data.content.forEach((image) => {
			let item = getStoryItem(image);
			$("#storyList").append(item);
		});

		//로그인과 동시에 이벤트 등록
		notification();
	}).fail(error => {
		console.log(error);
	});
}

//notification
function notification() {
	let eventSource = new EventSource("http://localhost:8080/sub");

	eventSource.addEventListener("notification", function(event) {
		let message = event.data;

		let notification_content = document.getElementById('notification-content');
		let notification_container = document.getElementById('notification-container');

		notification_content.textContent = message;

		notification_container.classList.add('show');
		setTimeout(() => {
			notification_container.classList.remove('show');
		}, 2000);
	});

	eventSource.addEventListener("error", function(event) {
		eventSource.close();
	});
}
  • 해당 코드에서는 notification 함수를 정의하여 로그인과 동시에 해당 사용자에 대한 알림 이벤트가 등록되도록 하고 있습니다.
  • SSE 통신을 위해서는 먼저 클라이언트에서 서버로의 연결이 필요하며 해당 연결 요청을 보내기 위해서 자바스크립트에서는 EventSource 객체를 제공하고 있습니다.
  • 위 코드에서는 간단히 서버에서 메시지를 전달받아 해당 메시지를 사용자에게 알림으로 띄워주도록 구현하였습니다.

📝 Controller

  • 다음으로 서버에는 위의 EventSource 객체를 통해 전달되는 요청을 받을 수 있도록 컨트롤러를 구현해주었습니다.
package com.cos.photogram.web;

...

@RequiredArgsConstructor
@Controller
public class NotificationController {
	
	private final NotificationService notificationService; 
	
	public static Map<Long, SseEmitter> sseEmitters = new ConcurrentHashMap<>();
	
	@GetMapping(value = "/sub", produces = "text/event-stream")
	public SseEmitter subscribe(@AuthenticationPrincipal PrincipalDetails principalDetails,
			@RequestHeader(value = "Last-Event-ID", required = false, defaultValue = "") String lastEventId) {
		
		Long userId = principalDetails.getUser().getId();
		
		SseEmitter sseEmitter = notificationService.subscribe(userId);
		
		return sseEmitter;
	}
}
  • SSE 통신을 위해 MIME 타입을 text/event-stream으로 설정해주었습니다.
  • 추가로 lastEventId는 클라이언트가 마지막에 수신한 데이터의 id 값을 의미하며, 이를 이용하여 유실된 데이터를 다시 보내줄 수 있습니다.

📝 Service

  • 다음으로는 SSE 연결과 관련된 구체적이 로직을 서비스 단에 구현해주었습니다.
package com.cos.photogram.service;

...

@Service
@RequiredArgsConstructor
public class NotificationService {

    public SseEmitter subscribe(Long userId) {

            // 현재 클라이언트를 위한 SseEmitter 생성
            SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE);
            try {
                // 연결!!
                sseEmitter.send(SseEmitter.event().name("connect"));
            } catch (IOException e) {
                e.printStackTrace();
            }

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

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

            return sseEmitter;
    }
}

 

  • 클라이언트의 SSE 연결 요청에 응답하기 위해서는 SseEmitter 객체를 생성하여 반환해주어야 합니다. 
  • 이때 유효 시간을 지정해줄 수 있으며 해당 시간만큼 SSE 연결이 유지되고, 시간이 지나면 자동으로 클라이언트에서 재연결 요청을 보내게 됩니다.
  • 추가로 SseEmitter의 시간 초과 및 네트워크 오류를 포함한 모든 이유로 비동기 요청이 정상 동작할 수 없다면 저장해둔 SseEmitter를 삭제하도록 구현해주었습니다.

📝 ImageService

  • 마지막으로 실제 실시간 알림 기능을 필요로 하는 구간에 알림을 보내기 위한 간단한 처리를 수행해주었습니다.
  • 필자의 경우 다른 사용자가 새로운 이미지를 업로드 하는 경우에 알림이 보내지도록 구현하고자 했기 때문에 아래와 같이 업로드 로직 함수 안에 코드를 추가해주었습니다.
/* 이미지 업로드 */
@Transactional
public void upload(ImageUploadDto imageUploadDto, PrincipalDetails principalDetails) {

    ...

    /* 알림 부분 */
    List<Long> subsToList = subscribeRepository.findSubscribeTo(principalDetails.getUser().getId());
    for (Long id : subsToList) {
        SseEmitter sseEmitter = NotificationController.sseEmitters.get(id);
        try {
            sseEmitter.send(SseEmitter.event().name("notification").data("새로운 글을 업로드했습니다!"));
        } catch (Exception e) {
            NotificationController.sseEmitters.remove(id);
        }
    }
}
  • 우리는 User의 PK 값과 연결된 SseEmiiter 저장소를 위에서 구현해놓았으며, 해당 저장소에 현재 새로운 이미지를 업로드 한 사용자를 구독하고 있는 사용자가 있다면 이벤트를 발행할 것입니다.

📝 Result


📝 Reference

  • https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/
  • https://velog.io/@dhk22/TIL-Day

 

GitHub - Daegwon-Kim/SpringBoot-Photogram

Contribute to Daegwon-Kim/SpringBoot-Photogram development by creating an account on GitHub.

github.com

 

저작자표시 (새창열림)

'🚗 Backend Toy Project > Photogram' 카테고리의 다른 글

[Photogram] 연관 검색어  (0) 2023.03.05
[Photogram] 해시태그 기능 추가  (0) 2023.01.03
[Photogram] 로그인 실패 처리  (0) 2022.12.25
[Photogram] 검색 기능 구현  (0) 2022.12.22
[Photogram] OAuth2 페이스북 로그인  (0) 2022.07.19
'🚗 Backend Toy Project/Photogram' 카테고리의 다른 글
  • [Photogram] 연관 검색어
  • [Photogram] 해시태그 기능 추가
  • [Photogram] 로그인 실패 처리
  • [Photogram] 검색 기능 구현
Baeg-won
Baeg-won
  • Baeg-won
    좋았다면 추억이고 나빴다면 경험이다.
    Baeg-won
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 🍃 Spring, Spring Boot
        • 스프링 프레임워크 기초
        • 스프링 핵심 원리 - 기본편
        • 자바 ORM 표준 JPA 프로그래밍 - 기본편
        • 스프링 MVC
        • 실전! 스프링 부트와 JPA 활용1 - 웹 애플리..
      • 🥑 Web Technoloy
      • 🚗 Backend Toy Project
        • 스프링 부트 게시판
        • Photogram
        • Baeg-won Clothing Gallery
      • 🥇 Problem Solving
        • Breadth-First Search
        • Depth-First Search
        • Backtracking
        • Simulation
        • Two-pointer
        • Binary Search
        • Greedy
        • Dynamic Programming
        • Minimum Spanning Tree
        • Dijkstra
        • Floyd warshall
      • ☕ Java
        • 명품 자바 에센셜
        • Applications
      • 🍦 JavaScript
        • JavaScript 기초
      • 🐧 Linux
        • 이것이 리눅스다(CentOS 8)
      • 📟 Database
        • 혼자 공부하는 SQL
      • 🧬 Data Structure
      • 🎬 HTML
      • 🎤 Tech Interview
      • 📌 etc
        • Unity 2D Raising Jelly Game
        • C++
        • 영어 쉐도잉
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Baeg-won
[Photogram] 실시간 알림
상단으로

티스토리툴바