이 프로젝트는 "대규모 시스템 설계 기초" 책을 읽고 학습한 내용을 바탕으로 설계했습니다.
알림 시스템의 요구사항과 아키텍처를 더 잘 이해하기 위해 이를 실습 구현해본 것입니다.
-
SPOF (Single Point of Failure)
알림 서비스에 장애가 발생하면 전체 서비스 장애로 이어질 수 있음. -
규모 확장성 문제
단일 서버에서 모든 기능을 처리하면, DB나 캐시 등의 중요 컴포넌트를 독립적으로 확장할 수 없음. -
성능 병목
알림 처리 과정(HTML 페이지 생성, 제3자 서비스 응답 대기 등)에서 많은 자원이 소모될 수 있음.
- 알림 서버와 시스템 컴포넌트 간 강한 결합을 끊기 위해
- 대량 알림 전송 시 서버 부하를 버퍼링하기 위해
- 빠른 응답 속도 (메모리 기반 조회)
- 데이터베이스 부하 감소
- 대량 트래픽 대응 시 시스템 안정성 향상
특히 이메일 알림을 위해 HTML 포맷의 템플릿을 Redis에 저장하고, 빠르게 로딩하여 사용합니다.
- H2 DB에 미리 여러 이메일 템플릿 (welcome, default 등) 등록.
- 이메일 보낼 때마다 템플릿 키와 변수 맵을 받아서 Redis에 템플릿을 캐싱해서 빠르게 조회.
- 실제 이메일 본문은 템플릿에 변수를 치환해서 만들어서 발송하도록 변경.
- 템플릿 예시
{{name}} Reset your password <a href=\"{{link}}\">here</a>.
컴포넌트 | 역할 |
---|---|
알림 전송 API | 사내 서비스 또는 인증된 클라이언트만 사용 가능, 스팸 방지 |
알림 검증 | 이메일 주소, 전화번호 등 기본 검증 수행 |
데이터베이스 또는 캐시 질의 | 알림에 포함할 데이터 조회 |
알림 전송 | 알림 데이터를 메시지 큐에 넣음 |
캐시 | 사용자 정보, 단말 정보, 알림 템플릿 캐시 |
데이터베이스 | 사용자, 알림, 설정 데이터 저장 |
메시지 큐 | 시스템 간 의존성 제거 및 버퍼링 |
작업 서버(Worker) | 큐에서 알림 이벤트를 꺼내 제3자 서비스로 전송 |
제3자 서비스 | APNS, FCM, SMS 연동 |
단말 | Android, iOS 사용자 기기 |
- 클라이언트가 API를 호출하여 알림 서버에 요청
- 알림 서버는 사용자 정보, 단말 토큰, 알림 설정 정보를 캐시 또는 DB에서 조회
- 알림 서버는 전송할 알림 이벤트를 생성하고, 해당 채널별 큐에 넣음
- 작업 서버가 메시지 큐에서 알림 이벤트를 꺼냄
- 작업 서버가 제3자 서비스(APNS, FCM 등)로 알림 전송
- 제3자 서비스가 사용자 단말에 최종 알림 전송
- api : 알림 요청을 수신하고, 알림 이벤트를 큐로 발행하는 API 서버
- core : 공통 도메인(Entity, Repository, DTO, Kafka 메시지 등)
- consumer : 큐를 구독하고 제3자 서비스로 알림을 전송하는 작업 서버
- Spring Boot 3.4.5
- Spring Data JPA
- Spring Data Redis
- Spring for Apache Kafka
- H2 Database
- Docker Compose
- Kafka: confluentinc/cp-kafka
- Zookeeper: confluentinc/cp-zookeeper
- Redis: redis:7.4.0
docker-compose up -d
docker-compose down
docker exec -it <kafka-container-id> /bin/bash
kafka-topics --list --bootstrap-server localhost:9092
- 다양한 상황별 이메일 템플릿 지원
- Redis 캐싱 최적화
- 제3자 서비스 응답 실패 시 재전송 처리
- 동작 흐름:
notification-event
토픽에서 첫 소비 실패- Spring이 자동으로
notification-event-retry
토픽으로 전송 retry-consumer
들이notification-event-retry
를 계속 소비하며 재시도- 재시도에서도 실패할 경우 →
notification-event-dlq
로 최종 이동
- 특정 사용자 또는 IP 기준으로 초당/분당 전송 요청 제한
- 구현 방식:
NotificationEventConsumer
에 초당 5회 제한 로직 적용- Redis의
INCR
,EXPIRE
활용하여 사용자별 요청 횟수 카운팅 - 전송 횟수 초과 시
RuntimeException
발생으로 처리 중단