99 lines
9.8 KiB
Markdown
99 lines
9.8 KiB
Markdown
---
|
|
title: "company-tech-blog / 우아한형제들 기술블로그 — Server-Sent Events로 실시간 알림 전달하기"
|
|
source_type: company-tech-blog
|
|
url: https://techblog.woowahan.com/23199/
|
|
archive_url:
|
|
related_branches: [feature-streaming-response-contract]
|
|
related_projects: [ca-skeleton]
|
|
tags: [sse, server-sent-events, realtime, notification, woowahan, baemin, kafka, thundering-herd, backpressure, spring-webflux, coroutine, company-case-study]
|
|
created: 2026-06-02
|
|
last_reviewed: 2026-06-02
|
|
---
|
|
|
|
# 우아한형제들 기술블로그 — Server-Sent Events로 실시간 알림 전달하기
|
|
|
|
> Layer: `raw/company-tech-blogs/` — 우아한형제들(배달의민족) 기술블로그 게시물 발췌.
|
|
> Strength 분류: `company-case-study` — 대기업 기술 블로그의 특정 서비스 운영 사례. **공식 best practice 로 취급 금지.**
|
|
> 이 자료의 진술은 우아한형제들 특정 시스템(배민 알림 시스템, Spring WebFlux + Coroutine 환경, Kafka 브로커 아키텍처) 에 한정된 사례이며, ca-skeleton 의 최소주의 환경에 직접 적용 가능하다는 보장 없음.
|
|
|
|
## Parent / 활용 branch (필수)
|
|
|
|
| Branch | 이 자료가 정당화하는 결정 |
|
|
|---|---|
|
|
| [[raw/branch-notes/feature-streaming-response-contract]] | SSE 대규모 운영 시 발생하는 **실무 문제(thundering herd, backpressure, multi-server connection 관리)** 의 산업 사례 근거 — 미지원 결정의 운영 부담 evidence + 지원 결정 시 고려해야 할 운영 과제 식별 |
|
|
|
|
## 출처 / Source
|
|
|
|
- 원본 URL: https://techblog.woowahan.com/23199/
|
|
- 저자 / 조직: 한우석 (Han Woo-seok) / 우아한형제들 (배달의민족) 기술블로그
|
|
- 발행일: 2025-10-24
|
|
- 카테고리: Backend
|
|
- 마지막 확인일: 2026-06-02
|
|
|
|
## 왜 저장했는지 / Why archived
|
|
|
|
SSE 를 실제 프로덕션에서 일 4천만 건 이벤트 처리에 운영한 우아한형제들의 사례. WebSocket 대신 SSE 를 선택한 이유(기존 REST 인프라 유지), 운영 중 마주친 문제(thundering herd, Kafka consumer timeout, 보안 인증), 해결책(jitter, buffer overflow 설정, Kafka 브로커 아키텍처)을 구체적으로 기술. ca-skeleton 에서 SSE 도입 결정 시 "운영 부담" 항목의 현실적 evidence 로 활용.
|
|
|
|
## 핵심 인용 / Key quotes (verbatim)
|
|
|
|
> "이미 안정적으로 운영 중인 REST API 인프라가 있는 상황에서 WebSocket으로 전환하려면 모든 API를 WebSocket 기반으로 재구현해야 합니다"
|
|
|
|
> "저희 서비스는 서버에서 클라이언트로의 알림 전달이 핵심입니다"
|
|
|
|
> "두 가지 프로토콜을 동시에 운영하는 것보다 REST API + SSE 조합이 관리 비용 측면에서 효율적입니다"
|
|
|
|
> "메시지 발행자는 클라이언트의 연결 상태나 서버 위치를 알 필요 없음" [Kafka 브로커 채택 이유 — loose coupling]
|
|
|
|
> "모든 서버로 메시지 전달" [Kafka 브로드캐스트 아키텍처 — multi-server SSE 환경]
|
|
|
|
> "일평균 약 4천만 건의 이벤트를 안정적으로 처리"
|
|
|
|
> "모든 세션은 다시 한꺼번에 서버에 접속하기 위해 시도할 것입니다...CPU가 계속 spike 되는 현상" [thundering herd 묘사]
|
|
|
|
> "random의 jitter 시간을 설정해 골고루 분포되도록 하였습니다" [thundering herd 해결책]
|
|
|
|
> "buffer가 0이라 만약 버퍼에서 Consumer가 처리가 늦어진다면 해당 코루틴은 계속 기다릴 것입니다. 이것이 Kafka의 중단을 일으켰습니다" [backpressure 문제]
|
|
|
|
> "정확성과 안정성이 더 중요하므로 허용 가능한 수준이었습니다" [추가 네트워크 홉에 의한 약간의 지연 증가에 대한 결론]
|
|
|
|
## Claims Extracted / 추출된 주장
|
|
|
|
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
|
|---|---|---|---|---|---|
|
|
| WOOWA-SSE-C1 | 우아한형제들은 WebSocket 대신 SSE 를 선택한 이유로 "기존 REST API 인프라를 WebSocket 으로 재구현해야 하는 비용"과 "서버→클라이언트 단방향 알림이 핵심 요구사항"을 들었다 | "WebSocket으로 전환하려면 모든 API를 WebSocket 기반으로 재구현해야 합니다" + "서버에서 클라이언트로의 알림 전달이 핵심입니다" | `company-case-study` | 단방향 server push 알림이 주 목적이고 기존 REST 인프라를 유지하려는 상황 | WebSocket 이 일반적으로 SSE 보다 도입 비용이 높다는 universal rule — 신규 프로젝트에서는 양방향 통신 요구에 따라 다를 수 있음 |
|
|
| WOOWA-SSE-C2 | SSE 를 multi-server 환경에서 운영할 때 "thundering herd" 문제(서버 재시작 시 모든 세션 동시 재연결 → CPU spike)가 발생했다 | "모든 세션은 다시 한꺼번에 서버에 접속하기 위해 시도할 것입니다...CPU가 계속 spike 되는 현상" | `company-case-study` | 다수 클라이언트(규모 불명)가 연결된 multi-server SSE 환경에서 서버 재시작 시나리오 | ca-skeleton 의 소규모 사용(< 수백 connection) 에서도 동일 현상이 발생한다는 뜻 아님 — 규모에 따라 심각도 다름 |
|
|
| WOOWA-SSE-C3 | Thundering herd 해결책으로 random jitter 를 세션 재연결 retry 시간에 적용했다 | "random의 jitter 시간을 설정해 골고루 분포되도록 하였습니다" | `company-case-study` | SSE 재연결 정책에서 thundering herd 를 방지하려는 구현 | Jitter 가 thundering herd 를 완전히 제거한다는 뜻 아님 — 분산을 개선할 뿐, 효과는 jitter range 와 connection 수에 따라 다름 |
|
|
| WOOWA-SSE-C4 | SSE + Kafka 브로드캐스트 아키텍처에서 Kafka consumer 처리가 늦어지면 coroutine 이 무한 대기 → Kafka 중단(backpressure 미설정)이 발생했다 | "buffer가 0이라 만약 버퍼에서 Consumer가 처리가 늦어진다면 해당 코루틴은 계속 기다릴 것입니다. 이것이 Kafka의 중단을 일으켰습니다" | `company-case-study` | Spring WebFlux + Coroutine + Kafka consumer 조합 | Spring MVC (servlet 기반) 또는 Kafka 없는 SSE 구현에서도 동일 문제가 발생한다는 뜻 아님 — 이 문제는 Coroutine channel + Kafka 조합 특화 |
|
|
| WOOWA-SSE-C5 | 우아한형제들 배민 알림 시스템은 일평균 약 4천만 건 이벤트를 SSE 로 안정적으로 처리했다 | "일평균 약 4천만 건의 이벤트를 안정적으로 처리" | `company-case-study` | 우아한형제들의 특정 배민 알림 시스템 (규모 · 아키텍처 · 인프라 명시 필요) | ca-skeleton 같은 범용 skeleton 도 동일 규모를 지원한다는 뜻 아님 — 이 수치는 우아한형제들의 전용 아키텍처(Kafka + multi-server + Coroutine) 기반 |
|
|
| WOOWA-SSE-C6 | SSE 서버를 multi-server 로 확장(auto-scaling) 할 때 "모든 서버에 브로드캐스트" 아키텍처(Kafka)를 통해 발행자가 클라이언트 연결 서버 위치를 알 필요 없게 했다 | "메시지 발행자는 클라이언트의 연결 상태나 서버 위치를 알 필요 없음" | `company-case-study` | SSE + horizontal scaling 환경. sticky session 없이 구현하려는 경우 | Kafka 가 SSE 의 multi-server 문제를 해결하는 유일한 방법이라는 뜻 아님 — Redis Pub/Sub, Hazelcast 등 대안 존재 |
|
|
|
|
## Usage Boundaries / 적용 경계
|
|
|
|
- **이 자료가 직접 증명하는 것** (단, `company-case-study` strength 한정):
|
|
- `C1`: 단방향 알림 + REST 인프라 유지 상황에서 SSE 가 WebSocket 대비 도입 비용 낮음 (우아한형제들 판단)
|
|
- `C2`, `C3`: Multi-server SSE 환경에서 thundering herd 는 실제 운영 문제이며 jitter 로 완화
|
|
- `C4`: SSE + Kafka + Coroutine 조합에서 backpressure buffer 설정 미흡 시 Kafka consumer 중단 가능
|
|
- `C5`: 일 4천만 이벤트 규모 SSE 운영이 가능함 (이 아키텍처와 인프라 하에서)
|
|
- `C6`: SSE 의 multi-server 확장 시 메시지 브로커 패턴(Kafka 브로드캐스트)이 유효
|
|
- **이 자료가 증명하지 않는 것**:
|
|
- SSE 가 WebSocket 보다 일반적으로 운영 부담이 낮다는 universal claim — 이 팀의 특정 요구사항(단방향, REST 유지) 에서의 판단
|
|
- SSE 가 ca-skeleton 같은 최소주의 skeleton 에서도 동일하게 쉽게 운영된다는 주장 — 이 팀은 Spring WebFlux + Kafka 라는 별도 인프라를 갖춤
|
|
- Thundering herd 나 backpressure 가 SSE 에만 특유한 문제라는 주장 — WebSocket, long-polling 도 유사 문제 존재
|
|
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
|
- ca-skeleton 이 Spring MVC (servlet) 기반이면, Coroutine + Kafka 아키텍처의 backpressure 문제 (`C4`) 는 직접 해당되지 않음
|
|
- ca-skeleton 의 SSE 도입 시 multi-server sticky session 정책 또는 Kafka/Redis Pub/Sub 필요 여부 결정 필요
|
|
- ca-skeleton 예상 connection 수 규모 — 소규모(< 100 connection)에서는 thundering herd (`C2`) 심각도 낮음
|
|
|
|
## 메모 / Notes
|
|
|
|
- `C1` 은 **운영팀의 판단** (`company-case-study`) — "WebSocket 은 항상 도입 비용이 높다" 는 공식 best practice 아님
|
|
- `C5` 의 "4천만 건" 수치는 우아한형제들의 특정 시스템 · 아키텍처 · 인프라 기반 — ca-skeleton 에 외삽 금지
|
|
- 이 아티클은 Spring WebFlux + Coroutine 환경 기준 — Spring MVC (ca-skeleton default) 와 threading 모델이 다름
|
|
|
|
## Related / 관련
|
|
|
|
- 같은 주제 다른 raw 자료: [[raw/official-docs/whatwg-html-server-sent-events]] (SSE 프로토콜 공식 사양)
|
|
- 같은 주제 다른 raw 자료: [[raw/official-docs/spring-mvc-async-streaming]] (Spring MVC SseEmitter vendor doc)
|
|
- 같은 주제 다른 raw 자료: [[raw/company-tech-blogs/realtime-service-experience-woowahan-websocket]] (우아한형제들 WebSocket 실시간 운영 경험기)
|
|
- 인용하는 branch: [[raw/branch-notes/feature-streaming-response-contract]]
|