SSE로 Spring Boot 실시간 업데이트 구현하기
Spring Boot에서 SSE를 이용해 서버에서 클라이언트로 실시간 이벤트를 푸시하는 개념과 구현 예제, 클라이언트 처리 및 주의사항 설명
목차
개요: SSE란 무엇인가
Server-Sent Events(SSE)는 서버에서 클라이언트로 단방향 실시간 데이터를 푸시하는 표준 기술이다. 웹소켓과 달리 연결이 간단하고 HTTP 기반으로 작동한다. 간단한 실시간 알림, 로그 스트리밍, 주기적 상태 업데이트에 적합하다. 이 글은 spring boot sse 사용법을 중심으로 server sent events spring boot 구현 흐름과 sse spring boot 예제를 다룬다.
SSE의 장단점
장점
- HTTP 기반으로 방화벽 우회가 용이하다.
- 브라우저에서 EventSource API로 쉽게 수신 가능하다.
- 구현이 비교적 단순하다.
단점
- 단방향 통신만 지원한다.
- 대량의 양방향 통신에는 적합하지 않다.
환경 설정
Spring Boot에서 SSE를 사용하려면 Spring Web(starter-web) 또는 WebFlux(starter-webflux)를 사용한다. 작은 예제는 MVC 기반의 SseEmitter로도 충분하다. 의존성은 다음과 같다.
pom.xml 의존성 예시
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
서버 구현: SseEmitter 예제
아래 코드는 MVC 환경에서 간단히 SSE를 구현한 컨트롤러이다. 클라이언트가 /stream 엔드포인트에 접속하면 서버는 주기적으로 이벤트를 전송한다.
package com.example.sse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@RestController
public class SseController {
@GetMapping("/stream")
public SseEmitter stream() {
SseEmitter emitter = new SseEmitter(0L); // timeout 없음
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
try {
emitter.send("data: 현재시간: " + System.currentTimeMillis() + "\n\n");
} catch (IOException e) {
emitter.completeWithError(e);
}
}, 0, 3, TimeUnit.SECONDS);
return emitter;
}
}
핵심 포인트
- SseEmitter는 서버에서 클라이언트로 스트리밍을 제공한다.
- 타임아웃을 0으로 설정하면 무한 대기 가능하다(자원 관리 유의).
- 에러 발생 시 completeWithError 또는 complete 호출로 연결을 종료한다.
리액티브 스타일: WebFlux와 Flux 사용
WebFlux를 사용하면 Flux<ServerSentEvent<T>> 형태로 더 선언적이고 확장성 있는 스트리밍을 만들 수 있다. 대량 연결과 백프레셔 처리가 필요한 경우 WebFlux가 유리하다.
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.time.Duration;
import java.time.LocalTime;
@RestController
public class FluxSseController {
@GetMapping(value = "/flux-stream", produces = "text/event-stream")
public Flux<ServerSentEvent<String>> streamFlux() {
return Flux.interval(Duration.ofSeconds(2))
.map(seq -> ServerSentEvent.builder("time: " + LocalTime.now()).id(String.valueOf(seq)).build());
}
}
클라이언트 구현 (브라우저)
브라우저에서는 EventSource API로 간단히 수신할 수 있다. reconnect는 브라우저가 자동으로 시도한다.
<!doctype html>
<html>
<head><meta charset="utf-8"/><title>SSE Client</title></head>
<body>
<div id="log"></div>
<script>
const evtSource = new EventSource('/stream');
evtSource.onmessage = function(event) {
const log = document.getElementById('log');
log.innerHTML += '<p>' + event.data + '</p>';
};
evtSource.onerror = function(err) {
console.error('SSE error', err);
};
</script>
</body>
</html>
운영에서의 고려사항
- 커넥션 수 제한과 스레드/리액터 자원 관리 계획 필요.
- 프록시나 로드밸런서에서 HTTP 타임아웃을 적절히 설정.
- 보안: 인증·인가 토큰을 헤더 또는 쿼리로 전달하고 만료 처리 필요.
- 재연결 정책: 클라이언트 단에서 재시도 로직을 보완하면 안정성 향상.
디버깅 및 테스트
curl로 간단히 이벤트를 확인할 수 있다. 서버가 text/event-stream을 반환하는지, 이벤트 포맷(data:, id:, event:)이 올바른지 점검한다.
맺음말
SSE는 단방향 실시간 업데이트에 적합한 경량 기술이다. spring boot sse 사용법을 이해하면 간단한 알림 시스템이나 모니터링 대시보드에 빠르게 적용할 수 있다. 더 많은 연결과 복잡한 양방향 통신이 필요하면 WebSocket과의 비교 검토가 필요하다. 위 예제는 server sent events spring boot와 sse spring boot 예제 참고용으로 기본 흐름을 설명한 내용이다.