SSE 통신 방식과 현재 진행 중인 프로젝트에서 어떻게 결제 시스템 처리 결과에 관한 알림을 다른 기능에서 받을 수 있을지 Claude AI를 통해 학습한 내용을 정리해봤습니다.
: Server-Sent Events(SSE)는 서버에서 클라이언트(브라우저)로 실시간 데이터를 전송하는 단방향 통신 기술이다.(HTTP 프로토콜을 기반)
서버가 클라이언트에게 지속적으로 이벤트를 푸시(sent)할 수 있다.
| 구분 | SSE | WebSocket |
|---|---|---|
| 통신 방향 | 단방향 (서버→클라이언트) | 양방향 |
| 프로토콜 | HTTP | WebSocket 프로토콜 |
| 복잡도 | 간단 | 상대적으로 복잡 |
| 적합한 사용 사례 | 알림, 실시간 피드, 진행 상태 | 채팅, 게임, 협업 도구 |
| 재연결 | 자동 | 수동 구현 필요 |
Flux는 Spring WebFlux에서 제공하는 비동기 데이터 스트림을 표현하는 타입이다. Java의 Stream API와 표현법이 유사하지만, 데이터가 주기적으로 생성되고 전달된다는 점이 다르다.
// Stream API (동기): 데이터가 이미 존재
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
// Flux (비동기): 데이터가 시간에 걸쳐 생성됨
Flux<String> flux = Flux.just("a", "b", "c");
➕ 프로젝트에서 자주 쓰던 ResponseEntity와의 비교:
ResponseEntity<String>: 하나의 값을 담는 상자 (택배)
ResponseEntity<Flux<String>>: 시간에 걸쳐 흐르는 데이터 스트림 (컨베이어 벨트)

: Sink는 데이터를 발행(emit)할 수 있는 중앙 통로다. 여러 구독자가 하나의 Sink를 통해 데이터를 받을 수 있다.
비유하자면,
Sink = 중앙 컨베이어 벨트
// Sink 1개를 여러 사용자가 공유
private final Sinks.Many<PaymentStatus> paymentSink =
Sinks.many().multicast().onBackpressureBuffer();
// 사용자별로 필터링
public Flux<PaymentStatus> getPaymentStream(Long orderId) {
return paymentSink.asFlux()
.filter(status -> status.getOrderId().equals(orderId));
}

[ ※ 본문과 관계없는 이미지입니다 ]
(with Portone V2)
AI와 학습하며 오갔던 질문과 대답입니다.
[결제 서비스] ─(Redis Pub/Sub)→ [주문 서비스] // 백엔드 간 통신
↓
(SSE)
↓
[브라우저] // 서버-클라이언트 통신
질문: 왜 결제를 하나의 트랜잭션으로 처리하지 않고 API를 분리했는가?
Portone API 가이드에서 결제 정보를 먼저 생성한 뒤 요청을 보내게 하는 이유가 궁금했다.
문제 상황:
해결:
POST /payment/prepare 결제 준비 API
[프론트엔드] 결제창 호출
POST /payment/verify 결제 검증 API
// 1. DB에서 우리가 저장한 정보 조회
Payment payment = paymentRepository.findByPaymentId(paymentId);
// 2. Portone API로 실제 결제 내역 조회
PortonePayment portonePayment = portoneClient.getPayment(paymentId);
// 3. 검증
if (payment.getAmount() != portonePayment.getAmount()) {
throw new PaymentVerificationException("금액 불일치");
}
if (!portonePayment.getStatus().equals("PAID")) {
throw new PaymentVerificationException("결제 미완료");
}
실제 프로젝트 Flow는 아니고, 통신 과정을 이해하기 위해 참고용으로 기록하려 합니다.
1. 브라우저 → 백엔드: POST /payment/prepare
← paymentId, amount
2. 브라우저: SSE 연결 시작 (/payment/stream/{paymentId})
3. 브라우저: PortOne.requestPayment() 호출
→ Portone 결제창 표시
4. 사용자 → Portone: 카드 정보 입력 및 결제
→ PG사 처리
5. Portone → 브라우저: 결제 완료 콜백
6. 브라우저 → 백엔드: POST /payment/verify
7. 백엔드 → Portone API: GET /v2/payments/{paymentId}
(실제 결제 내역 조회 및 검증)
8. 백엔드:
- DB 상태 변경 (READY → COMPLETED)
- Redis 발행 (주문 서비스에 알림)
- SSE로 브라우저에 알림
9. 브라우저: SSE로 결제 완료 메시지 수신
→ 사용자에게 완료 알림 표시
Portone V2 공식 문서
Spring WebFlux Flux 문서
SSE (Server-Sent Events) MDN 문서