SSE 자체는 브라우저와 서버가 통신하는 하나의 "기술 규약(프로토콜)" 이다. 라이브러리가 아니다!! HTTP가 약속된 규칙인 것처럼 SSE도 약속이다!
즉 SSE는 웹 기술(웹 표준)이다!
그럼 이건 대체 무슨 약속일까?
서버는 Content-Type: text/event-stream으로 응답을 보내야 하고, 클라이언트는 EventSource 객체로 계속 데이터를 받는 약속이다. 이건 그냥 HTTP 프로토콜 위에서 데이터 스트림을 여는 규칙을 정해놓은 것이다.
내가 웹사이트를 열면서 "나한테 누가 팔로우하면 알려줘~"하고 서버에 요청을 보낸다. 그러면 서버는 그 요청을 끝내지 않고 문 열어놓은 상태로 계속 기다린다. 이 문을 관리하는 것이 SseEmitter다.
즉, SseEmitter는 서버가 클라이언트(브라우저)한테 "끊지 않고 계속" 데이터를 보내기 위해 잡고 있는 통로야.
서버가 이벤트를 보내고 싶을 때마다 이 Emitter를 통해 데이터를 쏜다.
Spring이 HTTP 연결을 끊지 않고 유지해준다. emitter.send()로 원하는 이벤트를 클라이언트에 보낸다. 클라이언트는 EventSource로 이벤트를 받는다.
SseEmitter emitter = new SseEmitter();
Spring은 이 SSE 표준에 맞춰 HTTP 연결을 열어주고
우리가 편하게 SseEmitter를 통해 이벤트를 보내게 도와준다.
유저 A가 연결했을 때 → A의 emitter를 저장해두고
유저 B가 연결했을 때 → B의 emitter를 또 따로 저장
나중에 누가 팔로우했는지에 따라 "A한테만 알림 보내자",
"B한테만 알림 보내자" 이런 식으로 개별 알림을 줄 수 있어야 하므로,
=> 그래서 userId별로 emitter를 Map에 저장!
Map<Long, List<SseEmitter>> emitters = new ConcurrentHashMap<>();
Long ➔ userId
List<SseEmitter> ➔ 그 유저가 연결한 emitter들 (브라우저 탭 여러 개 열 수도 있으니까)
예를 들어, 팔로우 알림을 만든다면 알림폴더에
📦 notification
┣ 📄 Notification.java
┣ 📄 NotificationRepository.java
┣ 📄 NotificationService.java
┣ 📄 FollowSseController.java
이런 구조로 FollowSseController를 하나 더 만들어야 한다.
NotificationController = 과거 알림 조회용
FollowSseController = 실시간 알림 푸쉬용