실전 설계 예제 2 (Practical Design Examples 2)

실전 설계 예제 2 (Practical Design Examples 2)

난이도: ⭐⭐⭐⭐

개요

이 장에서는 소셜 미디어와 실시간 통신 시스템을 설계합니다: 뉴스 피드/타임라인, 채팅 시스템, 알림 시스템. 이러한 시스템은 대규모 사용자를 다루며, 실시간성과 확장성이 중요한 과제입니다.


목차

  1. 뉴스 피드 / 타임라인
  2. 채팅 시스템
  3. 알림 시스템
  4. 연습 문제

1. 뉴스 피드 / 타임라인

1.1 요구사항 정의

┌─────────────────────────────────────────────────────────────────────────┐
│                     기능 요구사항                                       │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  핵심 기능:                                                             │
│  1. 포스트 작성: 텍스트, 이미지, 비디오                                 │
│  2. 뉴스 피드 조회: 팔로우하는 사용자의 포스트                          │
│  3. 타임라인 조회: 특정 사용자의 포스트 목록                            │
│                                                                         │
│  부가 기능:                                                             │
│  4. 좋아요, 댓글                                                        │
│  5. 무한 스크롤                                                         │
│  6. 실시간 업데이트                                                     │
│                                                                         │
├─────────────────────────────────────────────────────────────────────────┤
│                     규모                                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  - 일간 활성 사용자(DAU): 100M                                          │
│  - 사용자당 팔로우: 평균 200명                                          │
│  - 일간 포스트: 10M                                                     │
│  - 피드 조회 빈도: 10회/일/사용자                                       │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

1.2 핵심 과제: Push vs Pull

┌─────────────────────────────────────────────────────────────────────────┐
                     피드 생성 전략                                      
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  방법 1: Pull (Fan-out on Read)                                        
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    사용자가 피드 요청 :                                             
                                                                       
    1. 팔로우 목록 조회                                                
    2.  팔로우의 최신 포스트 조회                                    
    3. 병합  정렬                                                    
    4. 반환                                                            
                                                                       
    ┌──────────┐                                                      
       User   │─── GET /feed                                         
    └────┬─────┘                                                      
                                                                      
                                                                      
    ┌────────────────────────────────────────────────────────────┐    
      SELECT * FROM posts                                           
      WHERE author_id IN (SELECT followee FROM follows              
                          WHERE follower = user_id)                 
      ORDER BY created_at DESC                                      
      LIMIT 20;                                                     
    └────────────────────────────────────────────────────────────┘    
                                                                       
    장점:                                                              
    - 저장 공간 절약                                                   
    - 포스트 작성이 빠름                                               
                                                                       
    단점:                                                              
    - 피드 조회가 느림 (팔로우 많으면)                                 
    - DB 부하                                                          
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  방법 2: Push (Fan-out on Write)                                       
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    포스트 작성 :                                                    
                                                                       
    1. 포스트 저장                                                     
    2. 모든 팔로워의 피드에 추가                                       
                                                                       
    ┌──────────┐                                                      
      Author  │─── POST /posts                                       
    └────┬─────┘                                                      
                                                                      
                                                                      
    ┌──────────────┐                                                  
     Save Post                                                      
    └──────┬───────┘                                                  
                                                                      
                                                                      
    ┌──────────────┐     ┌──────────────────────────────────────┐     
     Fan-out to   │────►│ Follower 1 Feed: [post_id, ...]           
     all followers│────►│ Follower 2 Feed: [post_id, ...]           
                  │────►│ Follower 3 Feed: [post_id, ...]           
                  │────►│ ...                                        
    └──────────────┘     └──────────────────────────────────────┘     
                                                                       
    장점:                                                              
    - 피드 조회가 빠름 (사전 계산됨)                                   
                                                                       
    단점:                                                              
    - 저장 공간 많이 사용                                              
    - 셀럽(팔로워 많은 사용자) 포스트 지연                             
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

1.3 하이브리드 접근

┌─────────────────────────────────────────────────────────────────────────┐
                     하이브리드 팬아웃                                   
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  핵심 아이디어:                                                         
  - 일반 사용자: Push (Fan-out on Write)                                
  -  유저(셀럽): Pull (Fan-out on Read)                               
                                                                         
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    포스트 작성:                                                       
                                                                       
    ┌──────────┐                                                      
      Author  │──► 팔로워  확인                                    
    └────┬─────┘                                                      
                                                                      
    ┌────┴────┐                                                        
                                                                     
                                                                     
   < 10K     10K                                                     
   팔로워   팔로워                                                     
                                                                     
                                                                     
   Push     Save to                                                   
   to all   hot_posts                                                 
   feeds    table only                                                
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  피드 조회:                                                             
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    1. 사전 계산된 피드 조회 (Push된 포스트)                           
    2. 팔로우 중인  유저의 최신 포스트 조회                          
    3. 병합  정렬                                                    
                                                                       
    ┌──────────────────────────────────────────────────────────┐      
                                                                    
      Pre-computed Feed     Hot Users Posts                         
      ┌───────────────┐    ┌───────────────┐                       
       [post1]            [celeb_post1]                        
       [post2]        +   [celeb_post2]                        
       [post3]                                                 
      └───────────────┘    └───────────────┘                       
                                                                  
               └────────┬───────────┘                               
                                                                   
                 Merge & Sort                                       
                                                                   
                                                                   
                Final Feed                                          
                                                                    
    └──────────────────────────────────────────────────────────┘      
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

1.4 시스템 아키텍처

┌─────────────────────────────────────────────────────────────────────────┐
│                     뉴스 피드 아키텍처                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌───────────────────────────────────────────────────────────────────┐ │
│  │                                                                   │ │
│  │   ┌────────┐                                                     │ │
│  │   │ Client │                                                     │ │
│  │   └───┬────┘                                                     │ │
│  │       │                                                          │ │
│  │       ▼                                                          │ │
│  │   ┌─────────────────┐                                            │ │
│  │   │   API Gateway   │                                            │ │
│  │   └───────┬─────────┘                                            │ │
│  │           │                                                      │ │
│  │   ┌───────┴───────┐                                              │ │
│  │   │               │                                              │ │
│  │   ▼               ▼                                              │ │
│  │ ┌─────────────┐ ┌─────────────┐                                 │ │
│  │ │ Post Service│ │ Feed Service│                                 │ │
│  │ └──────┬──────┘ └──────┬──────┘                                 │ │
│  │        │               │                                        │ │
│  │        ▼               │                                        │ │
│  │ ┌─────────────┐        │                                        │ │
│  │ │   Posts DB  │        │                                        │ │
│  │ │  (Sharded)  │        │                                        │ │
│  │ └──────┬──────┘        │                                        │ │
│  │        │               │                                        │ │
│  │        ▼               ▼                                        │ │
│  │ ┌──────────────────────────────────────┐                        │ │
│  │ │           Message Queue              │                        │ │
│  │ │            (Kafka)                   │                        │ │
│  │ └─────────────────┬────────────────────┘                        │ │
│  │                   │                                              │ │
│  │                   ▼                                              │ │
│  │ ┌──────────────────────────────────────┐                        │ │
│  │ │        Fanout Workers                │                        │ │
│  │ │  ┌────────┐ ┌────────┐ ┌────────┐   │                        │ │
│  │ │  │Worker 1│ │Worker 2│ │Worker 3│   │                        │ │
│  │ │  └───┬────┘ └───┬────┘ └───┬────┘   │                        │ │
│  │ └──────┼──────────┼──────────┼────────┘                        │ │
│  │        │          │          │                                  │ │
│  │        └──────────┼──────────┘                                  │ │
│  │                   ▼                                              │ │
│  │ ┌──────────────────────────────────────┐                        │ │
│  │ │         Feed Cache (Redis)           │                        │ │
│  │ │   user:123:feed = [post_ids...]      │                        │ │
│  │ └──────────────────────────────────────┘                        │ │
│  │                                                                   │ │
│  └───────────────────────────────────────────────────────────────────┘ │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

1.5 피드 캐싱 전략

┌─────────────────────────────────────────────────────────────────────────┐
│                     피드 캐싱                                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Redis 피드 구조:                                                       │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                  │   │
│  │  Key: feed:{user_id}                                            │   │
│  │  Type: Sorted Set                                               │   │
│  │  Score: timestamp                                               │   │
│  │  Member: post_id                                                │   │
│  │                                                                  │   │
│  │  ZADD feed:123 1704067200 "post_abc"                           │   │
│  │  ZADD feed:123 1704067300 "post_def"                           │   │
│  │  ...                                                            │   │
│  │                                                                  │   │
│  │  조회: ZREVRANGE feed:123 0 19                                  │   │
│  │  → 최신 20개 포스트 ID                                          │   │
│  │                                                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  캐시 관리:                                                             │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                  │   │
│  │  - 피드 크기 제한: 최근 800개 포스트만 유지                      │   │
│  │    ZREMRANGEBYRANK feed:123 0 -801                              │   │
│  │                                                                  │   │
│  │  - TTL 설정: 활성 사용자만 캐시 유지                             │   │
│  │    비활성 사용자 → 조회 시 재구축                               │   │
│  │                                                                  │   │
│  │  - 포스트 내용은 별도 캐시                                       │   │
│  │    Key: post:{post_id}                                          │   │
│  │    Value: { author, content, media, ... }                       │   │
│  │                                                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

2. 채팅 시스템

2.1 요구사항 정의

┌─────────────────────────────────────────────────────────────────────────┐
│                     기능 요구사항                                       │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  핵심 기능:                                                             │
│  1. 1:1 채팅                                                            │
│  2. 그룹 채팅 (최대 500명)                                              │
│  3. 온라인 상태 표시                                                    │
│  4. 읽음 확인                                                           │
│                                                                         │
│  메시지 기능:                                                           │
│  5. 텍스트, 이미지, 파일 전송                                           │
│  6. 메시지 히스토리 동기화                                              │
│  7. 푸시 알림 (오프라인 시)                                             │
│                                                                         │
├─────────────────────────────────────────────────────────────────────────┤
│                     비기능 요구사항                                     │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  - 실시간 전송: < 100ms                                                 │
│  - 메시지 순서 보장                                                     │
│  - 메시지 유실 방지                                                     │
│  - 동시 접속: 수백만                                                    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

2.2 통신 프로토콜

┌─────────────────────────────────────────────────────────────────────────┐
                     프로토콜 선택                                       
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  1. HTTP Polling                                                       
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    Client              Server                                        
                                                                    
      │── GET /messages ─►│                                           
      │◄── [] ────────────│                                           
                                                                    
      │── GET /messages ─►│ (5 )                                  
      │◄── [] ────────────│                                           
                                                                    
      │── GET /messages ─►│ (5 )                                  
      │◄── [msg1] ────────│                                           
                                                                       
    단점: 지연, 불필요한 요청, 서버 부하                               
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  2. Long Polling                                                       
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    Client              Server                                        
                                                                    
      │── GET /messages ─►│                                           
          (대기...)                                                 
                          메시지 도착!                               
      │◄── [msg1] ────────│                                           
      │── GET /messages ─►│ (즉시 재연결)                             
          (대기...)                                                 
                                                                       
    개선: 불필요한 요청 감소                                           
    단점: 연결 오버헤드, 서버 리소스                                   
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  3. WebSocket (권장)                                                   
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    Client              Server                                        
                                                                    
      │── HTTP Upgrade ──►│                                           
      │◄─ 101 Switching ──│                                           
                                                                    
      │═══ WebSocket ═════│ (양방향 연결 유지)                        
                                                                    
      │◄── [msg1] ────────│ (서버 푸시)                               
      │── [msg2] ────────►│ (클라이언트 전송)                         
      │◄── [msg3] ────────│                                           
                                                                       
    장점: 실시간, 양방향, 오버헤드 최소                                
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

2.3 시스템 아키텍처

┌─────────────────────────────────────────────────────────────────────────┐
│                     채팅 시스템 아키텍처                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌───────────────────────────────────────────────────────────────────┐ │
│  │                                                                   │ │
│  │  ┌──────────┐    ┌──────────┐    ┌──────────┐                    │ │
│  │  │ Client A │    │ Client B │    │ Client C │                    │ │
│  │  └────┬─────┘    └────┬─────┘    └────┬─────┘                    │ │
│  │       │ WebSocket     │ WebSocket     │ WebSocket                │ │
│  │       │               │               │                          │ │
│  │       ▼               ▼               ▼                          │ │
│  │  ┌─────────────────────────────────────────────────────────────┐ │ │
│  │  │                   Load Balancer                             │ │ │
│  │  │              (Sticky Sessions by user_id)                   │ │ │
│  │  └──────────────────────┬──────────────────────────────────────┘ │ │
│  │                         │                                        │ │
│  │       ┌─────────────────┼─────────────────┐                     │ │
│  │       ▼                 ▼                 ▼                     │ │
│  │  ┌────────────┐   ┌────────────┐   ┌────────────┐               │ │
│  │  │ Chat Srv 1 │   │ Chat Srv 2 │   │ Chat Srv 3 │               │ │
│  │  │ [A's conn] │   │ [B's conn] │   │ [C's conn] │               │ │
│  │  └─────┬──────┘   └─────┬──────┘   └─────┬──────┘               │ │
│  │        │                │                │                       │ │
│  │        └────────────────┼────────────────┘                       │ │
│  │                         │                                        │ │
│  │                         ▼                                        │ │
│  │  ┌─────────────────────────────────────────────────────────────┐ │ │
│  │  │              Message Broker (Redis Pub/Sub)                 │ │ │
│  │  │                                                             │ │ │
│  │  │  A sends to B:                                              │ │ │
│  │  │  1. Chat Srv 1 → PUBLISH chat:B "msg from A"               │ │ │
│  │  │  2. Chat Srv 2 ← SUBSCRIBE chat:B → deliver to B           │ │ │
│  │  │                                                             │ │ │
│  │  └─────────────────────────────────────────────────────────────┘ │ │
│  │                         │                                        │ │
│  │                         ▼                                        │ │
│  │  ┌─────────────────────────────────────────────────────────────┐ │ │
│  │  │               Message Store (Cassandra)                     │ │ │
│  │  │                                                             │ │ │
│  │  │  Partition Key: conversation_id                            │ │ │
│  │  │  Clustering Key: message_id (time-based)                   │ │ │
│  │  │                                                             │ │ │
│  │  └─────────────────────────────────────────────────────────────┘ │ │
│  │                                                                   │ │
│  └───────────────────────────────────────────────────────────────────┘ │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

2.4 메시지 전송 흐름

┌─────────────────────────────────────────────────────────────────────────┐
                     1:1 메시지 전송                                     
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  User A  User B 메시지 전송:                                          
                                                                         
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    1. A ─── [msg] ───► Chat Server 1                                 
                                                                      
    2.                                                                
             ┌─────────────────────────────┐                          
              Message Service                                       
              - Generate message_id                                 
              - Validate message                                    
             └─────────────┬───────────────┘                          
                                                                      
    3.                                                                
             ┌─────────────┼───────────────┐                          
                                                                   
                                                                   
       ┌──────────┐  ┌──────────┐   ┌──────────────┐                 
        Store       Publish      Check B                       
        to DB       to Kafka     online?                       
       └──────────┘  └──────────┘   └──────┬───────┘                 
                                                                     
    4.                              ┌──────┴──────┐                   
                                                                    
                                 Online        Offline                
                                                                    
                                                                    
                             ┌──────────┐  ┌──────────┐              
                              Pub/Sub      Push                  
                              to B         Queue                 
                             └────┬─────┘  └──────────┘              
                                                                     
    5.                                                               
                       Chat Server 2 ─── [msg] ───► B                 
                                                                       
    6. A ◄─── [ack] ─── Chat Server 1                                 
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

2.5 그룹 채팅

┌─────────────────────────────────────────────────────────────────────────┐
                     그룹 채팅 설계                                      
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  그룹 메시지 전송:                                                      
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    1. Sender ─── [msg] ───► Message Service                          
                                                                      
    2.                                                                
                      Get group members (100)                       
                                                                      
    3.                                                                
                ┌────────────────┼────────────────┐                   
                                                                   
                                                                   
           [Online 60명]   [Online 30명]   [Offline 10명]            
                                                                   
                                                                   
           Pub/Sub          Pub/Sub         Push Queue               
           (batch)          (batch)                                   
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  최적화:                                                                
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    1. 온라인 멤버 우선 전송                                           
    2. 배치 처리 ( 번에 여러 멤버)                                   
    3. 읽음 확인은 샘플링 (전체 X)                                     
    4. 대형 그룹: 클라이언트 Pull 방식 고려                            
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

2.6 온라인 상태 관리

┌─────────────────────────────────────────────────────────────────────────┐
│                     온라인 상태 (Presence)                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  상태 종류:                                                             │
│  - Online: 현재 연결됨                                                  │
│  - Offline: 연결 없음                                                   │
│  - Away: 연결됐지만 비활성                                              │
│  - Last Seen: 마지막 접속 시간                                          │
│                                                                         │
│  구현:                                                                  │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                  │   │
│  │  1. Heartbeat 기반                                               │   │
│  │                                                                  │   │
│  │  Client ─── heartbeat (30초마다) ───► Server                    │   │
│  │                                          │                       │   │
│  │                                          ▼                       │   │
│  │                              Redis: SET presence:user123 "online"│   │
│  │                                     EXPIRE presence:user123 60   │   │
│  │                                                                  │   │
│  │  60초간 heartbeat 없으면 → 키 만료 → Offline                    │   │
│  │                                                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  상태 전파:                                                             │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                  │   │
│  │  User A의 상태 변경 시:                                          │   │
│  │                                                                  │   │
│  │  1. A의 친구 목록 조회                                           │   │
│  │  2. 현재 온라인인 친구만 필터링                                  │   │
│  │  3. 해당 친구들에게 상태 변경 푸시                               │   │
│  │                                                                  │   │
│  │  최적화:                                                         │   │
│  │  - 친구가 많으면 배치 처리                                       │   │
│  │  - 빈번한 변경 방지 (debounce)                                   │   │
│  │  - 활성 대화창의 상대방에게만 우선 전파                          │   │
│  │                                                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

2.7 메시지 저장

┌─────────────────────────────────────────────────────────────────────────┐
                     메시지 DB 스키마 (Cassandra)                        
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  messages 테이블:                                                       
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    CREATE TABLE messages (                                           
        conversation_id UUID,         -- 대화방 ID                    
        message_id TIMEUUID,          -- 시간 기반 UUID               
        sender_id UUID,               -- 발신자                        
        content TEXT,                 -- 메시지 내용                  
        content_type TEXT,            -- text, image, file            
        created_at TIMESTAMP,         -- 생성 시간                    
        PRIMARY KEY (conversation_id, message_id)                     
    ) WITH CLUSTERING ORDER BY (message_id DESC);                     
                                                                       
    -- 최신 메시지부터 정렬                                            
    -- 대화방별 파티셔닝 ( 파티션 주의)                              
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  conversations 테이블:                                                  
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    CREATE TABLE user_conversations (                                 
        user_id UUID,                                                 
        last_message_at TIMESTAMP,    -- 정렬용                       
        conversation_id UUID,                                         
        conversation_type TEXT,       -- dm, group                    
        unread_count INT,             -- 읽지 않은 메시지             
        PRIMARY KEY (user_id, last_message_at, conversation_id)       
    ) WITH CLUSTERING ORDER BY (last_message_at DESC);                
                                                                       
    -- 사용자별 대화 목록 조회                                         
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

3. 알림 시스템

3.1 요구사항 정의

┌─────────────────────────────────────────────────────────────────────────┐
│                     기능 요구사항                                       │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  알림 채널:                                                             │
│  1. iOS 푸시 (APNs)                                                    │
│  2. Android 푸시 (FCM)                                                 │
│  3. SMS                                                                │
│  4. Email                                                              │
│                                                                         │
│  기능:                                                                  │
│  5. 알림 템플릿                                                         │
│  6. 사용자별 알림 설정                                                  │
│  7. 스케줄 알림                                                         │
│  8. 알림 히스토리                                                       │
│                                                                         │
├─────────────────────────────────────────────────────────────────────────┤
│                     비기능 요구사항                                     │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  - 일간 알림: 10B (100억)                                               │
│  - Soft real-time: 수 초 내 전송                                        │
│  - 중복 방지                                                            │
│  - 우선순위 지원                                                        │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

3.2 시스템 아키텍처

┌─────────────────────────────────────────────────────────────────────────┐
│                     알림 시스템 아키텍처                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌───────────────────────────────────────────────────────────────────┐ │
│  │                                                                   │ │
│  │  ┌─────────────────────────────────────────────────────────────┐ │ │
│  │  │                    Event Sources                            │ │ │
│  │  │  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐              │ │ │
│  │  │  │Order   │ │Payment │ │Social  │ │Schedule│              │ │ │
│  │  │  │Service │ │Service │ │Service │ │Service │              │ │ │
│  │  │  └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘              │ │ │
│  │  │      │          │          │          │                    │ │ │
│  │  └──────┼──────────┼──────────┼──────────┼────────────────────┘ │ │
│  │         │          │          │          │                      │ │
│  │         └──────────┴──────────┴──────────┘                      │ │
│  │                         │                                        │ │
│  │                         ▼                                        │ │
│  │  ┌─────────────────────────────────────────────────────────────┐ │ │
│  │  │                 Notification Service                        │ │ │
│  │  │  ┌──────────────────────────────────────────────────────┐  │ │ │
│  │  │  │ 1. Validation                                         │  │ │ │
│  │  │  │ 2. User Preferences Check                             │  │ │ │
│  │  │  │ 3. Rate Limiting                                      │  │ │ │
│  │  │  │ 4. Template Rendering                                 │  │ │ │
│  │  │  │ 5. Priority Assignment                                │  │ │ │
│  │  │  └──────────────────────────────────────────────────────┘  │ │ │
│  │  └──────────────────────┬──────────────────────────────────────┘ │ │
│  │                         │                                        │ │
│  │                         ▼                                        │ │
│  │  ┌─────────────────────────────────────────────────────────────┐ │ │
│  │  │              Message Queues (Priority-based)                │ │ │
│  │  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │ │ │
│  │  │  │ High Queue  │ │ Medium Queue│ │ Low Queue   │           │ │ │
│  │  │  └──────┬──────┘ └──────┬──────┘ └──────┬──────┘           │ │ │
│  │  └─────────┼───────────────┼───────────────┼───────────────────┘ │ │
│  │            │               │               │                     │ │
│  │            ▼               ▼               ▼                     │ │
│  │  ┌─────────────────────────────────────────────────────────────┐ │ │
│  │  │                     Workers                                 │ │ │
│  │  │  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐              │ │ │
│  │  │  │  iOS   │ │Android │ │  SMS   │ │ Email  │              │ │ │
│  │  │  │ Worker │ │ Worker │ │ Worker │ │ Worker │              │ │ │
│  │  │  └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘              │ │ │
│  │  └──────┼──────────┼──────────┼──────────┼─────────────────────┘ │ │
│  │         │          │          │          │                       │ │
│  │         ▼          ▼          ▼          ▼                       │ │
│  │      ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐                     │ │
│  │      │ APNs │  │ FCM  │  │Twilio│  │ SES  │                     │ │
│  │      └──────┘  └──────┘  └──────┘  └──────┘                     │ │
│  │                                                                   │ │
│  └───────────────────────────────────────────────────────────────────┘ │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

3.3 알림 흐름

┌─────────────────────────────────────────────────────────────────────────┐
                     알림 전송 흐름                                      
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    1. 이벤트 수신                                                     
    ─────────────                                                      
    {                                                                  
      "event_type": "order_shipped",                                  
      "user_id": "user123",                                           
      "data": { "order_id": "ORD456", "tracking": "..." }             
    }                                                                  
                                                                       
    2. 사용자 설정 확인                                                
    ─────────────────                                                  
    SELECT * FROM user_notification_settings                          
    WHERE user_id = 'user123';                                        
                                                                       
     push: true, email: true, sms: false                             
                                                                       
    3. 디바이스 토큰 조회                                              
    ─────────────────                                                  
    SELECT device_token, platform                                     
    FROM user_devices WHERE user_id = 'user123';                      
                                                                       
     [{ token: "abc...", platform: "ios" },                          
        { token: "def...", platform: "android" }]                     
                                                                       
    4. 템플릿 렌더링                                                   
    ─────────────────                                                  
    Template: "Your order {order_id} has been shipped!"               
    Result: "Your order ORD456 has been shipped!"                     
                                                                       
    5. 중복 체크                                                       
    ─────────────────                                                  
    Redis SETNX dedup:{event_hash} 1 EX 86400                         
     24시간  동일 알림 방지                                         
                                                                       
    6. 큐에 발행                                                       
    ─────────────────                                                  
    Publish to ios_queue, android_queue, email_queue                  
                                                                       
    7. Worker 처리                                                     
    ─────────────────                                                  
    iOS Worker  APNs API  Apple 서버                                
    Android Worker  FCM API  Google 서버                            
    Email Worker  SES API  이메일 전송                              
                                                                       
    8. 결과 저장                                                       
    ─────────────────                                                  
    INSERT INTO notification_logs (...)                               
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

3.4 중복 방지

┌─────────────────────────────────────────────────────────────────────────┐
                     중복 알림 방지                                      
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  문제:                                                                  
  - 이벤트 중복 발행                                                     
  - Worker 재시도로 인한 중복                                            
  - 분산 환경에서의 Race Condition                                       
                                                                         
  해결책:                                                                
  ┌─────────────────────────────────────────────────────────────────┐   
                                                                       
    1. 이벤트 레벨 중복 제거                                           
    ─────────────────────────                                          
    event_key = hash(event_type + user_id + key_data)                 
                                                                       
    if not redis.setnx(f"dedup:{event_key}", 1, ex=86400):           
        return  # 이미 처리됨                                         
                                                                       
    2. 전송 레벨 중복 제거                                             
    ─────────────────────────                                          
    notification_id = generate_unique_id()                            
                                                                       
     채널별 전송  체크:                                            
    if not redis.setnx(f"sent:{notification_id}:{channel}", 1):      
        return  # 이미 전송됨                                         
                                                                       
    3. 빈도 제한                                                       
    ─────────────────────────                                          
    max_per_hour = 10                                                 
                                                                       
    current = redis.incr(f"rate:{user_id}:{hour}")                   
    if current > max_per_hour:                                        
        # 제한 초과  나중에 발송 또는 무시                            
        enqueue_for_later()                                           
                                                                       
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

3.5 우선순위 처리

┌─────────────────────────────────────────────────────────────────────────┐
│                     알림 우선순위                                       │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  우선순위 분류:                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                  │   │
│  │  HIGH (즉시):                                                    │   │
│  │  - 보안 알림 (비정상 로그인)                                     │   │
│  │  - 2FA 인증 코드                                                 │   │
│  │  - 긴급 시스템 알림                                              │   │
│  │                                                                  │   │
│  │  MEDIUM (수 초 내):                                              │   │
│  │  - 채팅 메시지                                                   │   │
│  │  - 주문 상태 변경                                                │   │
│  │  - 결제 알림                                                     │   │
│  │                                                                  │   │
│  │  LOW (수 분 ~ 시간):                                             │   │
│  │  - 마케팅 알림                                                   │   │
│  │  - 추천 알림                                                     │   │
│  │  - 요약 알림                                                     │   │
│  │                                                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  구현:                                                                  │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                  │   │
│  │  우선순위별 큐:                                                  │   │
│  │                                                                  │   │
│  │  ┌─────────────────┐                                            │   │
│  │  │ HIGH Queue      │ ──► 전용 Worker 10대                       │   │
│  │  └─────────────────┘     (항상 즉시 처리)                       │   │
│  │                                                                  │   │
│  │  ┌─────────────────┐                                            │   │
│  │  │ MEDIUM Queue    │ ──► 공유 Worker 50대                       │   │
│  │  └─────────────────┘     (HIGH 비어있을 때)                     │   │
│  │                                                                  │   │
│  │  ┌─────────────────┐                                            │   │
│  │  │ LOW Queue       │ ──► 공유 Worker 50대                       │   │
│  │  └─────────────────┘     (HIGH, MEDIUM 비어있을 때)             │   │
│  │                                                                  │   │
│  │  Worker 처리 순서:                                               │   │
│  │  while True:                                                    │   │
│  │      msg = high_queue.pop() or                                  │   │
│  │            medium_queue.pop() or                                │   │
│  │            low_queue.pop()                                      │   │
│  │      process(msg)                                               │   │
│  │                                                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

3.6 알림 설정 스키마

┌─────────────────────────────────────────────────────────────────────────┐
                     DB 스키마                                           
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  user_notification_settings:                                           
  ┌─────────────────────────────────────────────────────────────────┐   
    user_id          UUID          PK                                 
    channel          VARCHAR       PK (push/email/sms)                
    enabled          BOOLEAN                                          
    quiet_hours      JSONB         {"start": "22:00", "end": "08:00"}  
    frequency        VARCHAR       immediate/daily/weekly             
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  notification_type_settings:                                           
  ┌─────────────────────────────────────────────────────────────────┐   
    user_id          UUID          PK                                 
    notification_type VARCHAR      PK (order/social/marketing)        
    push_enabled     BOOLEAN                                          
    email_enabled    BOOLEAN                                          
    sms_enabled      BOOLEAN                                          
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  user_devices:                                                         
  ┌─────────────────────────────────────────────────────────────────┐   
    device_id        UUID          PK                                 
    user_id          UUID          FK                                 
    platform         VARCHAR       ios/android                        
    device_token     VARCHAR       푸시 토큰                           
    last_active      TIMESTAMP     마지막 활성                         
    app_version      VARCHAR        버전                             
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
  notification_logs:                                                    
  ┌─────────────────────────────────────────────────────────────────┐   
    id               UUID          PK                                 
    user_id          UUID                                             
    type             VARCHAR       알림 유형                           
    channel          VARCHAR       전송 채널                           
    content          JSONB         알림 내용                           
    status           VARCHAR       sent/failed/pending                
    sent_at          TIMESTAMP     전송 시간                           
    read_at          TIMESTAMP     읽음 시간                           
  └─────────────────────────────────────────────────────────────────┘   
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

4. 연습 문제

연습 1: 뉴스 피드 확장

다음 기능을 추가하는 설계를 하세요: - 광고 삽입 (5번째 포스트마다) - 트렌딩 포스트 추천 - "관심 없음" 피드백 반영

연습 2: 채팅 시스템 확장

다음 요구사항을 만족하는 설계를 하세요: - End-to-End 암호화 - 메시지 편집/삭제 (24시간 내) - 화상/음성 통화 시그널링

연습 3: 알림 시스템 최적화

다음 상황을 처리하는 설계를 하세요: - 글로벌 서비스: 다국어 알림 - 알림 배칭: 유사 알림 묶음 - A/B 테스트: 알림 문구 최적화


마무리

이 시리즈를 통해 시스템 설계의 핵심 개념과 패턴을 학습했습니다. 실제 면접이나 프로젝트에서는 요구사항을 명확히 하고, 트레이드오프를 고려하며, 확장 가능한 설계를 하는 것이 중요합니다.

다음 단계로는: - 실제 오픈소스 시스템 코드 분석 - 기업 기술 블로그 연구 (Netflix, Uber, Twitter 등) - 모의 시스템 설계 면접 연습


참고 자료

  • "System Design Interview" - Alex Xu Vol.1 & Vol.2
  • "Designing Data-Intensive Applications" - Martin Kleppmann
  • Twitter Timeline Architecture
  • Facebook News Feed Architecture
  • WhatsApp Architecture at Scale
  • Discord How Discord Stores Billions of Messages
  • Airbnb Notification System
to navigate between lessons