Files
llm-wiki/raw/company-tech-blogs/sse-realtime-notification-woowahan.md
T

9.8 KiB

title, source_type, url, archive_url, related_branches, related_projects, tags, created, last_reviewed
title source_type url archive_url related_branches related_projects tags created last_reviewed
company-tech-blog / 우아한형제들 기술블로그 — Server-Sent Events로 실시간 알림 전달하기 company-tech-blog https://techblog.woowahan.com/23199/
feature-streaming-response-contract
ca-skeleton
sse
server-sent-events
realtime
notification
woowahan
baemin
kafka
thundering-herd
backpressure
spring-webflux
coroutine
company-case-study
2026-06-02 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 모델이 다름