notification-systems

2 개의 포스트

netflix4분 읽기큐레이션 요약

개인화된 알림 시스템을 위한 빠른 사고와 느린 사고

Netflix의 개인화 알림 시스템은 장기적인 메시징 전략을 세우는 느린 정책(Slow Policy)과, 실제 발송 시점에 최적의 콘텐츠를 선택하는 빠른 정책(Fast Policy)으로 분리된다. 느린 정책은 회원별 주간 채널 빈도와 발송 페이싱을 결정하고, 빠른 정책은 해당 범위 안에서 즉각적인 관련성과 참여 가능성이 가장 높은 메시지를 선택한다. 이를 통해 단기 참여율뿐 아니라 알림 피로도와 채널 구독 해지 위험까지 함께 관리한다. ## 기존 단일 정책 방식의 한계 - 기존 시스템은 단일 알림이 단기간에 유발하는 인과적 효과를 예측해 발송 여부를 결정했다. - 즉시 시청, 클릭, 앱 방문 같은 단기 지표 최적화에는 효과적이지만, 다음과 같은 장기 영향을 반영하기 어려웠다. - 반복 발송으로 인한 알림 피로도 증가 - 몇 주에 걸쳐 나타나는 참여도 하락 - 이메일·푸시 수신 거부 가능성 증가 - 지속적인 콘텐츠 시청 습관과 회원 만족도 변화 - 발송 여부와 콘텐츠 순위를 하나의 판단으로 처리했기 때문에, 회원별 주간 발송 빈도는 명시적인 정책이 아니라 일별 의사결정의 부산물이었다. - 전체 발송량은 모델 점수 임계값으로 조절했지만, 임계값을 바꾸면 발송 빈도뿐 아니라 선택되는 메시지의 품질과 분포도 함께 변했다. - 결과적으로 회원마다 다른 참여 패턴에 맞춰 발송 빈도와 콘텐츠 선택을 독립적으로 개인화하기 어려웠다. ## 느린 정책과 빠른 정책의 계층 구조 - **느린 정책(Slow Policy)** - 주간 등 일정한 긴 시간 범위에서 회원별 메시지 계획을 수립한다. - 채널별 목표 빈도와 시간에 따른 발송 페이싱을 결정한다. - 장기 참여 패턴과 회원의 메시지 반응을 바탕으로 전략적 결정을 내린다. - **빠른 정책(Fast Policy)** - 실제 발송 기회가 발생할 때마다 작동한다. - 느린 정책이 정한 빈도와 페이싱 범위 안에서 가장 관련성 높은 메시지를 선택한다. - 특정 시점의 콘텐츠 적합성과 단기 참여 가능성을 최적화한다. - 푸시와 이메일 빈도를 독립적으로 조합한 이산적 행동 공간을 사용하며, 약 100개 수준의 채널별 페이싱 조합을 표현할 수 있다. ## 장기 효용 함수와 메시지 비용 느린 정책은 회원별 효용 함수를 최대화하는 행동을 선택한다. > U(member, action) = Σ wₖ · Rewardₖ(member, action) − Cost(action) - **긍정적 신호** - 회원이 알림을 통해 콘텐츠나 기능의 가치를 발견할 가능성 - 알림 이후의 시청, 클릭, 플랫폼 참여 가능성 - **부정적 신호** - 메시지 피로도 증가 가능성 - 특정 채널의 수신 거부 또는 이탈 가능성 - 부정적 피드백은 실제 데이터에서 매우 희소하기 때문에, 이를 모델링하는 것만으로는 충분하지 않다. - 부정적 비용이 거의 없다고 예측되면 모델이 “가능한 한 많이 발송”하는 정책으로 치우칠 수 있다. - 이를 방지하기 위해 모든 발송에 공통으로 적용되는 **보편적 메시지 비용(universal message cost)** 을 추가한다. - 이 비용은 보상 함수를 오목하고 안정적으로 만들어 과도한 발송 정책을 억제한다. - 비용의 크기는 온라인 실험과 오프라인 평가 지표를 함께 사용해 경험적으로 조정한다. ## 빈도와 시간 배분을 분리한 페이싱 - 느린 정책은 평균 발송 빈도뿐 아니라 일주일 동안 메시지를 어떻게 분산할지도 결정할 수 있다. - 가장 단순한 방식은 균등 무작위 페이싱이다. - 목표 빈도를 발송 기회별 확률로 변환한다. - 각 기회마다 확률에 따라 발송 여부를 무작위로 결정한다. - 개별 발송 시점은 달라질 수 있지만 장기적으로는 목표 발송률을 유지한다. - 향후에는 다음과 같은 비균등 페이싱도 적용할 수 있다. - 요일별 발송 패턴 - 회원의 최근 활동 여부에 따른 발송 - 콘텐츠 출시 시점에 맞춘 집중 발송 - 특정 이벤트나 캠페인에 맞춘 일시적 발송 증가 ## 두 정책 간의 비동기 통신 - 느린 정책과 빠른 정책은 저지연 feature store를 통해 연결된다. - **Planner** - 회원별 최적 페이싱 계획을 계산한다. - 계산된 전략적 의도를 feature store에 저장한다. - **Executor** - 매일 알림 발송 기회가 생기면 저장된 계획을 feature로 읽는다. - 해당 계획을 제약 조건으로 사용해 실제 발송 여부와 메시지를 결정한다. - 이 구조에서는 장기 계획 계산과 실시간 메시지 선택을 비동기적으로 분리할 수 있어, 각 정책이 서로 다른 시간 규모의 문제에 집중할 수 있다. ## 실용적인 결론 개인화 알림 시스템에서는 “무엇을 보낼 것인가”와 “얼마나 자주 보낼 것인가”를 하나의 모델에 맡기기보다 분리하는 것이 효과적이다. 장기 효용과 메시지 피로도를 반영하는 계획 계층을 먼저 만들고, 실시간 선택 계층이 그 계획 안에서 콘텐츠를 고르게 하면 단기 성과와 장기적인 회원 경험을 함께 최적화할 수 있다.

원문 읽기(새 탭에서 열림)
slack3분 읽기큐레이션 요약

슬랙이 알림 기능을 어떻게 다시 만들었나 📣

Slack은 알림이 많아서가 아니라 사용자가 알림 설정을 이해하고 통제하기 어려워서 “소음”을 느낀다고 진단했다. 이에 데스크톱과 모바일의 서로 다른 설정 체계를 하나의 모델로 통합하고, 활동 알림과 푸시 알림을 분리했다. 레거시 호환성과 롤백 가능성을 고려한 읽기 시점 마이그레이션, 자동 저장, 클라이언트 간 상태 동기화를 통해 더 예측 가능하고 차분한 알림 경험을 구축했다. ## 알림 소음의 근본 원인 - Slack에서 알림 과부하는 고객 불만의 주요 원인이며, 고객 경험 관련 문의의 상위 3개 요인에 포함됐다. - 참여한 채널 수가 많을수록 사용자는 알림 동작을 더 혼란스럽고 압도적으로 느꼈다. - 문제는 단순히 알림의 양이 아니라, 여러 해에 걸쳐 누적된 레거시 아키텍처에도 있었다. - 데스크톱과 모바일이 서로 다른 선호도 모델을 사용했다. - 모바일의 `nothing`과 데스크톱의 `Off`가 같은 의미가 아니었다. - 설정을 바꿔도 실제 결과를 예측하기 어려웠다. - 알림 대상과 전달 방식이 강하게 결합되어 있었다. - 푸시 알림을 줄이면 인앱 활동 확인까지 포기해야 했다. - 클라이언트 간 설정 동기화가 불안정해 데스크톱과 모바일에서 서로 다른 알림이 발생하거나 설정을 반복해야 했다. - 고급 기능이 여러 메뉴에 흩어져 있어 파워 유저도 전체 설정 구조를 파악하기 어려웠다. ## 하나의 알림 모델로 통합 Slack은 UI만 개편하지 않고 알림의 동작 방식 자체를 재설계했다. - 채널 알림을 세 가지 선택지로 단순화했다. - 모든 새 게시물 - 멘션 - 음소거 - 데스크톱과 모바일의 푸시 알림을 독립적인 켜기/끄기 옵션으로 통합했다. - 모바일에도 “모든 읽지 않은 항목 배지 표시” 같은 고급 제어 기능을 제공했다. - 데스크톱과 모바일의 전역 설정 구조와 문구를 일관되게 정비했다. - 단순화된 선호도 로직을 사용해 여러 클라이언트에서 동일한 상태를 유지하도록 개선했다. ## 선호도 데이터 마이그레이션 기존에는 데스크톱과 모바일 설정이 각각 알림 대상과 푸시 전달을 함께 결정했다. ```text 기존 desktop: everything | mentions | nothing mobile: everything | mentions | nothing ``` 새 모델에서는 활동과 푸시를 분리했다. ```text 변경 후 desktop: everything | mentions desktop_push_enabled: true | false mobile: everything | mentions | nothing ``` - 데스크톱의 `desktop_push_enabled`를 새로 도입해 푸시 알림만 별도로 제어했다. - 기존 사용자의 데이터베이스 값을 일괄적으로 직접 변경하지 않았다. - 잘못된 마이그레이션이나 롤백 문제를 피하기 위한 선택이다. - 새 설정을 기존 값에 기반해 백필했다. - 읽기 시점에 레거시 `Off`를 새로운 의미의 `Mentions + 푸시 비활성화`로 해석했다. - 사용자는 기존과 동일한 경험을 유지하면서도 새로운 분리형 알림 구조를 사용할 수 있었다. - 결과적으로 인앱 배지와 활동 알림은 계속 확인할 수 있고, 실제 방해가 되는 푸시만 별도로 끌 수 있게 됐다. ## 자동 저장과 “무엇을 받을지”와 “어떻게 받을지”의 분리 기존 알림 모달은 변경할 때마다 사용자가 `Save` 버튼을 눌러야 했다. - 저장을 잊으면 설정이 적용되지 않아 사용자가 알림 시스템이 고장 났다고 오해할 수 있었다. - 새 모달은 자동 저장을 적용해 변경 즉시 설정이 반영된다. - 알림 설정을 다음 두 축으로 분리했다. - 어떤 활동을 받을 것인가 - 어떤 방식으로 전달받을 것인가 - 예를 들어 모든 활동을 인앱에서 확인하되, 푸시는 멘션에 대해서만 받는 구성이 가능해졌다. - 데스크톱과 모바일 UI에는 재사용 가능한 React 컴포넌트를 활용해 일관성을 높였다. - 기존 모바일 전용 레거시 UI 코드도 대체해 플랫폼별 동작 차이를 줄였다. ## 크로스플랫폼 일관성 - 프로젝트는 수백 개의 댓글이 달린 기술 논의와 제품·디자인·프론트엔드·백엔드·모바일 팀 간 조율을 필요로 했다. - 핵심 목표는 단순히 같은 화면을 만드는 것이 아니라, 어떤 클라이언트에서 설정해도 동일한 상태와 의미를 갖게 하는 것이었다. - 이 구조를 통해 사용자는 데스크톱에서 바꾼 설정이 모바일에서 다르게 동작하는 문제를 덜 겪게 됐다. Slack의 접근 방식은 알림을 무조건 줄이는 대신, 활동 알림과 푸시 알림을 분리하고 설정 의미를 명확히 하는 데 초점을 둔다. 비슷한 시스템을 설계할 때도 레거시 값을 즉시 덮어쓰기보다 읽기 시점 호환성, 점진적 백필, 독립적인 설정 축, 자동 저장을 활용하는 것이 안전하고 사용자 혼란도 줄일 수 있다.

원문 읽기(새 탭에서 열림)