Patreon이 팬아웃으로 알림 시스템을 확장한 방법
(patreon.com)- 연간 수십억 건의 알림을 처리해 온 13년 된 시스템은 대형 크리에이터 한 명당 수백만 건을 생성하면서 타임아웃에 부딪혔고, Patreon은 수신자별 알림을 미리 생성하는 팬아웃 계층으로 이를 재설계함
- 수신자 목록을 작은 배치로 나누고 인앱 피드·푸시·이메일을 독립적으로 처리하는 2단계 팬아웃을 적용해 수평 확장성과 채널 장애 격리를 확보함
- 대형 크리에이터의 푸시·인앱 피드는 80%, 이메일은 55% 빨라졌으며, 청중이 4배 늘 때 종단 간 지연 증가율도 기존 186%에서 각각 33%와 60%로 낮아짐
- Notification Factory와 단일 API
send_fanout_notifications로 필터링·자격 검사·페이로드 생성을 표준화하고, 단계별 타이밍과 수신자별 결과를 추적해 운영과 디버깅을 단순화함 - 10개 팀과 30명 이상의 엔지니어가 200개 넘는 알림을 이전했으며, AI 기반 초안 생성과 리더십이 정한 명확한 마감 시한이 반복 작업과 장기 병행 운영을 줄이는 데 기여함
대형 크리에이터가 드러낸 확장 한계
- Patreon은 크리에이터가 게시물을 발행하거나 라이브스트림을 시작할 때 팬에게 연간 수십억 건의 알림을 보냄
- 무료 멤버십 출시와 성장으로 크리에이터당 팬 수가 급증하면서, 2025년 초 대형 크리에이터를 중심으로 심각한 성능 병목이 드러남
- 기존 게시물 알림은 하나의 비동기 작업이 전체 수신자를 가져와 사용자별 설정으로 필터링하고, 각 수신자의 페이로드를 생성한 뒤 인앱 피드·푸시·이메일 전달 시스템으로 넘기는 구조였음
- 대형 크리에이터의 경우 단일 작업이 수백만 건의 알림 생성을 맡으면서 지속적으로 타임아웃이 발생했고, 대규모 청중에게 알림을 안정적으로 전달하기 어려워짐
- 기존 흐름에는 두 가지 구조적 한계가 있었음
- 수신자별 생성 비용이 하나의 비동기 작업에 집중돼 수평 확장이 불가능하고 처리량에 명확한 상한이 생김
- 인앱 피드·푸시·이메일이 강하게 결합돼 한 채널의 실패가 다른 채널까지 막을 수 있었음
- 여러 제품 팀이 고용량 알림마다 별도의 확장 방식을 만들면서 단기 문제는 해결했지만, 플랫폼 복잡도가 높아지고 개발·운영·디버깅은 어려워짐
- 기존 전달 시스템은 유지하되 그 앞에 공통 팬아웃 계층을 배치해 수평 확장성, 채널 격리, 관측 가능성을 제공하기로 함
팬아웃 플랫폼의 여섯 가지 설계 조건
- 새 계층은 큰 수신자 목록을 작은 배치로 분할하고, 수신자별 알림을 생성한 뒤 기존 인앱 피드·푸시·이메일 시스템으로 전달하도록 설계함
- 플랫폼에는 다음 여섯 가지 요구사항을 적용함
- 개발자 친화성: 고용량 알림마다 확장성과 신뢰성을 처음부터 해결할 필요가 없어야 하며, 사용 오류를 줄이면서 다양한 사례를 지원해야 함
- 수평 확장성: 수신자 목록을 자동으로 작은 배치로 나눠 비동기로 처리해야 함
- 채널 격리: 인앱 피드·푸시·이메일을 독립적으로 실행해 한 채널의 장애가 다른 채널로 전파되지 않아야 함
- 우선순위 처리: 시간에 민감한 알림은 전용 큐와 워커 풀을 사용해 일반 알림과 분리해야 함
- 관측 가능성: 필터링 및 전달 결과를 조사하고 알림 생명주기 전체의 지연 시간을 측정할 수 있어야 함
- 확장 가능성: 수신자 목록 생성 같은 향후 기능을 또 다른 대규모 리팩터링 없이 추가할 수 있어야 함
Notification Factory로 비즈니스 로직 분리
- 플랫폼 오케스트레이션과 알림별 비즈니스 로직을 분리하기 위해 Notification Factory 추상화를 도입함
- 각 팩터리는 알림 처리의 청사진으로 다음 항목을 정의함
- 처리 우선순위
- 적용할 알림 설정
- 수신자 필터링 방식
- 알림별 자격 검사
- 채널별 페이로드 생성 방식
- 이 패턴은 Patreon의 자체 분산 작업 엔진을 기반으로 하며 여러 개념을 공유함
- Notification 모델은 특정 알림에 고유하면서 모든 수신자가 공유하는 데이터를 담은 작업 인자를 정의함
- 연결된 BaseNotificationFactory 하위 클래스는 작업 처리기로 동작하며, 수신자를 필터링하고 알림을 생성하는 설정과 메서드를 포함함
- 직렬화된 알림 페이로드만 받는 팬아웃 작업이 실행할 비즈니스 로직을 찾을 수 있도록, 각 Notification 모델을 해당 팩터리 클래스에 매핑하는 레지스트리를 사용함
- 다양한 알림 유형을 지원하는 동시에 흔한 구현 오류와 비효율적인 데이터 조회를 막아야 했음
- Patreon에는 여러 사용자 레코드 유형과 필터링 방식이 있으므로 수신자 인터페이스는 일반화함
- 사용자 설정과 알림별 자격 검사를 구현할 수 있는 명확한 훅을 제공함
단일 API와 2단계 팬아웃
- 과거에는 채널마다 별도의 발송 API를 사용해 인앱 피드·푸시·이메일 흐름이 서로 달라질 수 있었고, 제품 팀도 너무 이른 시점부터 채널별 동작을 고려해야 했음
- 재설계 후 진입점을 단일 API인
send_fanout_notifications로 통합함- 호출 측은 알림 페이로드, 수신자 목록, 전달 지연 측정에 사용할 타임스탬프를 넘김
- API는 해당 알림의 팩터리를 찾고 배치 크기와 우선순위를 결정한 뒤 채널별 작업을 독립적으로 분산함
- 한 채널의 실패나 지연이 다른 채널을 막지 않음
- 첫 번째 단계는 알림 생성 확장을 담당함
- 전체 수신자 목록을 작은 배치로 분할함
- 각 배치는
FanoutNotifications작업이 처리함 - 작업은 수신자를 필터링하고 알림별 자격 검사를 실행한 뒤 Notification Factory로 채널별 페이로드를 생성함
- 두 번째 단계는 전달 인계 확장을 맡음
- 생성된 알림을 인앱 피드·푸시·이메일 전용 전달 작업으로 다시 팬아웃함
- 핵심인
FanoutNotifications작업 처리기는 API와 기존 채널 전달 시스템 사이에 위치해, 하나의 거대한 작업을 독립적으로 처리할 수 있는 다수의 작은 작업으로 바꿈
수신자 단위 관측과 디버깅
- 알림은 사용자가 설정을 비활성화했거나 자격 검사를 통과하지 못했거나 이메일 주소로 전달할 수 없는 경우 등 여러 이유로 실패하거나 건너뛸 수 있음
- 기존 플랫폼에서는 특정 알림의 처리 결과를 확인하려면 여러 시스템의 로그와 데이터를 조합해야 했고, 포괄적인 수신자별 실행 추적도 없어 조사에 몇 시간이 걸릴 수 있었음
- 새 플랫폼은 다음 질문에 답할 수 있도록 설계함
- 알림 발송에 얼마나 걸렸는지
- 최초 수신자 목록이 얼마나 컸는지
- 몇 명이 어떤 이유로 필터링됐는지
- 채널별로 몇 건이 성공적으로 발송됐는지
- 특정 사용자가 특정 알림을 받거나 받지 않은 이유가 무엇인지
- 플랫폼 전체로 전달되는 타이밍·로깅 데이터 모델을 도입함
- 알림 흐름의 각 단계에서 타임스탬프를 기록함
- 집계 결과와 수신자별 결과 모두에 대해 메트릭과 이벤트를 생성함
- 개발자는 로깅 속성을 붙여 관련 엔티티를 실행 추적과 이벤트에 포함할 수 있음
- 관측성, 경보, 대시보드, 디버깅 도구가 개선되면서 제품 엔지니어와 지원 팀이 플랫폼 엔지니어에게 의존하지 않고 더 많은 문제를 조사할 수 있게 됨
200개가 넘는 알림 마이그레이션
- Notification Factory 패턴은 알림을 개발하고 유지하는 방식 자체를 바꿨기 때문에 단순한 코드 변환 도구(codemod) 만으로 이전할 수 없었음
- 각 알림의 비즈니스 로직, 데이터 의존성, 자격 규칙, 채널별 동작을 그대로 보존해야 했음
- 이전 문서와 모범적인 수작업 마이그레이션 PR을 바탕으로 AI 스킬을 구축함
- 개발자는
/migrate-notif-fanout <notif_name>을 실행해 1차 마이그레이션 결과를 생성할 수 있었음 - 알림 모델 생성, 팩터리 연결, 호출 지점 수정, 플랫폼 패턴 적용 같은 반복 작업에 효과적이었음
- 개발자는
- AI는 미묘한 비즈니스 로직을 놓치거나 환각을 일으키고 정리가 필요한 중복 코드를 만들 수 있어, 엔지니어의 판단을 대체하지 않았음
- 200개가 넘는 알림을 마이그레이션했으며, AI 활용을 포함한 대규모 협업으로 남은 80%를 6주 만에 완료함
성능 개선과 제품 개발 효과
- 수신자별 알림 생성을 수평 확장 가능한 작업으로 옮겨, 크리에이터 한 명당 수백만 건의 알림을 타임아웃 없이 안정적으로 생성할 수 있게 됨
- 대형 크리에이터의 고용량 알림 성능이 크게 개선됨
- 푸시와 인앱 피드 알림은 80% 빨라짐
- 이메일 알림은 55% 빨라짐
- 청중이 4배 늘 때 푸시·인앱 피드의 종단 간 지연은 33%, 이메일은 60% 증가함
- 같은 조건에서 기존 플랫폼의 지연 증가율은 186%였음
- 제품 팀은 고용량 알림마다 별도의 확장 솔루션을 설계할 필요가 없어졌고 개발 속도도 개선됨
- 새 플랫폼은 2025년의 Lives, Quips, 대형 크리에이터 확보 같은 주요 출시를 지원함
- 팬아웃 재설계가 없었다면 이들 출시는 기존 병목에 부딪혀 일회성 확장 솔루션을 만드는 데 추가 엔지니어링 시간이 필요했음
- 단계별 타이밍 메트릭으로 시간이 소비되는 지점을 파악하고, 속도와 비용의 균형에 맞춰 인프라를 조정할 수 있게 됨
9개월 병행 운영과 조직 조율
- 운영 중인 레거시 시스템을 변경하면서 여러 팀의 마이그레이션을 조율하는 일이 가장 어려운 부분이었음
- 9개월 동안 두 알림 시스템을 병행 운영함
- 모든 플랫폼 변경이 기존 경로와 새 팬아웃 경로에 모두 호환돼야 했음
- 버그와 장애가 발생할 수 있는 범위가 커졌고, 마이그레이션이 길어질수록 운영 위험도 늘어남
- 호환성 문제로 소규모 장애가 몇 차례 발생해 빠르고 안전하게 이전을 끝낼 필요성이 커짐
- 초기에는 각 제품 팀이 자체적으로 알림 마이그레이션의 우선순위를 정하는 상향식 방식을 택함
- 6개월 동안 약 20% 를 이전하는 데 그침
- 제품 로드맵과 다른 팀 우선순위에 밀리면서 진행 속도가 느려짐
- 엔지니어링 리더십이 2026년 1분기 완료에 합의하고 명확한 마감 시한과 전사 우선순위를 부여한 뒤, 남은 마이그레이션을 6주 만에 완료함
- 전체 작업에는 10개 팀과 30명 이상의 엔지니어가 참여함
- Notion 추적기와 자동화로 진행 상황을 관리함
- 이해관계자와 자주 소통하고 작업을 여러 팀에 분산함
- 플랫폼 팀이 약 절반의 마이그레이션을 수행한 뒤 알림 소유 팀에 배포와 정리를 넘김
- 알림이 많은 팀에 작업이 집중되지 않도록 부하를 분산함
- 오피스 아워, 리더보드, 상품, 해피아워를 포함한 알림 마이그레이션 주간을 열어 진행을 가속하고 완료를 기념함
- 전체 마감보다 2주 앞서 인계를 끝내는 완충 기간을 확보함
- 추가 지원이 필요한 팀을 돕고 코드베이스에서 누락된 작업을 검색함
- 마지막 점검에서 새로 필요한 마이그레이션 4개와 여러 미완료 항목을 찾아 일정에 작은 영향만 주고 마무리함
대규모 플랫폼 이전에서 얻은 교훈
- 명시적 우선순위가 없으면 여러 팀에 걸친 마이그레이션은 각 팀의 로드맵과 경쟁하며 쉽게 뒤로 밀림
- 확장 효과가 덜 뚜렷한 저용량 알림의 긴 꼬리 구간에서 특히 두드러짐
- 리더십 합의, 공동 우선순위, 명확한 마감 시한이 합리적인 기간 안에 이전을 끝내는 데 중요했음
-
플랫폼 작업은 API에서 끝나지 않음
- 이전에는 다른 팀의 알림 질문과 전달 문제를 조사하는 일이 플랫폼 엔지니어 온콜 업무의 상당 부분을 차지했음
- 관측성, 경보, 대시보드, 디버깅 도구를 함께 개선해야 제품 및 지원 팀이 독립적으로 조사할 수 있고 플랫폼 온콜 부담도 줄어듦
- 현재 병목뿐 아니라 다음 병목도 고려해야 함
- 한 번의 프로젝트로 모든 알림 확장 문제를 해결할 수는 없었음
- 향후 기능을 수용할 수 있게 설계해 알림량이 증가할 때 또 다른 대규모 리팩터링이 필요할 가능성을 낮춤
다음 병목과 향후 계획
- 알림 생성은 확장했지만 다음 병목은 수신자 목록 생성임
- 알림 대상이 계속 커지면 수백만 명의 수신자를 효율적으로 조회하는 일이 점점 더 중요해짐
- 수신자 목록 생성을 Notification Factory에 포함해 플랫폼이 종단 간 알림 흐름을 더 많이 담당하도록 할 계획임
- 알림 설정도 개발자와 팬 양쪽에서 개선할 예정임
- 개발자는 새 설정을 더 쉽게 만들고 관리할 수 있도록 지원함
- 팬은 알림 선호도를 더 쉽게 이해하고 제어할 수 있도록 개선함