--- 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]]