20 KiB
Notification Service - 백엔드 개발 결과서
1. 개요
1.1 서비스 설명
- 서비스명: Notification Service
- 목적: 회의 초대 및 Todo 할당 알림 발송 서비스
- 아키텍처 패턴: Layered Architecture
- 주요 기능:
- Azure Event Hubs를 통한 이벤트 기반 알림 발송
- 이메일 템플릿 기반 알림 생성
- 사용자별 알림 설정 관리
- 재시도 메커니즘 (Exponential Backoff)
- 중복 발송 방지 (Idempotency)
1.2 개발 기간
- 시작일: 2025-10-23
- 완료일: 2025-10-23
- 개발자: 준호 (Backend Developer)
2. 기술 스택
2.1 프레임워크 및 라이브러리
- Spring Boot: 3.3.0
- Java: 21
- Spring Data JPA: 3.3.0
- Spring Security: 6.3.0
- Spring Mail: 3.3.0
- Spring Retry: 2.0.6
- Thymeleaf: 3.1.2
- Azure Event Hubs: 5.18.4
- Azure Storage Blob: 12.26.1
- PostgreSQL Driver: 42.7.3
- SpringDoc OpenAPI: 2.5.0
- Lombok: 1.18.32
2.2 데이터베이스
- DBMS: PostgreSQL 16.4
- Host: 4.230.159.143:5432
- Database: notificationdb
- User: hgzerouser
2.3 메시지 큐
- Azure Event Hubs: 이벤트 기반 알림 처리
- Consumer Group: notification-service
- Checkpoint Store: Azure Blob Storage
2.4 이메일 발송
- SMTP: Gmail SMTP 또는 사용자 정의 SMTP
- Port: 587 (TLS)
- Template Engine: Thymeleaf
3. 구현 내용
3.1 패키지 구조
notification/
└── src/main/java/com/unicorn/hgzero/notification/
├── NotificationApplication.java # Spring Boot 메인 클래스
│
├── domain/ # Domain Layer
│ ├── Notification.java # 알림 Entity
│ ├── NotificationRecipient.java # 수신자 Entity
│ └── NotificationSetting.java # 알림 설정 Entity
│
├── repository/ # Data Access Layer
│ ├── NotificationRepository.java
│ ├── NotificationRecipientRepository.java
│ └── NotificationSettingRepository.java
│
├── service/ # Business Logic Layer
│ ├── NotificationService.java # 알림 비즈니스 로직
│ ├── EmailTemplateService.java # 템플릿 렌더링
│ └── EmailClient.java # 이메일 발송
│
├── controller/ # Presentation Layer
│ ├── NotificationController.java # 알림 조회 API
│ └── NotificationSettingsController.java # 설정 API
│
├── event/ # Event Handler Layer
│ ├── EventHandler.java # Event Hub 핸들러
│ ├── event/
│ │ ├── MeetingCreatedEvent.java # 회의 생성 이벤트 DTO
│ │ └── TodoAssignedEvent.java # Todo 할당 이벤트 DTO
│ └── processor/
│ └── EventProcessorService.java # Processor 라이프사이클
│
├── dto/ # Data Transfer Objects
│ ├── request/
│ │ └── UpdateSettingsRequest.java
│ └── response/
│ ├── NotificationResponse.java
│ ├── NotificationListResponse.java
│ └── SettingsResponse.java
│
├── config/ # Configuration Layer
│ ├── EventHubConfig.java # Event Hub 설정
│ ├── BlobStorageConfig.java # Blob Storage 설정
│ ├── RetryConfig.java # 재시도 정책
│ ├── SecurityConfig.java # Spring Security
│ ├── SwaggerConfig.java # Swagger 설정
│ └── EmailConfig.java # Email 설정
│
└── exception/ # Exception Handling
└── (향후 추가 예정)
3.2 구현된 주요 클래스
3.2.1 Domain Layer (Entity)
Notification.java (notification/src/main/java/com/unicorn/hgzero/notification/domain/Notification.java:1)
- 알림 기본 정보 관리
- 수신자 목록 (OneToMany)
- 중복 방지를 위한 eventId (unique)
- 상태 추적 (PENDING, PROCESSING, SENT, FAILED, PARTIAL)
NotificationRecipient.java (notification/src/main/java/com/unicorn/hgzero/notification/domain/NotificationRecipient.java:1)
- 수신자별 발송 상태 추적
- 재시도 로직 지원 (retryCount, nextRetryAt)
- 발송 성공/실패 처리 메서드
NotificationSetting.java (notification/src/main/java/com/unicorn/hgzero/notification/domain/NotificationSetting.java:1)
- 사용자별 알림 설정
- 채널 활성화 (email, SMS, push)
- 알림 유형별 활성화
- 방해 금지 시간대 (DND)
3.2.2 Repository Layer
NotificationRepository.java (notification/src/main/java/com/unicorn/hgzero/notification/repository/NotificationRepository.java:1)
- JpaRepository 확장
- 커스텀 쿼리 메서드:
- findByEventId() - 중복 체크
- findByReferenceIdAndReferenceType() - 참조 조회
- findByStatusIn() - 배치 처리
- countByStatusAndCreatedAtBetween() - 통계
NotificationRecipientRepository.java (notification/src/main/java/com/unicorn/hgzero/notification/repository/NotificationRecipientRepository.java:1)
- 수신자 관리
- 커스텀 쿼리 메서드:
- findByNotificationId() - 알림별 수신자
- findByStatusAndNextRetryAtBefore() - 재시도 대상
- findByRecipientEmail() - 사용자 히스토리
NotificationSettingRepository.java (notification/src/main/java/com/unicorn/hgzero/notification/repository/NotificationSettingRepository.java:1)
- 알림 설정 관리
- 커스텀 쿼리 메서드:
- findByUserId() - 사용자 설정 조회
- findByEmailEnabledAndInvitationEnabled() - 발송 대상 필터링
3.2.3 Service Layer
NotificationService.java (notification/src/main/java/com/unicorn/hgzero/notification/service/NotificationService.java:1)
- 핵심 비즈니스 로직
- 주요 메서드:
- sendMeetingInvitation() - 회의 초대 알림 발송
- sendTodoAssignment() - Todo 할당 알림 발송
- canSendNotification() - 알림 발송 가능 여부 확인
- 기능:
- 이벤트 중복 체크 (eventId)
- 사용자 알림 설정 확인
- 템플릿 렌더링 및 이메일 발송
- 수신자별 상태 추적
EmailTemplateService.java (notification/src/main/java/com/unicorn/hgzero/notification/service/EmailTemplateService.java:1)
- Thymeleaf 템플릿 렌더링
- 주요 메서드:
- renderMeetingInvitation() - 회의 초대 템플릿
- renderTodoAssigned() - Todo 할당 템플릿
- renderReminder() - 리마인더 템플릿
EmailClient.java (notification/src/main/java/com/unicorn/hgzero/notification/service/EmailClient.java:1)
- SMTP 이메일 발송
- 재시도 메커니즘 (@Retryable)
- Exponential Backoff (5분 → 15분 → 30분)
- HTML 및 텍스트 이메일 지원
3.2.4 Controller Layer
NotificationController.java (notification/src/main/java/com/unicorn/hgzero/notification/controller/NotificationController.java:1)
- 알림 조회 API
- 엔드포인트:
- GET /notifications - 알림 목록 조회
- GET /notifications/{id} - 특정 알림 조회
- GET /notifications/statistics - 통계 조회
NotificationSettingsController.java (notification/src/main/java/com/unicorn/hgzero/notification/controller/NotificationSettingsController.java:1)
- 알림 설정 API
- 엔드포인트:
- GET /notifications/settings - 설정 조회
- PUT /notifications/settings - 설정 업데이트
3.2.5 Event Handler Layer
EventHandler.java (notification/src/main/java/com/unicorn/hgzero/notification/event/EventHandler.java:1)
- Consumer 구현
- 이벤트 수신 및 처리
- 토픽별 라우팅 (meeting, todo)
- 이벤트 유형별 처리 (MeetingCreated, TodoAssigned)
- Checkpoint 업데이트
EventProcessorService.java (notification/src/main/java/com/unicorn/hgzero/notification/event/processor/EventProcessorService.java:1)
- EventProcessorClient 라이프사이클 관리
- @PostConstruct - 시작
- @PreDestroy - 종료
- @Retryable - 재시도 지원
3.2.6 Config Layer
EventHubConfig.java (notification/src/main/java/com/unicorn/hgzero/notification/config/EventHubConfig.java:1)
- EventProcessorClient Bean 생성
- BlobCheckpointStore 설정
- 오류 핸들러 등록
BlobStorageConfig.java (notification/src/main/java/com/unicorn/hgzero/notification/config/BlobStorageConfig.java:1)
- Blob Container Async Client
- Checkpoint 저장소 연결
RetryConfig.java (notification/src/main/java/com/unicorn/hgzero/notification/config/RetryConfig.java:1)
- @EnableRetry
- RetryTemplate 구성
- Exponential Backoff Policy
SecurityConfig.java (notification/src/main/java/com/unicorn/hgzero/notification/config/SecurityConfig.java:1)
- Spring Security 설정
- CORS 설정
- Stateless 세션 관리
SwaggerConfig.java (notification/src/main/java/com/unicorn/hgzero/notification/config/SwaggerConfig.java:1)
- OpenAPI 3.0 설정
- Swagger UI 구성
EmailConfig.java (notification/src/main/java/com/unicorn/hgzero/notification/config/EmailConfig.java:1)
- JavaMailSender Bean
- SMTP 설정
4. 데이터베이스 설계
4.1 테이블 구조
notifications (알림 테이블)
CREATE TABLE notifications (
notification_id VARCHAR(36) PRIMARY KEY,
event_id VARCHAR(100) UNIQUE NOT NULL,
reference_id VARCHAR(36) NOT NULL,
reference_type VARCHAR(20) NOT NULL,
notification_type VARCHAR(30) NOT NULL,
title VARCHAR(500) NOT NULL,
message TEXT,
status VARCHAR(20) NOT NULL,
channel VARCHAR(20) NOT NULL,
sent_count INTEGER NOT NULL DEFAULT 0,
failed_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL,
sent_at TIMESTAMP
);
CREATE INDEX idx_notification_reference ON notifications(reference_id, reference_type);
CREATE INDEX idx_notification_created_at ON notifications(created_at);
notification_recipients (수신자 테이블)
CREATE TABLE notification_recipients (
recipient_id VARCHAR(36) PRIMARY KEY,
notification_id VARCHAR(36) NOT NULL,
recipient_user_id VARCHAR(100) NOT NULL,
recipient_name VARCHAR(200) NOT NULL,
recipient_email VARCHAR(320) NOT NULL,
status VARCHAR(20) NOT NULL,
retry_count INTEGER NOT NULL DEFAULT 0,
sent_at TIMESTAMP,
error_message VARCHAR(1000),
next_retry_at TIMESTAMP,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP,
FOREIGN KEY (notification_id) REFERENCES notifications(notification_id)
);
CREATE INDEX idx_recipient_notification ON notification_recipients(notification_id);
CREATE INDEX idx_recipient_email ON notification_recipients(recipient_email);
CREATE INDEX idx_recipient_status ON notification_recipients(status);
notification_settings (알림 설정 테이블)
CREATE TABLE notification_settings (
user_id VARCHAR(100) PRIMARY KEY,
email_enabled BOOLEAN NOT NULL DEFAULT TRUE,
sms_enabled BOOLEAN NOT NULL DEFAULT FALSE,
push_enabled BOOLEAN NOT NULL DEFAULT FALSE,
invitation_enabled BOOLEAN NOT NULL DEFAULT TRUE,
todo_assigned_enabled BOOLEAN NOT NULL DEFAULT TRUE,
todo_reminder_enabled BOOLEAN NOT NULL DEFAULT TRUE,
meeting_reminder_enabled BOOLEAN NOT NULL DEFAULT TRUE,
minutes_updated_enabled BOOLEAN NOT NULL DEFAULT TRUE,
todo_completed_enabled BOOLEAN NOT NULL DEFAULT FALSE,
dnd_enabled BOOLEAN NOT NULL DEFAULT FALSE,
dnd_start_time TIME,
dnd_end_time TIME,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP
);
CREATE UNIQUE INDEX idx_setting_user_id ON notification_settings(user_id);
5. API 명세
5.1 알림 조회 API
알림 목록 조회
- URL: GET /notifications
- Query Parameters:
- referenceType: MEETING | TODO (optional)
- notificationType: INVITATION | TODO_ASSIGNED | ... (optional)
- status: PENDING | SENT | FAILED | PARTIAL (optional)
- startDate: ISO DateTime (optional)
- endDate: ISO DateTime (optional)
- Response: List
알림 상세 조회
- URL: GET /notifications/{notificationId}
- Path Parameter: notificationId (String)
- Response: NotificationResponse
알림 통계 조회
- URL: GET /notifications/statistics
- Query Parameters:
- startDate: ISO DateTime (required)
- endDate: ISO DateTime (required)
- Response: { sent, failed, partial, total }
5.2 알림 설정 API
알림 설정 조회
- URL: GET /notifications/settings
- Query Parameter: userId (String, required)
- Response: SettingsResponse
알림 설정 업데이트
- URL: PUT /notifications/settings
- Query Parameter: userId (String, required)
- Request Body: UpdateSettingsRequest
- Response: SettingsResponse
6. 이벤트 처리 흐름
6.1 회의 초대 알림 발송
1. Meeting Service → Event Hub (MeetingCreatedEvent)
2. Event Hub → NotificationService (EventHandler.accept())
3. EventHandler → NotificationService.sendMeetingInvitation()
4. NotificationService:
- 중복 체크 (eventId)
- Notification 엔티티 생성
- 각 참석자별:
- 알림 설정 확인
- NotificationRecipient 생성
- EmailTemplateService.renderMeetingInvitation()
- EmailClient.sendHtmlEmail()
- 상태 업데이트 (SENT/FAILED)
- Notification 상태 업데이트 (SENT/PARTIAL/FAILED)
5. EventHandler → eventContext.updateCheckpoint()
6.2 Todo 할당 알림 발송
1. Meeting/AI Service → Event Hub (TodoAssignedEvent)
2. Event Hub → NotificationService (EventHandler.accept())
3. EventHandler → NotificationService.sendTodoAssignment()
4. NotificationService:
- 중복 체크 (eventId)
- Notification 엔티티 생성
- 알림 설정 확인 (assignee)
- NotificationRecipient 생성
- EmailTemplateService.renderTodoAssigned()
- EmailClient.sendHtmlEmail()
- 상태 업데이트 (SENT/FAILED)
5. EventHandler → eventContext.updateCheckpoint()
7. 재시도 메커니즘
7.1 재시도 정책
- 최대 재시도 횟수: 3회
- Backoff 전략: Exponential Backoff
- 초기 대기 시간: 5분 (300,000ms)
- 최대 대기 시간: 30분 (1,800,000ms)
- 배수: 2.0
7.2 재시도 시나리오
-
이메일 발송 실패 시:
- EmailClient의 @Retryable 어노테이션 적용
- 1차 실패 → 5분 후 재시도
- 2차 실패 → 10분 후 재시도
- 3차 실패 → 20분 후 재시도
- 최종 실패 → NotificationRecipient 상태를 FAILED로 변경
-
Event Processor 시작 실패 시:
- EventProcessorService의 @Retryable 적용
- 최대 3번 재시도 (2초, 4초, 8초 대기)
8. 설정 파일
8.1 application.yml
- 위치: notification/src/main/resources/application.yml
- 주요 설정:
- 데이터베이스 연결 (PostgreSQL)
- Azure Event Hubs 연결
- Azure Blob Storage 연결
- SMTP 설정
- Thymeleaf 템플릿
- Actuator
- Logging
- SpringDoc OpenAPI
8.2 환경 변수 (필수)
# 데이터베이스
DB_PASSWORD=<PostgreSQL 비밀번호>
# Azure Event Hub
AZURE_EVENTHUB_CONNECTION_STRING=<Event Hub 연결 문자열>
# Azure Blob Storage
AZURE_STORAGE_CONNECTION_STRING=<Blob Storage 연결 문자열>
# 이메일
MAIL_USERNAME=<SMTP 사용자명>
MAIL_PASSWORD=<SMTP 비밀번호>
# JWT (향후 추가)
JWT_SECRET=<JWT 비밀 키>
9. 빌드 및 실행
9.1 빌드 방법
IntelliJ IDEA 사용
- IntelliJ에서 프로젝트 열기
- Gradle 탭에서 notification → Tasks → build → build 더블클릭
- 또는 우측 상단 Build → Build Project
커맨드 라인 (Gradle 설치 필요)
# Gradle Wrapper 생성 (프로젝트 루트에서)
gradle wrapper
# 빌드
./gradlew :notification:build
# 빌드 결과 확인
ls notification/build/libs/
9.2 실행 방법
IntelliJ IDEA 사용
- NotificationApplication.java 파일 열기
- main() 메서드 좌측의 실행 버튼 클릭
- 또는 Run → Run 'NotificationApplication'
JAR 파일 실행
java -jar notification/build/libs/notification.jar
9.3 서버 포트
- 기본 포트: 8084
- 변경 방법: 환경 변수 SERVER_PORT 설정
10. 테스트
10.1 API 테스트 (Swagger UI)
-
서버 실행 후 브라우저에서 접속:
http://localhost:8084/swagger-ui.html -
API 엔드포인트 테스트:
- GET /notifications - 알림 목록 조회
- GET /notifications/{id} - 특정 알림 조회
- GET /notifications/settings - 알림 설정 조회
- PUT /notifications/settings - 알림 설정 업데이트
10.2 이벤트 발행 테스트
- Meeting Service에서 MeetingCreatedEvent 발행
- Notification Service 로그 확인:
이벤트 수신 - Topic: meeting, EventType: MeetingCreated 회의 초대 알림 처리 시작 - MeetingId: xxx, EventId: xxx 회의 초대 알림 발송 성공 - Email: xxx@xxx.com
10.3 Health Check
curl http://localhost:8084/actuator/health
11. 향후 개선 사항
11.1 기능 개선
- SMS 발송 지원: 현재 이메일만 지원, SMS 발송 기능 추가
- Push 알림 지원: 모바일 Push 알림 기능 추가
- 리마인더 스케줄링: 회의 및 Todo 리마인더 자동 발송
- 배치 알림 발송: 대량 알림 발송 최적화
- 알림 템플릿 관리: 템플릿 동적 관리 및 다국어 지원
11.2 성능 개선
- 비동기 이메일 발송: @Async를 사용한 비동기 처리
- 캐싱 적용: 알림 설정 캐싱으로 DB 부하 감소
- 배치 처리: 대량 수신자 알림의 배치 처리
- Connection Pool 최적화: HikariCP 설정 최적화
11.3 모니터링 개선
- 메트릭 수집: Prometheus 메트릭 추가
- 로그 집계: ELK Stack 연동
- 알림 대시보드: Grafana 대시보드 구성
- 알림 실패 알람: 발송 실패 시 관리자 알림
12. 문제 해결 가이드
12.1 이메일 발송 실패
증상: 이메일이 발송되지 않음
원인 및 해결방법:
-
SMTP 인증 정보 확인:
MAIL_USERNAME=<이메일 주소> MAIL_PASSWORD=<앱 비밀번호> -
Gmail 사용 시 앱 비밀번호 생성:
- Google 계정 → 보안 → 2단계 인증 활성화
- 앱 비밀번호 생성
-
방화벽 확인:
- 포트 587 (TLS) 또는 465 (SSL) 개방 확인
12.2 Event Hub 연결 실패
증상: 이벤트를 수신하지 못함
원인 및 해결방법:
-
연결 문자열 확인:
AZURE_EVENTHUB_CONNECTION_STRING=<연결 문자열> -
Consumer Group 확인:
azure: eventhub: consumer-group: notification-service -
Event Hub 방화벽 설정 확인
12.3 데이터베이스 연결 실패
증상: 애플리케이션 시작 실패
원인 및 해결방법:
-
PostgreSQL 서버 상태 확인
-
연결 정보 확인:
datasource: url: jdbc:postgresql://4.230.159.143:5432/notificationdb username: hgzerouser password: ${DB_PASSWORD} -
방화벽 확인: 포트 5432 개방 확인
13. 참고 자료
13.1 문서
13.2 외부 라이브러리
작성일: 2025-10-23 작성자: 준호 (Backend Developer) 버전: 1.0