
Blog
하루 N억 건의 알림 시스템 구축기 (1): 카카오뱅크는 어떻게 발송 플랫폼의 새로운 뼈대를 세웠나
KakaoBank
2026.09.15
·Web·by igor#Architecture#Developer Experience#Messaging System#Performance Optimization#Scalability
핵심 포인트
- 1카카오뱅크는 레거시 발송 시스템의 구조적 한계와 성능 병목을 해결하기 위해 Service Layer와 Core Layer를 분리하는 계층화 아키텍처를 도입했습니다.
- 2개발자가 비즈니스 로직에만 집중할 수 있도록 복잡한 성능 최적화 요소를 Core Layer 내부로 캡슐화하고 단순한 인터페이스를 제공하여 멘탈 모델을 단순화했습니다.
- 3Lock-Free 방식의 Work-Stealing 알고리즘과 GC 최적화를 적용하여, 초당 1만 건 이상의 처리량(TPS)과 낮은 지연시간을 동시에 확보하며 시스템의 확장성을 극대화했습니다.
카카오뱅크 알림경험엔지니어링팀은 하루 N억 건의 메시지를 처리하는 UMS(Unified Messaging System) 발송기를 재구축하며, 시스템의 성능 최적화와 개발자 경험(DX) 개선이라는 두 가지 목표를 동시에 달성했습니다. 본 시스템의 핵심 철학은 "개발자의 멘탈 모델을 단순하게 유지하는 것"이며, 이를 위해 아키텍처를 Service Layer와 Core Layer로 계층 분리하였습니다.
1. 설계 전략: 계층 분리 아키텍처
플랫폼 개발자는 성능과 복잡성 사이의 Trade-off를 극복하기 위해 기술적 복잡성을 내부로 캡슐화했습니다.- Service Layer (단순 인터페이스 제공): 개발자는
RequestHandler와Sender라는 두 가지 핵심 인터페이스만 구현합니다. 비즈니스 로직(메시지 조립, 전송, 결과 처리)에만 집중할 수 있도록 동시성 제어나 큐 분배 로직을 배제하여 선언적(Declarative) API 구조를 갖추었습니다. - Core Layer (복잡성 및 최적화 캡슐화): 고성능 달성을 위한 Lock-Free, Work-Stealing, GC(Garbage Collection) 최적화 등 고도화된 기술이 구현되어 있습니다.
2. 핵심 기술: Age-Based Work-Stealing
레거시 시스템의 병목 원인이었던 '단일 큐에 의한 Tail Latency' 문제를 해결하기 위해 채널별로 큐를 분리하고, 부하 분산을 위한 Work-Stealing 알고리즘을 도입했습니다.- 동작 원리: 중앙 관리자가 100ms마다 각 큐를 스캔하여, 큐 생성 시각을 기준으로 일정 시간(Age > 500ms) 이상 대기한 큐를 발견하면 이를 유휴 상태인 Worker에게 분산시킵니다.
- Lock-Free 구현:
AtomicReferenceArray와CAS(Compare-And-Set)연산을 통해 Lock 획득/해제 오버헤드를 제거했습니다. 또한,Barrier를 활용하여 큐를 나눌 때 배열을 복사하지 않는 Zero-Copy 방식을 채택하여 메모리 할당을 최소화했습니다. - 성능 최적화 의사코드:
kotlin
fun steal(): List<Task>? {
val mid = (h + t) / 2
if (!barrier.compareAndSet(-1, mid)) return null
val stolen = (mid until t).mapNotNull { idx -> tasks.getAndSet(idx % 1024, null) }
tail.set(mid)
barrier.set(-1)
return stolen
}3. 추가 성능 최적화 기법
- GC 최적화: 고정 크기 Array를 사용하여 CPU 캐시 효율을 높이고, 처리된 요청을 즉시
null로 설정하여 객체가 Old Generation으로 승격되는 것을 방지했습니다. - Kotlin Inline Class: 불필요한 Wrapper 객체 생성을 줄여 primitive 타입과 유사한 성능을 확보했습니다.
- 템플릿 캐싱: 메시지 조립 시 매번 파싱하는 대신 전처리(Preprocessing)를 통해 변수 위치를 미리 캐싱함으로써, 알고리즘 복잡도를 에서 으로 개선했습니다. (단, : 치환 변수 개수, : 템플릿 본문 길이)