diff --git a/.gradle/8.14/checksums/checksums.lock b/.gradle/8.14/checksums/checksums.lock
new file mode 100644
index 0000000..2a68f3c
Binary files /dev/null and b/.gradle/8.14/checksums/checksums.lock differ
diff --git a/.gradle/8.14/fileChanges/last-build.bin b/.gradle/8.14/fileChanges/last-build.bin
new file mode 100644
index 0000000..f76dd23
Binary files /dev/null and b/.gradle/8.14/fileChanges/last-build.bin differ
diff --git a/.gradle/8.14/fileHashes/fileHashes.lock b/.gradle/8.14/fileHashes/fileHashes.lock
new file mode 100644
index 0000000..a76855d
Binary files /dev/null and b/.gradle/8.14/fileHashes/fileHashes.lock differ
diff --git a/.gradle/8.14/gc.properties b/.gradle/8.14/gc.properties
new file mode 100644
index 0000000..e69de29
diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock
new file mode 100644
index 0000000..e919f99
Binary files /dev/null and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ
diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties
new file mode 100644
index 0000000..835b5b8
--- /dev/null
+++ b/.gradle/buildOutputCleanup/cache.properties
@@ -0,0 +1,2 @@
+#Thu Oct 23 15:10:42 KST 2025
+gradle.version=8.14
diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties
new file mode 100644
index 0000000..e69de29
diff --git a/build/reports/problems/problems-report.html b/build/reports/problems/problems-report.html
new file mode 100644
index 0000000..54d21ff
--- /dev/null
+++ b/build/reports/problems/problems-report.html
@@ -0,0 +1,663 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Gradle Configuration Cache
+
+
+
+
+
+
+ Loading...
+
+
+
+
+
+
+
diff --git a/develop/dev/dev-notification.md b/develop/dev/dev-notification.md
new file mode 100644
index 0000000..850bb71
--- /dev/null
+++ b/develop/dev/dev-notification.md
@@ -0,0 +1,623 @@
+# 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 (알림 테이블)
+```sql
+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 (수신자 테이블)
+```sql
+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 (알림 설정 테이블)
+```sql
+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 재시도 시나리오
+
+1. **이메일 발송 실패 시**:
+ - EmailClient의 @Retryable 어노테이션 적용
+ - 1차 실패 → 5분 후 재시도
+ - 2차 실패 → 10분 후 재시도
+ - 3차 실패 → 20분 후 재시도
+ - 최종 실패 → NotificationRecipient 상태를 FAILED로 변경
+
+2. **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 환경 변수 (필수)
+```bash
+# 데이터베이스
+DB_PASSWORD=
+
+# Azure Event Hub
+AZURE_EVENTHUB_CONNECTION_STRING=
+
+# Azure Blob Storage
+AZURE_STORAGE_CONNECTION_STRING=
+
+# 이메일
+MAIL_USERNAME=
+MAIL_PASSWORD=
+
+# JWT (향후 추가)
+JWT_SECRET=
+```
+
+---
+
+## 9. 빌드 및 실행
+
+### 9.1 빌드 방법
+
+#### IntelliJ IDEA 사용
+1. IntelliJ에서 프로젝트 열기
+2. Gradle 탭에서 notification → Tasks → build → build 더블클릭
+3. 또는 우측 상단 Build → Build Project
+
+#### 커맨드 라인 (Gradle 설치 필요)
+```bash
+# Gradle Wrapper 생성 (프로젝트 루트에서)
+gradle wrapper
+
+# 빌드
+./gradlew :notification:build
+
+# 빌드 결과 확인
+ls notification/build/libs/
+```
+
+### 9.2 실행 방법
+
+#### IntelliJ IDEA 사용
+1. NotificationApplication.java 파일 열기
+2. main() 메서드 좌측의 실행 버튼 클릭
+3. 또는 Run → Run 'NotificationApplication'
+
+#### JAR 파일 실행
+```bash
+java -jar notification/build/libs/notification.jar
+```
+
+### 9.3 서버 포트
+- **기본 포트**: 8084
+- **변경 방법**: 환경 변수 SERVER_PORT 설정
+
+---
+
+## 10. 테스트
+
+### 10.1 API 테스트 (Swagger UI)
+1. 서버 실행 후 브라우저에서 접속:
+ ```
+ http://localhost:8084/swagger-ui.html
+ ```
+
+2. API 엔드포인트 테스트:
+ - GET /notifications - 알림 목록 조회
+ - GET /notifications/{id} - 특정 알림 조회
+ - GET /notifications/settings - 알림 설정 조회
+ - PUT /notifications/settings - 알림 설정 업데이트
+
+### 10.2 이벤트 발행 테스트
+1. Meeting Service에서 MeetingCreatedEvent 발행
+2. Notification Service 로그 확인:
+ ```
+ 이벤트 수신 - Topic: meeting, EventType: MeetingCreated
+ 회의 초대 알림 처리 시작 - MeetingId: xxx, EventId: xxx
+ 회의 초대 알림 발송 성공 - Email: xxx@xxx.com
+ ```
+
+### 10.3 Health Check
+```bash
+curl http://localhost:8084/actuator/health
+```
+
+---
+
+## 11. 향후 개선 사항
+
+### 11.1 기능 개선
+1. **SMS 발송 지원**: 현재 이메일만 지원, SMS 발송 기능 추가
+2. **Push 알림 지원**: 모바일 Push 알림 기능 추가
+3. **리마인더 스케줄링**: 회의 및 Todo 리마인더 자동 발송
+4. **배치 알림 발송**: 대량 알림 발송 최적화
+5. **알림 템플릿 관리**: 템플릿 동적 관리 및 다국어 지원
+
+### 11.2 성능 개선
+1. **비동기 이메일 발송**: @Async를 사용한 비동기 처리
+2. **캐싱 적용**: 알림 설정 캐싱으로 DB 부하 감소
+3. **배치 처리**: 대량 수신자 알림의 배치 처리
+4. **Connection Pool 최적화**: HikariCP 설정 최적화
+
+### 11.3 모니터링 개선
+1. **메트릭 수집**: Prometheus 메트릭 추가
+2. **로그 집계**: ELK Stack 연동
+3. **알림 대시보드**: Grafana 대시보드 구성
+4. **알림 실패 알람**: 발송 실패 시 관리자 알림
+
+---
+
+## 12. 문제 해결 가이드
+
+### 12.1 이메일 발송 실패
+**증상**: 이메일이 발송되지 않음
+
+**원인 및 해결방법**:
+1. SMTP 인증 정보 확인:
+ ```bash
+ MAIL_USERNAME=<이메일 주소>
+ MAIL_PASSWORD=<앱 비밀번호>
+ ```
+
+2. Gmail 사용 시 앱 비밀번호 생성:
+ - Google 계정 → 보안 → 2단계 인증 활성화
+ - 앱 비밀번호 생성
+
+3. 방화벽 확인:
+ - 포트 587 (TLS) 또는 465 (SSL) 개방 확인
+
+### 12.2 Event Hub 연결 실패
+**증상**: 이벤트를 수신하지 못함
+
+**원인 및 해결방법**:
+1. 연결 문자열 확인:
+ ```bash
+ AZURE_EVENTHUB_CONNECTION_STRING=<연결 문자열>
+ ```
+
+2. Consumer Group 확인:
+ ```yaml
+ azure:
+ eventhub:
+ consumer-group: notification-service
+ ```
+
+3. Event Hub 방화벽 설정 확인
+
+### 12.3 데이터베이스 연결 실패
+**증상**: 애플리케이션 시작 실패
+
+**원인 및 해결방법**:
+1. PostgreSQL 서버 상태 확인
+2. 연결 정보 확인:
+ ```yaml
+ datasource:
+ url: jdbc:postgresql://4.230.159.143:5432/notificationdb
+ username: hgzerouser
+ password: ${DB_PASSWORD}
+ ```
+
+3. 방화벽 확인: 포트 5432 개방 확인
+
+---
+
+## 13. 참고 자료
+
+### 13.1 문서
+- [백엔드개발가이드](claude/dev-backend.md)
+- [패키지구조도](develop/dev/package-structure-notification.md)
+- [API설계서](design/backend/api/API설계서.md)
+- [데이터베이스설치결과서](develop/database/exec/db-exec-dev.md)
+
+### 13.2 외부 라이브러리
+- [Spring Boot Documentation](https://spring.io/projects/spring-boot)
+- [Azure Event Hubs Java SDK](https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-java-get-started-send)
+- [Spring Retry](https://github.com/spring-projects/spring-retry)
+- [Thymeleaf](https://www.thymeleaf.org/)
+
+---
+
+**작성일**: 2025-10-23
+**작성자**: 준호 (Backend Developer)
+**버전**: 1.0
diff --git a/develop/dev/package-structure-notification.md b/develop/dev/package-structure-notification.md
new file mode 100644
index 0000000..5d05f60
--- /dev/null
+++ b/develop/dev/package-structure-notification.md
@@ -0,0 +1,146 @@
+# Notification Service - 패키지 구조도
+
+## 아키텍처 패턴
+- **Layered Architecture**: 단순하고 명확한 계층 구조
+
+## 패키지 구조
+
+```
+notification/
+└── src/
+ └── main/
+ ├── java/
+ │ └── com/
+ │ └── unicorn/
+ │ └── hgzero/
+ │ └── notification/
+ │ ├── NotificationApplication.java
+ │ │
+ │ ├── 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 # Public API
+ │ │ └── NotificationSettingsController.java # 설정 API
+ │ │
+ │ ├── event/ # Event Handler Layer
+ │ │ ├── EventHandler.java # Event Hub 이벤트 핸들러
+ │ │ ├── event/
+ │ │ │ ├── MeetingCreatedEvent.java # 회의 생성 이벤트
+ │ │ │ └── TodoAssignedEvent.java # Todo 할당 이벤트
+ │ │ └── processor/
+ │ │ └── EventProcessorService.java # Processor 라이프사이클
+ │ │
+ │ ├── dto/ # Data Transfer Objects
+ │ │ ├── request/
+ │ │ │ ├── SendNotificationRequest.java
+ │ │ │ └── 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
+ │ ├── GlobalExceptionHandler.java
+ │ ├── NotificationException.java
+ │ └── EventProcessingException.java
+ │
+ └── resources/
+ ├── application.yml # 메인 설정 파일
+ ├── application-dev.yml # 개발 환경 설정
+ ├── application-prod.yml # 운영 환경 설정
+ └── templates/ # Email Templates
+ ├── meeting-invitation.html # 회의 초대 템플릿
+ ├── todo-assigned.html # Todo 할당 템플릿
+ └── reminder.html # 리마인더 템플릿
+```
+
+## 주요 클래스 역할
+
+### Domain Layer
+- **Notification**: 알림 정보 엔티티 (알림ID, 유형, 상태, 발송일시)
+- **NotificationRecipient**: 수신자별 알림 상태 (발송완료, 실패, 재시도)
+- **NotificationSetting**: 사용자별 알림 설정 (채널, 유형, 방해금지 시간대)
+
+### Repository Layer
+- **NotificationRepository**: 알림 이력 조회/저장
+- **NotificationRecipientRepository**: 수신자별 상태 관리
+- **NotificationSettingRepository**: 알림 설정 관리
+
+### Service Layer
+- **NotificationService**: 알림 발송 비즈니스 로직, 중복 방지, 재시도 관리
+- **EmailTemplateService**: Thymeleaf 템플릿 렌더링
+- **EmailClient**: SMTP 이메일 발송, 에러 처리
+
+### Controller Layer
+- **NotificationController**: 알림 발송 API, 알림 이력 조회 API
+- **NotificationSettingsController**: 알림 설정 조회/업데이트 API
+
+### Event Handler Layer
+- **EventHandler**: Event Hub 이벤트 수신 및 처리 (Consumer 구현)
+- **EventProcessorService**: EventProcessorClient 라이프사이클 관리
+- **MeetingCreatedEvent**: 회의 생성 이벤트 DTO
+- **TodoAssignedEvent**: Todo 할당 이벤트 DTO
+
+### Config Layer
+- **EventHubConfig**: EventProcessorClient Bean 생성, CheckpointStore 설정
+- **BlobStorageConfig**: Azure Blob Storage 연결 설정
+- **RetryConfig**: @EnableRetry, ExponentialBackOffPolicy 설정
+- **SecurityConfig**: JWT 인증, CORS 설정
+- **SwaggerConfig**: OpenAPI 문서화 설정
+- **EmailConfig**: JavaMailSender 설정
+
+## 의존성 흐름
+```
+Controller → Service → Repository → Entity
+ ↓
+ EmailClient
+ ↓
+ EmailTemplateService
+
+EventHandler → Service → Repository
+```
+
+## 기술 스택
+- **Framework**: Spring Boot 3.3.0, Java 21
+- **Database**: PostgreSQL (JPA/Hibernate)
+- **Messaging**: Azure Event Hubs
+- **Storage**: Azure Blob Storage (Checkpoint)
+- **Email**: Spring Mail (SMTP)
+- **Template**: Thymeleaf
+- **Retry**: Spring Retry
+- **Security**: Spring Security + JWT
+- **Documentation**: SpringDoc OpenAPI
+
+## 특징
+1. **Layered Architecture**: 계층 분리로 명확한 역할과 책임
+2. **Event-Driven**: Azure Event Hubs 기반 비동기 처리
+3. **Retry Mechanism**: Exponential Backoff 기반 재시도
+4. **Template Engine**: Thymeleaf로 동적 이메일 생성
+5. **Idempotency**: 이벤트 ID 기반 중복 발송 방지
+6. **Monitoring**: Actuator Health Check, Metrics
+
+---
+
+**작성일**: 2025-10-23
+**작성자**: 준호 (Backend Developer)
diff --git a/notification/src/main/java/com/unicorn/hgzero/notification/NotificationApplication.java b/notification/src/main/java/com/unicorn/hgzero/notification/NotificationApplication.java
new file mode 100644
index 0000000..187e6fc
--- /dev/null
+++ b/notification/src/main/java/com/unicorn/hgzero/notification/NotificationApplication.java
@@ -0,0 +1,22 @@
+package com.unicorn.hgzero.notification;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Notification Service Application
+ *
+ * 회의 초대 및 Todo 할당 알림 발송 서비스
+ * Azure Event Hubs를 통한 이벤트 기반 알림 처리
+ *
+ * @author 준호 (Backend Developer)
+ * @version 1.0
+ * @since 2025-10-23
+ */
+@SpringBootApplication
+public class NotificationApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(NotificationApplication.class, args);
+ }
+}
diff --git a/notification/src/main/java/com/unicorn/hgzero/notification/config/BlobStorageConfig.java b/notification/src/main/java/com/unicorn/hgzero/notification/config/BlobStorageConfig.java
new file mode 100644
index 0000000..3284d70
--- /dev/null
+++ b/notification/src/main/java/com/unicorn/hgzero/notification/config/BlobStorageConfig.java
@@ -0,0 +1,48 @@
+package com.unicorn.hgzero.notification.config;
+
+import com.azure.storage.blob.BlobContainerAsyncClient;
+import com.azure.storage.blob.BlobContainerClientBuilder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Azure Blob Storage 설정
+ *
+ * Event Hub Checkpoint 저장용 Blob Storage 연결 구성
+ * EventProcessorClient의 체크포인트 저장소로 사용
+ *
+ * @author 준호 (Backend Developer)
+ * @version 1.0
+ * @since 2025-10-23
+ */
+@Slf4j
+@Configuration
+public class BlobStorageConfig {
+
+ @Value("${azure.storage.connection-string}")
+ private String storageConnectionString;
+
+ @Value("${azure.storage.container-name}")
+ private String containerName;
+
+ /**
+ * Blob Container Async Client Bean 생성
+ *
+ * @return Blob Container Async Client
+ */
+ @Bean
+ public BlobContainerAsyncClient blobContainerAsyncClient() {
+ log.info("BlobContainerAsyncClient 생성 중 - Container: {}", containerName);
+
+ BlobContainerAsyncClient client = new BlobContainerClientBuilder()
+ .connectionString(storageConnectionString)
+ .containerName(containerName)
+ .buildAsyncClient();
+
+ log.info("BlobContainerAsyncClient 생성 완료");
+
+ return client;
+ }
+}
diff --git a/notification/src/main/java/com/unicorn/hgzero/notification/config/EmailConfig.java b/notification/src/main/java/com/unicorn/hgzero/notification/config/EmailConfig.java
new file mode 100644
index 0000000..e6f76b2
--- /dev/null
+++ b/notification/src/main/java/com/unicorn/hgzero/notification/config/EmailConfig.java
@@ -0,0 +1,72 @@
+package com.unicorn.hgzero.notification.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+
+import java.util.Properties;
+
+/**
+ * 이메일 발송 설정
+ *
+ * JavaMailSender 구성 및 SMTP 설정
+ * Gmail SMTP 또는 다른 SMTP 서버 사용
+ *
+ * @author 준호 (Backend Developer)
+ * @version 1.0
+ * @since 2025-10-23
+ */
+@Slf4j
+@Configuration
+public class EmailConfig {
+
+ @Value("${spring.mail.host}")
+ private String host;
+
+ @Value("${spring.mail.port}")
+ private int port;
+
+ @Value("${spring.mail.username}")
+ private String username;
+
+ @Value("${spring.mail.password}")
+ private String password;
+
+ @Value("${spring.mail.properties.mail.smtp.auth:true}")
+ private String smtpAuth;
+
+ @Value("${spring.mail.properties.mail.smtp.starttls.enable:true}")
+ private String starttlsEnable;
+
+ /**
+ * JavaMailSender Bean 생성
+ *
+ * @return JavaMailSender
+ */
+ @Bean
+ public JavaMailSender javaMailSender() {
+ log.info("JavaMailSender 구성 중 - Host: {}, Port: {}", host, port);
+
+ JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
+
+ // SMTP 서버 설정
+ mailSender.setHost(host);
+ mailSender.setPort(port);
+ mailSender.setUsername(username);
+ mailSender.setPassword(password);
+
+ // SMTP 속성 설정
+ Properties props = mailSender.getJavaMailProperties();
+ props.put("mail.transport.protocol", "smtp");
+ props.put("mail.smtp.auth", smtpAuth);
+ props.put("mail.smtp.starttls.enable", starttlsEnable);
+ props.put("mail.debug", "false"); // 디버그 모드 (필요 시 true)
+
+ log.info("JavaMailSender 구성 완료");
+
+ return mailSender;
+ }
+}
diff --git a/notification/src/main/java/com/unicorn/hgzero/notification/config/EventHubConfig.java b/notification/src/main/java/com/unicorn/hgzero/notification/config/EventHubConfig.java
new file mode 100644
index 0000000..e789ae5
--- /dev/null
+++ b/notification/src/main/java/com/unicorn/hgzero/notification/config/EventHubConfig.java
@@ -0,0 +1,74 @@
+package com.unicorn.hgzero.notification.config;
+
+import com.azure.messaging.eventhubs.EventProcessorClient;
+import com.azure.messaging.eventhubs.EventProcessorClientBuilder;
+import com.azure.messaging.eventhubs.checkpointstore.blob.BlobCheckpointStore;
+import com.azure.messaging.eventhubs.models.EventContext;
+import com.azure.storage.blob.BlobContainerAsyncClient;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.function.Consumer;
+
+/**
+ * Azure Event Hubs 설정
+ *
+ * EventProcessorClient 구성 및 이벤트 처리 설정
+ * Blob Storage 기반 Checkpoint 관리
+ *
+ * @author 준호 (Backend Developer)
+ * @version 1.0
+ * @since 2025-10-23
+ */
+@Slf4j
+@Configuration
+public class EventHubConfig {
+
+ @Value("${azure.eventhub.connection-string}")
+ private String eventHubConnectionString;
+
+ @Value("${azure.eventhub.name}")
+ private String eventHubName;
+
+ @Value("${azure.eventhub.consumer-group}")
+ private String consumerGroup;
+
+ /**
+ * EventProcessorClient Bean 생성
+ *
+ * @param blobContainerAsyncClient Blob Storage 클라이언트
+ * @param eventHandler 이벤트 핸들러
+ * @return EventProcessorClient
+ */
+ @Bean
+ public EventProcessorClient eventProcessorClient(
+ BlobContainerAsyncClient blobContainerAsyncClient,
+ Consumer eventHandler
+ ) {
+ log.info("EventProcessorClient 생성 중 - EventHub: {}, ConsumerGroup: {}",
+ eventHubName, consumerGroup);
+
+ // Blob Checkpoint Store 생성
+ BlobCheckpointStore checkpointStore = new BlobCheckpointStore(blobContainerAsyncClient);
+
+ // EventProcessorClient 빌더 구성
+ EventProcessorClient eventProcessorClient = new EventProcessorClientBuilder()
+ .connectionString(eventHubConnectionString, eventHubName)
+ .consumerGroup(consumerGroup)
+ .checkpointStore(checkpointStore)
+ .processEvent(eventHandler)
+ .processError(errorContext -> {
+ log.error("이벤트 처리 오류 발생 - PartitionId: {}, ErrorType: {}",
+ errorContext.getPartitionContext().getPartitionId(),
+ errorContext.getThrowable().getClass().getSimpleName(),
+ errorContext.getThrowable());
+ })
+ .buildEventProcessorClient();
+
+ log.info("EventProcessorClient 생성 완료");
+
+ return eventProcessorClient;
+ }
+}
diff --git a/notification/src/main/java/com/unicorn/hgzero/notification/config/RetryConfig.java b/notification/src/main/java/com/unicorn/hgzero/notification/config/RetryConfig.java
new file mode 100644
index 0000000..7813a50
--- /dev/null
+++ b/notification/src/main/java/com/unicorn/hgzero/notification/config/RetryConfig.java
@@ -0,0 +1,56 @@
+package com.unicorn.hgzero.notification.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.retry.annotation.EnableRetry;
+import org.springframework.retry.backoff.ExponentialBackOffPolicy;
+import org.springframework.retry.policy.SimpleRetryPolicy;
+import org.springframework.retry.support.RetryTemplate;
+
+/**
+ * 재시도 정책 설정
+ *
+ * Spring Retry를 사용한 재시도 메커니즘 구성
+ * 이메일 발송 실패 시 Exponential Backoff 전략 적용
+ *
+ * @author 준호 (Backend Developer)
+ * @version 1.0
+ * @since 2025-10-23
+ */
+@Slf4j
+@Configuration
+@EnableRetry
+public class RetryConfig {
+
+ /**
+ * RetryTemplate Bean 생성
+ *
+ * 재시도 정책:
+ * - 최대 3번 재시도
+ * - Exponential Backoff: 초기 5분, 최대 30분, 배수 2.0
+ *
+ * @return 구성된 RetryTemplate
+ */
+ @Bean
+ public RetryTemplate retryTemplate() {
+ RetryTemplate retryTemplate = new RetryTemplate();
+
+ // 재시도 정책: 최대 3번
+ SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
+ retryPolicy.setMaxAttempts(3);
+
+ // Backoff 정책: Exponential Backoff
+ ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
+ backOffPolicy.setInitialInterval(300000); // 5분
+ backOffPolicy.setMaxInterval(1800000); // 30분
+ backOffPolicy.setMultiplier(2.0); // 배수
+
+ retryTemplate.setRetryPolicy(retryPolicy);
+ retryTemplate.setBackOffPolicy(backOffPolicy);
+
+ log.info("RetryTemplate 생성 완료 - MaxAttempts: 3, InitialInterval: 5분, MaxInterval: 30분, Multiplier: 2.0");
+
+ return retryTemplate;
+ }
+}
diff --git a/notification/src/main/java/com/unicorn/hgzero/notification/config/SecurityConfig.java b/notification/src/main/java/com/unicorn/hgzero/notification/config/SecurityConfig.java
new file mode 100644
index 0000000..44af01d
--- /dev/null
+++ b/notification/src/main/java/com/unicorn/hgzero/notification/config/SecurityConfig.java
@@ -0,0 +1,113 @@
+package com.unicorn.hgzero.notification.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
+
+/**
+ * Spring Security 설정
+ *
+ * JWT 기반 인증, CORS 설정
+ * Stateless 세션 관리
+ *
+ * @author 준호 (Backend Developer)
+ * @version 1.0
+ * @since 2025-10-23
+ */
+@Slf4j
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+ /**
+ * Security Filter Chain 구성
+ *
+ * @param http HttpSecurity
+ * @return SecurityFilterChain
+ * @throws Exception 설정 오류 시
+ */
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ log.info("SecurityFilterChain 구성 중...");
+
+ http
+ // CSRF 비활성화 (REST API이므로)
+ .csrf(AbstractHttpConfigurer::disable)
+
+ // CORS 설정 활성화
+ .cors(cors -> cors.configurationSource(corsConfigurationSource()))
+
+ // 세션 관리: Stateless (JWT 사용)
+ .sessionManagement(session ->
+ session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ )
+
+ // 요청 인가 규칙
+ .authorizeHttpRequests(auth -> auth
+ // Swagger UI 및 API 문서는 인증 없이 접근 가능
+ .requestMatchers(
+ "/swagger-ui/**",
+ "/v3/api-docs/**",
+ "/swagger-resources/**",
+ "/webjars/**"
+ ).permitAll()
+
+ // Actuator Health Check는 인증 없이 접근 가능
+ .requestMatchers("/actuator/health").permitAll()
+
+ // 그 외 모든 요청은 인증 필요
+ .anyRequest().authenticated()
+ );
+
+ log.info("SecurityFilterChain 구성 완료");
+
+ return http.build();
+ }
+
+ /**
+ * CORS 설정
+ *
+ * @return CORS Configuration Source
+ */
+ @Bean
+ public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+
+ // 허용할 Origin (개발 환경)
+ configuration.setAllowedOrigins(List.of(
+ "http://localhost:3000",
+ "http://localhost:8080"
+ ));
+
+ // 허용할 HTTP 메서드
+ configuration.setAllowedMethods(List.of(
+ "GET", "POST", "PUT", "DELETE", "OPTIONS"
+ ));
+
+ // 허용할 헤더
+ configuration.setAllowedHeaders(List.of("*"));
+
+ // 인증 정보 포함 허용
+ configuration.setAllowCredentials(true);
+
+ // 최대 캐시 시간 (초)
+ configuration.setMaxAge(3600L);
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", configuration);
+
+ log.info("CORS 설정 완료");
+
+ return source;
+ }
+}
diff --git a/notification/src/main/java/com/unicorn/hgzero/notification/config/SwaggerConfig.java b/notification/src/main/java/com/unicorn/hgzero/notification/config/SwaggerConfig.java
new file mode 100644
index 0000000..84a88d6
--- /dev/null
+++ b/notification/src/main/java/com/unicorn/hgzero/notification/config/SwaggerConfig.java
@@ -0,0 +1,65 @@
+package com.unicorn.hgzero.notification.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import io.swagger.v3.oas.models.servers.Server;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * Swagger/OpenAPI 설정
+ *
+ * API 문서 자동 생성 및 Swagger UI 설정
+ * SpringDoc OpenAPI 3.0 사용
+ *
+ * @author 준호 (Backend Developer)
+ * @version 1.0
+ * @since 2025-10-23
+ */
+@Slf4j
+@Configuration
+public class SwaggerConfig {
+
+ @Value("${spring.application.name:notification}")
+ private String applicationName;
+
+ /**
+ * OpenAPI 설정 Bean 생성
+ *
+ * @return OpenAPI 설정
+ */
+ @Bean
+ public OpenAPI openAPI() {
+ log.info("OpenAPI 설정 생성 중...");
+
+ OpenAPI openAPI = new OpenAPI()
+ .info(new Info()
+ .title("HGZero Notification Service API")
+ .description("회의 초대 및 Todo 할당 알림 발송 서비스 API")
+ .version("1.0.0")
+ .contact(new Contact()
+ .name("Backend Team")
+ .email("backend@hgzero.com"))
+ .license(new License()
+ .name("Apache 2.0")
+ .url("https://www.apache.org/licenses/LICENSE-2.0.html")))
+ .servers(List.of(
+ new Server()
+ .url("http://localhost:8080")
+ .description("Local Development Server"),
+ new Server()
+ .url("https://api.hgzero.com")
+ .description("Production Server")
+ ));
+
+ log.info("OpenAPI 설정 생성 완료");
+
+ return openAPI;
+ }
+}
diff --git a/notification/src/main/java/com/unicorn/hgzero/notification/controller/NotificationController.java b/notification/src/main/java/com/unicorn/hgzero/notification/controller/NotificationController.java
new file mode 100644
index 0000000..23ce4ee
--- /dev/null
+++ b/notification/src/main/java/com/unicorn/hgzero/notification/controller/NotificationController.java
@@ -0,0 +1,183 @@
+package com.unicorn.hgzero.notification.controller;
+
+import com.unicorn.hgzero.notification.domain.Notification;
+import com.unicorn.hgzero.notification.dto.response.NotificationListResponse;
+import com.unicorn.hgzero.notification.dto.response.NotificationResponse;
+import com.unicorn.hgzero.notification.repository.NotificationRepository;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 알림 조회 API Controller
+ *
+ * 알림 이력 조회 API 제공
+ * 알림 목록 조회, 특정 알림 상세 조회 지원
+ *
+ * @author 준호 (Backend Developer)
+ * @version 1.0
+ * @since 2025-10-23
+ */
+@Slf4j
+@RestController
+@RequestMapping("/notifications")
+@RequiredArgsConstructor
+@Tag(name = "Notification", description = "알림 조회 API")
+public class NotificationController {
+
+ private final NotificationRepository notificationRepository;
+
+ /**
+ * 알림 목록 조회
+ *
+ * @param referenceType 참조 유형 (MEETING, TODO) - optional
+ * @param notificationType 알림 유형 (INVITATION, TODO_ASSIGNED 등) - optional
+ * @param status 알림 상태 (PENDING, SENT, FAILED 등) - optional
+ * @param startDate 시작 일시 - optional
+ * @param endDate 종료 일시 - optional
+ * @return 알림 목록
+ */
+ @Operation(summary = "알림 목록 조회", description = "다양한 조건으로 알림 목록을 조회합니다")
+ @ApiResponses(value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "조회 성공",
+ content = @Content(schema = @Schema(implementation = NotificationListResponse.class))
+ ),
+ @ApiResponse(responseCode = "400", description = "잘못된 요청"),
+ @ApiResponse(responseCode = "500", description = "서버 오류")
+ })
+ @GetMapping
+ public ResponseEntity> getNotifications(
+ @Parameter(description = "참조 유형 (MEETING, TODO)")
+ @RequestParam(required = false) Notification.ReferenceType referenceType,
+
+ @Parameter(description = "알림 유형 (INVITATION, TODO_ASSIGNED, TODO_REMINDER 등)")
+ @RequestParam(required = false) Notification.NotificationType notificationType,
+
+ @Parameter(description = "알림 상태 (PENDING, PROCESSING, SENT, FAILED, PARTIAL)")
+ @RequestParam(required = false) Notification.NotificationStatus status,
+
+ @Parameter(description = "시작 일시 (yyyy-MM-dd'T'HH:mm:ss)")
+ @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate,
+
+ @Parameter(description = "종료 일시 (yyyy-MM-dd'T'HH:mm:ss)")
+ @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate
+ ) {
+ log.info("알림 목록 조회 - ReferenceType: {}, NotificationType: {}, Status: {}, StartDate: {}, EndDate: {}",
+ referenceType, notificationType, status, startDate, endDate);
+
+ List notifications;
+
+ // 조건별 조회
+ if (notificationType != null) {
+ notifications = notificationRepository.findByNotificationType(notificationType);
+ } else if (status != null) {
+ notifications = notificationRepository.findByStatusIn(List.of(status));
+ } else if (startDate != null && endDate != null) {
+ notifications = notificationRepository.findByCreatedAtBetween(startDate, endDate);
+ } else {
+ // 기본: 모든 알림 조회 (최근 순)
+ notifications = notificationRepository.findAll();
+ }
+
+ List response = notifications.stream()
+ .map(NotificationListResponse::from)
+ .collect(Collectors.toList());
+
+ log.info("알림 목록 조회 완료 - 조회 건수: {}", response.size());
+ return ResponseEntity.ok(response);
+ }
+
+ /**
+ * 특정 알림 상세 조회
+ *
+ * @param notificationId 알림 ID
+ * @return 알림 상세 정보 (수신자 목록 포함)
+ */
+ @Operation(summary = "알림 상세 조회", description = "특정 알림의 상세 정보를 조회합니다 (수신자 목록 포함)")
+ @ApiResponses(value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "조회 성공",
+ content = @Content(schema = @Schema(implementation = NotificationResponse.class))
+ ),
+ @ApiResponse(responseCode = "404", description = "알림을 찾을 수 없음"),
+ @ApiResponse(responseCode = "500", description = "서버 오류")
+ })
+ @GetMapping("/{notificationId}")
+ public ResponseEntity getNotification(
+ @Parameter(description = "알림 ID", required = true)
+ @PathVariable String notificationId
+ ) {
+ log.info("알림 상세 조회 - NotificationId: {}", notificationId);
+
+ Notification notification = notificationRepository.findById(notificationId)
+ .orElseThrow(() -> {
+ log.error("알림을 찾을 수 없음 - NotificationId: {}", notificationId);
+ return new RuntimeException("알림을 찾을 수 없습니다: " + notificationId);
+ });
+
+ NotificationResponse response = NotificationResponse.from(notification);
+
+ log.info("알림 상세 조회 완료 - NotificationId: {}", notificationId);
+ return ResponseEntity.ok(response);
+ }
+
+ /**
+ * 알림 상태별 통계 조회 (모니터링용)
+ *
+ * @param startDate 시작 일시
+ * @param endDate 종료 일시
+ * @return 상태별 알림 건수
+ */
+ @Operation(summary = "알림 통계 조회", description = "기간별 알림 상태 통계를 조회합니다")
+ @GetMapping("/statistics")
+ public ResponseEntity