|
| 1 | +--- |
| 2 | +title: "[Book - 가상 면접 사례로 배우는 대규모 시스템 설계 기초] 10. 알림 시스템 설계" |
| 3 | +date: 2025-10-13 23:30:00 +0900 |
| 4 | +categories: [Book - 가상 면접 사례로 배우는 대규모 시스템 설계 기초] |
| 5 | +tags: [book, System Design Interview, message queue] |
| 6 | +--- |
| 7 | + |
| 8 | +알림 시스템은 단순 히 모바일 푸시 알림에 한정되지 않는다. |
| 9 | +알림 시스템은 모바일 푸시 알림, SMS 메시지, 이메일의 세 가지로 분류할 수 있다. |
| 10 | + |
| 11 | +# 1단계. 문제 이해 및 설계 범위 확정 |
| 12 | + |
| 13 | +적절한 질문을 통해 요구 사항이 무엇인지 지원자 스스로 알아내야 한다. |
| 14 | +``` |
| 15 | +질문1: 이 시스템은 어떤 종류의 알림을 지원해야 하나요? |
| 16 | +> 푸시 알림, SMS 메시지, 이메일 |
| 17 | +
|
| 18 | +질문2: 실시간(real-time) 시스템이어야 하나요? |
| 19 | +> 연성 실시간(soft real-time) 시스템이라고 가정 |
| 20 | +> 가능한 빨리, 부하 시 약간의 지연은 무방 |
| 21 | +
|
| 22 | +질문3: 어떤 종류의 단말을 지원해야 하나요? |
| 23 | +> iOS, android, 랩톱/데스크톱 |
| 24 | +
|
| 25 | +질문4: 사용자에게 보낼 알림은 누가 만들 수 있나요? |
| 26 | +> 클라이언트 애플리케이션 프로그램, 서버 측에서 스케줄링 |
| 27 | +
|
| 28 | +질문5: 사용자가 알림을 받지 않도록(opt-out) 설정할 수도 있어야 하나요? |
| 29 | +> 네 |
| 30 | +
|
| 31 | +질문6: 하루에 몇 건의 알림을 보낼 수 있어야 하나요? |
| 32 | +> 천만 건의 모바일 푸시 알림, 백만 건의 SMS 메시지, 5백만 건의 이메일 |
| 33 | +``` |
| 34 | + |
| 35 | +# 2단계. 개략적 설계안 제시 및 동의 구하기 |
| 36 | + |
| 37 | +## 알림 유형별 지원 방안 |
| 38 | + |
| 39 | +### 1. iOS 푸시 알림 |
| 40 | +iOS에서 푸시 알림을 보내기 위해서는 세 가지 컴포넌트가 필요하다. |
| 41 | + |
| 42 | +```mermaid |
| 43 | +flowchart LR |
| 44 | + provider((알림 제공자)) |
| 45 | + apns[[APNS]] |
| 46 | + ios["iOS 단말"] |
| 47 | +
|
| 48 | + provider --> apns --> ios |
| 49 | +``` |
| 50 | +> APNS: 애플이 제공하는 원격 서비스 |
| 51 | +
|
| 52 | +### 2. 안드로이드 푸시 알림 |
| 53 | +```mermaid |
| 54 | +flowchart LR |
| 55 | + provider((알림 제공자)) |
| 56 | + fcm[[FCM]] |
| 57 | + android["안드로이드 단말"] |
| 58 | +
|
| 59 | + provider --> fcm --> android |
| 60 | +``` |
| 61 | +> FCM(Firebase Cloud Messaging) |
| 62 | +
|
| 63 | +### 3. SMS 메시지 |
| 64 | +```mermaid |
| 65 | +flowchart LR |
| 66 | + provider((알림 제공자)) |
| 67 | + service[[SMS 서비스]] |
| 68 | + sms["SMS 수신 단말"] |
| 69 | +
|
| 70 | + provider --> service --> sms |
| 71 | +``` |
| 72 | +> 보통 상용 서비스로 Twilio, Nexmo 같은 제3사업자의 서비스를 많이 이용 |
| 73 | +
|
| 74 | +### 4. 이메일 |
| 75 | +```mermaid |
| 76 | +flowchart LR |
| 77 | + provider((알림 제공자)) |
| 78 | + service[[이메일 서비스]] |
| 79 | + email["이메일 수신 단말"] |
| 80 | +
|
| 81 | + provider --> service --> email |
| 82 | +``` |
| 83 | +> 보통 상용 서비스로 Sendgrid, Mailchimp를 사용 |
| 84 | +
|
| 85 | +## 연락처 정보 수집 절차 |
| 86 | +알림을 보내려면 모바일 단말 토큰, 전화번호, 이메일 주소 등의 정보가 필요하다. |
| 87 | +```mermaid |
| 88 | +flowchart LR |
| 89 | + user[사용자 단말] |
| 90 | + lb[로드밸런서] |
| 91 | + api[API 서버들] |
| 92 | + db[(DB)] |
| 93 | +
|
| 94 | + user -- 앱 설치 또는 계정 등록 --> lb |
| 95 | + lb --> api |
| 96 | + api -- 연락처 정보 저장 --> db |
| 97 | +``` |
| 98 | + |
| 99 | +## 알림 전송 및 수신 절차 |
| 100 | +개략적 설계안부터 점진적으로 최적화해 보자. |
| 101 | + |
| 102 | +### 개략적 설계안 (초안) |
| 103 | +* 1부터 N까지의 서비스 |
| 104 | + * 마이크로서비스 or 크론잡 or 분산 시스템 컴포넌트 |
| 105 | + * 예시 |
| 106 | + * 사용자에게 납기일을 알리고자 하는 billing service |
| 107 | + * 배송 알림을 보내려는 쇼핑몰 서비스 |
| 108 | +* 알림 시스템 |
| 109 | + * 알림 전송/수신 처리의 핵심 |
| 110 | + * 1개 서버만 사용하는 시스템 (가정) |
| 111 | + * 서비스 1~N에 알림 전송을 위한 API 제공 필요 |
| 112 | + * 제3자 서비스에 전달한 알림 payload 필요 |
| 113 | +* 제3자 서비스 |
| 114 | + * 사용자에게 알림을 실제로 전달하는 역할 |
| 115 | + * 확장성 유의 |
| 116 | +* iOS, android, SMS |
| 117 | + * 사용자는 자기 단말에서 알림을 수신 |
| 118 | + |
| 119 | +```mermaid |
| 120 | +flowchart LR |
| 121 | + %% 서비스들 |
| 122 | + subgraph Services[서비스들] |
| 123 | + s1[서비스 1] |
| 124 | + s2[서비스 2] |
| 125 | + s3["..."] |
| 126 | + sN[서비스 N] |
| 127 | + end |
| 128 | +
|
| 129 | + %% 알림 시스템 |
| 130 | + notify[알림 시스템] |
| 131 | +
|
| 132 | + %% 제3자 제공 서비스 |
| 133 | + subgraph ThirdParty[제3자 제공 서비스] |
| 134 | + apns[[APNS]] |
| 135 | + fcm[[FCM]] |
| 136 | + sms[[SMS 서비스]] |
| 137 | + email[[이메일 서비스]] |
| 138 | + end |
| 139 | +
|
| 140 | + %% 단말들 |
| 141 | + ios[iOS 단말] |
| 142 | + android[Android 단말] |
| 143 | + sms_client[SMS 단말] |
| 144 | + email_client[이메일 수신 단말] |
| 145 | +
|
| 146 | + %% 서비스 -> 알림 시스템 |
| 147 | + s1 --> notify |
| 148 | + s2 --> notify |
| 149 | + s3 --> notify |
| 150 | + sN --> notify |
| 151 | +
|
| 152 | + %% 알림 시스템 -> 제3자 서비스 |
| 153 | + notify --> apns |
| 154 | + notify --> fcm |
| 155 | + notify --> sms |
| 156 | + notify --> email |
| 157 | +
|
| 158 | + %% 제3자 서비스 -> 각 단말 |
| 159 | + apns --> ios |
| 160 | + fcm --> android |
| 161 | + sms --> sms_client |
| 162 | + email --> email_client |
| 163 | +``` |
| 164 | + |
| 165 | +이 설계에는 아래와 같은 문제가 있다. |
| 166 | +* SPOF(Single Point of Failure) |
| 167 | + * 알림 서비스에 서버가 하나밖에 없어서 장애가 생기면 전체 서비스의 장애로 이어진다. |
| 168 | +* 규모 확장성 |
| 169 | + * 한 대 서비스로 푸시 알림에 관계된 모든 것을 처리하므로, |
| 170 | + * DB나 cache 등 중요 컴포넌트의 규모를 개별적으로 늘릴 방법이 없다. |
| 171 | +* 성능 병목 |
| 172 | + * 사용자 트래픽이 많이 몰리는 시간에는 시스템이 과부하 상태에 빠질 수 있다. |
| 173 | + |
| 174 | +### 개략적 설계안 (개선된 버전) |
| 175 | +아래와 같은 방향으로 개선해 보자. |
| 176 | +* DB와 cache를 알림 시스템의 주 서버에서 분리 |
| 177 | +* 알림 서버를 증설하고 자동으로 수평적 규모 확장 보장 |
| 178 | +* 메시지 큐를 이용해 시스템 컴포넌트 사이의 강한 결합 제거 |
| 179 | +> 알림 서버는 아래와 같은 기능을 제공한다. |
| 180 | +> |
| 181 | +> * 알림 전송 API |
| 182 | +> * 알림 검증 |
| 183 | +> * 데이터베이스 또는 캐시 질의 |
| 184 | +> * 알림 전송 |
| 185 | +
|
| 186 | + |
| 187 | + |
| 188 | +1. API를 호출하여 알림 서버로 알림을 보낸다. |
| 189 | +2. 알림 섬버는 사용자 정보, 단말 토큰, 알림 설정 같은 메타데이터를 캐시나 데이터베이스에서 가져온다. |
| 190 | +3. 알림 서버는 전송할 알림에 맞는 이벤트를 만들어서 해당 이벤트를 위한 큐에 넣는다. |
| 191 | +4. 작업 서버는 메시지 큐에서 알림 이벤트를 꺼낸다. |
| 192 | +5. 작업 서버는 알림을 제3자 서비스로 보낸다. |
| 193 | +6. 제3자 서비스는 사용자 단말로 알림을 전송한다. |
| 194 | + |
| 195 | +# 3단계. 상세 설계 |
| 196 | +애래 내용을 좀 더 자세히 알아보자. |
| 197 | +* 안정성(reliability) |
| 198 | +* 추가로 필요한 컴포넌트 및 고려사항 |
| 199 | + * 알림 템플릿 |
| 200 | + * 알림 설정 |
| 201 | + * 전송률 제한 |
| 202 | + * 재시도 메커니즘 |
| 203 | + * 보안 |
| 204 | + * 큐에 보관된 알림에 대한 모니터링과 이벤트 추적 |
| 205 | +* 개선된 설계안 |
| 206 | + |
| 207 | +## 안정성 |
| 208 | +**분산 환경**에서 운영될 알림 시스템을 설계할 때는 안정성을 확보하기 위한 사항 몇 가지를 반드시 고려해야 한다. |
| 209 | + |
| 210 | +### 데이터 손실 방지 |
| 211 | +데이터 손실 방지 요구사항을 만족하려면 알림 데이터를 DB에 보관하고 재시도 매커니즘을 구현해야 한다. |
| 212 | +```mermaid |
| 213 | +flowchart LR |
| 214 | + %% 노드 정의 |
| 215 | + iosQ["iOS 푸시 알림 큐"] |
| 216 | + worker["작업 서버"] |
| 217 | + apns["APNS"] |
| 218 | + logDB[("알림 로그 데이터베이스")] |
| 219 | +
|
| 220 | + %% 흐름 |
| 221 | + iosQ --> worker --> apns |
| 222 | + worker --> logDB |
| 223 | +
|
| 224 | + %% 스타일 |
| 225 | + classDef dashed stroke-dasharray: 5 5, stroke:#999; |
| 226 | + class worker dashed; |
| 227 | + class logDB dashed; |
| 228 | +``` |
| 229 | + |
| 230 | +### 알림 중복 전송 방지 |
| 231 | +같은 알림이 여러 번 반복되는 것을 완전히 막는 것은 가능하지 않지만, 그 빈도를 줄이기 위해 중복을 탐지하는 메커니즘을 도입한다. |
| 232 | +* 보내야 할 알림이 도착하면 그 이벤트 ID를 검사하여 이전에 본 적이 있는 이벤트인지 상핀다. |
| 233 | +* 중복된 이벤트라면 버리고, 아니면 알림을 발송한다. |
| 234 | + |
| 235 | +## 추가로 필요한 컴포넌트 및 고려사항 |
| 236 | +### 알림 템플릿 |
| 237 | +``` |
| 238 | +지금 [item_name]을 주문 또는 예약하세요! |
| 239 | +``` |
| 240 | + |
| 241 | +### 알림 설정 |
| 242 | +사용자가 알림 설정을 상세히 조정할 수 있도록 알림 설정 테이블 사용 |
| 243 | + |
| 244 | +### 전송률 제한 |
| 245 | +한 사용자가 받을 수 있는 알림의 빈도를 제한 |
| 246 | + |
| 247 | +### 재시도 방법 |
| 248 | +제3자 서비스가 알림 전송에 실패하면, 해당 알림을 재시도 전용 큐에 넣는다. |
| 249 | + |
| 250 | +같은 문제가 계속해서 발생하면 개발자에게 통지한다. |
| 251 | + |
| 252 | +### 푸시 알림과 보안 |
| 253 | +App의 경우, 알림 전송 API는 appKey와 appSecret을 사용하여 보안을 유지한다. |
| 254 | + |
| 255 | +### 큐 모니터링 |
| 256 | +큐에 쌓인 알림의 metric 수가 너무 크면 작업 서버들이 이벤트를 빠르게 처리하고 있지 못하다는 뜻이므로, 작업 서버를 증설하는 게 바람직할 것이다. |
| 257 | + |
| 258 | +### 이벤트 추적 |
| 259 | +알림 확인율, 클릭율, 실제 앱 사용으로 이어지는 비율 같은 메트릭은 사용자를 이해하는데 중요하다. |
| 260 | + |
| 261 | +보통 알림 시스템을 만들면 데이터 분석 서비스와도 통합해야만 한다. |
| 262 | + |
| 263 | + |
| 264 | + |
| 265 | +## 수정된 설계안 |
| 266 | + |
| 267 | + |
| 268 | +# 4단계. 마무리 |
| 269 | +알림은 중요 정보를 계속 알려준다는 점에서 필요불가결한 기능이다. |
| 270 | + |
| 271 | +시스템 컴포넌트 사이의 결합도를 낮추기 위해 메시지 큐를 적극적으로 사용하였다. |
| 272 | + |
| 273 | +각 컴포넌트의 구현 방법과 최적화 기법에 대해서 아래 주제에 집중하였다. |
| 274 | +* 안정성(reliability) |
| 275 | + * 메시지 전송 실패율을 낮추기 위해 안정적인 재시도 메커니즘 도입 |
| 276 | +* 보안(security) |
| 277 | + * 인증된 클라이언트만이 알림을 보낼 수 있도록 appKey, appSecret 등의 메커니즘 이용 |
| 278 | +* 이벤트 추적 및 모니터링 |
| 279 | + * 알림이 만들어진 후 성공적으로 전송되기까지의 과정을 추적하고 시스템 상태를 모니터링하기 위해 |
| 280 | + * 알림 전송의 각 단계마다 이벤트를 추적하고 모니터링할 수 있는 시스템을 통합 |
| 281 | +* 사용자 설정 |
| 282 | + * 사용자가 알림 수신 설정을 조정할 수 있도록 설계 |
| 283 | +* 전송률 제한 |
| 284 | + * 사용장에게 알림을 보내는 빈도를 제한 |
0 commit comments