edit logical architecture

This commit is contained in:
cherry2250 2025-10-22 11:25:59 +09:00
parent 058710c40b
commit b63657ec42
4 changed files with 1230 additions and 303 deletions

View File

@ -0,0 +1,910 @@
# KT AI 기반 소상공인 이벤트 자동 생성 서비스 - 논리 아키텍처
## 문서 정보
- **작성일**: 2025-10-21
- **최종 수정일**: 2025-10-22
- **버전**: 2.0 (CQRS + Event-Driven 전환)
- **작성자**: System Architect
- **관련 문서**:
- [유저스토리](../../userstory.md)
- [아키텍처 패턴](../../pattern/architecture-pattern.md)
- [UI/UX 설계서](../../uiux/uiux.md)
## 버전 이력
- **v1.0** (2025-10-21): 초기 마이크로서비스 아키텍처 설계
- **v2.0** (2025-10-22): CQRS 패턴 및 Event-Driven 아키텍처 전환, Resilience 패턴 전면 적용
---
## 목차
1. [개요](#1-개요)
2. [서비스 아키텍처](#2-서비스-아키텍처)
3. [주요 사용자 플로우](#3-주요-사용자-플로우)
4. [데이터 흐름 및 캐싱 전략](#4-데이터-흐름-및-캐싱-전략)
5. [확장성 및 성능 고려사항](#5-확장성-및-성능-고려사항)
6. [보안 고려사항](#6-보안-고려사항)
7. [논리 아키텍처 다이어그램](#7-논리-아키텍처-다이어그램)
---
## 1. 개요
### 1.1 설계 원칙
본 논리 아키텍처는 다음 원칙을 기반으로 설계되었습니다:
#### 유저스토리 기반 설계
- 20개 유저스토리와 정확히 매칭
- 불필요한 추가 기능 배제
- 비즈니스 요구사항 우선 반영
#### CQRS (Command Query Responsibility Segregation)
- **읽기/쓰기 분리**: Command Service와 Query Service로 책임 분리
- **독립적 확장**: 읽기와 쓰기 부하에 따라 독립적으로 확장
- **성능 최적화**: Query Service는 읽기 최적화 데이터 모델 사용
- **이벤트 소싱 준비**: 도메인 이벤트 기반 상태 동기화
#### Event-Driven 아키텍처
- **비동기 메시징**: Event Bus(Kafka/SQS)를 통한 서비스 간 통신
- **느슨한 결합**: 서비스 간 직접 의존성 제거
- **확장성**: 이벤트 구독자 추가로 기능 확장 용이
- **장애 격리**: 이벤트 발행/구독 실패 시 서비스 독립성 유지
#### Resilience 패턴 적용
- **Circuit Breaker**: 외부 API 장애 시 빠른 실패 및 복구 (Hystrix/Resilience4j)
- **Retry Pattern**: 일시적 장애 시 자동 재시도 (지수 백오프)
- **Timeout Pattern**: 응답 시간 제한으로 리소스 점유 방지
- **Bulkhead Pattern**: 리소스 격리로 장애 전파 차단
- **Fallback Pattern**: 장애 시 대체 로직 실행 (캐시 응답 등)
### 1.2 핵심 컴포넌트 정의
#### Command Services (쓰기 전용)
1. **User Service**: 사용자 인증 및 매장정보 관리
- 회원가입/로그인 (JWT 발급)
- 프로필 수정
- 사업자번호 검증 (외부 API 연동)
2. **Event Command Service**: 이벤트 생성/수정/삭제
- 이벤트 생성 플로우 오케스트레이션
- 도메인 이벤트 발행 (EventCreated, EventPublished)
- 이벤트 상태 변경
3. **Participation Command Service**: 참여 및 당첨자 관리
- 참여 접수 및 검증
- 당첨자 추첨 실행
- 도메인 이벤트 발행 (ParticipantRegistered, WinnerSelected)
#### Query Services (읽기 전용)
1. **Event Query Service**: 이벤트 조회 최적화
- 이벤트 목록/상세 조회
- 이벤트 검색 및 필터링
- 읽기 최적화 데이터 모델 (비정규화)
2. **Participation Query Service**: 참여자/당첨자 조회
- 참여자 목록 조회
- 당첨자 조회
- 읽기 최적화 집계 데이터
3. **Analytics Query Service**: 실시간 성과 분석
- 대시보드 데이터 조회
- 채널별 성과 집계
- ROI 계산 및 분석
#### Async Services (비동기 처리)
1. **AI Service**: AI 기반 이벤트 추천
- Job Queue를 통한 비동기 처리
- Circuit Breaker 적용 (외부 AI API)
- 결과 캐싱 (Redis)
2. **Content Service**: SNS 이미지 생성
- Job Queue를 통한 비동기 처리
- Circuit Breaker 적용 (외부 이미지 API)
- CDN 업로드 및 캐싱
3. **Distribution Service**: 다중 채널 배포
- Event Bus를 통한 EventPublished 구독
- 병렬 배포 및 Circuit Breaker 적용
- 배포 완료 이벤트 발행 (DistributionCompleted)
#### Event Bus (Kafka/SQS)
- **도메인 이벤트 발행/구독**: 서비스 간 비동기 통신
- **이벤트 종류**:
- EventCreated: 이벤트 생성 시
- EventPublished: 이벤트 배포 승인 시
- ParticipantRegistered: 참여자 등록 시
- WinnerSelected: 당첨자 선정 시
- DistributionCompleted: 배포 완료 시
- **보장 수준**: At-Least-Once Delivery
#### Job Queue (RabbitMQ)
- **장시간 비동기 작업**: AI 추천, 이미지 생성
- **Priority Queue**: 작업 우선순위 관리
- **Dead Letter Queue**: 실패 작업 처리
#### Data Layer
- **Redis Cache**: 세션, AI 결과, 이미지 URL, 대시보드 캐싱
- **PostgreSQL**: 서비스별 독립 데이터베이스
- User DB, Event Write DB, Event Read DB, Participation Write DB, Participation Read DB, Analytics DB
- **읽기 전용 복제본**: Query Service 성능 최적화
#### External Systems
- **국세청 API**: 사업자번호 검증
- **AI APIs**: Claude/GPT-4 (트렌드 분석)
- **이미지 생성 APIs**: Stable Diffusion/DALL-E
- **배포 채널 APIs**: 우리동네TV, 링고비즈, 지니TV, SNS APIs
---
## 2. 서비스 아키텍처
### 2.1 CQRS 패턴 적용
#### 설계 원칙
- **Command와 Query 분리**: 쓰기와 읽기 책임을 독립된 서비스로 분리
- **독립적 확장**: 읽기/쓰기 부하에 따라 독립적으로 스케일링
- **최적화된 데이터 모델**: Query Service는 비정규화된 읽기 최적화 모델 사용
- **이벤트 동기화**: Command Service가 발행한 도메인 이벤트로 Query Service 동기화
### 2.2 Command Services (쓰기 전용)
#### User Service
**핵심 책임**:
- 회원가입/로그인 (JWT 토큰 발급)
- 프로필 수정 (매장 정보 포함)
- 사업자번호 검증 (국세청 API 연동)
- 세션 관리
**관련 유저스토리**: UFR-USER-010, 020, 030, 040
**Resilience 패턴**:
- **Circuit Breaker**: 국세청 API 호출 시 (실패율 5% 초과 시 Open)
- **Retry**: 최대 3회 재시도 (지수 백오프: 1초, 2초, 4초)
- **Timeout**: 5초
- **Fallback**: 사업자번호 검증 스킵 (수동 확인 안내)
**데이터 저장**:
- User DB: users, stores 테이블
- Redis: 세션 정보 (TTL 7일), 사업자번호 검증 결과 (TTL 7일)
#### Event Command Service
**핵심 책임**:
- 이벤트 생성/수정/삭제
- 이벤트 생성 플로우 오케스트레이션
- 도메인 이벤트 발행 (EventCreated, EventPublished)
**관련 유저스토리**: UFR-EVENT-010, 020, 030, 040, 050, 060, 070
**도메인 이벤트**:
1. **EventCreated**: 이벤트 생성 완료 시
- Payload: eventId, storeId, title, objective, createdAt
- 구독자: Event Query Service, Analytics Query Service
2. **EventPublished**: 이벤트 배포 승인 시
- Payload: eventId, distributionChannels, publishedAt
- 구독자: Distribution Service
**주요 플로우**:
1. 이벤트 목적 선택 → Event DB 저장
2. AI 추천 요청 → Job Queue 발행
3. 이미지 생성 요청 → Job Queue 발행
4. 배포 승인 → EventPublished 이벤트 발행
**데이터 저장**:
- Event Write DB: events, event_objectives, event_prizes 테이블
#### Participation Command Service
**핵심 책임**:
- 이벤트 참여 접수 및 검증
- 당첨자 추첨 실행
- 도메인 이벤트 발행 (ParticipantRegistered, WinnerSelected)
**관련 유저스토리**: UFR-PART-010, 020, 030
**도메인 이벤트**:
1. **ParticipantRegistered**: 참여자 등록 시
- Payload: participantId, eventId, phoneNumber, registeredAt
- 구독자: Participation Query Service, Analytics Query Service
2. **WinnerSelected**: 당첨자 선정 시
- Payload: winnerId, eventId, selectedAt
- 구독자: Participation Query Service
**주요 기능**:
- 중복 참여 체크 (전화번호 기반)
- 난수 기반 무작위 추첨
- 매장 방문 고객 가산점 적용
**데이터 저장**:
- Participation Write DB: participants, winners 테이블
### 2.3 Query Services (읽기 전용)
#### Event Query Service
**핵심 책임**:
- 이벤트 목록/상세 조회
- 이벤트 검색 및 필터링
- 읽기 최적화 데이터 제공
**이벤트 구독**:
- **EventCreated**: 읽기 DB에 이벤트 데이터 동기화
**데이터 모델**:
- **비정규화**: 조인 없이 단일 쿼리로 조회 가능
- **인덱스 최적화**: storeId, status, createdAt
**데이터 저장**:
- Event Read DB: events_view (비정규화 테이블)
#### Participation Query Service
**핵심 책임**:
- 참여자 목록 조회
- 당첨자 조회
- 집계 데이터 제공
**이벤트 구독**:
- **ParticipantRegistered**: 참여자 데이터 동기화
- **WinnerSelected**: 당첨자 데이터 동기화
**데이터 모델**:
- **집계 테이블**: 이벤트별 참여자 수, 당첨자 수 사전 계산
**데이터 저장**:
- Participation Read DB: participants_view, winners_view, event_participant_stats
#### Analytics Query Service
**핵심 책임**:
- 실시간 성과 대시보드
- 채널별 성과 분석
- ROI 계산
**관련 유저스토리**: UFR-ANAL-010
**이벤트 구독**:
- **EventCreated**: 이벤트 기본 정보 동기화
- **ParticipantRegistered**: 참여자 수 실시간 업데이트
- **DistributionCompleted**: 배포 통계 업데이트
**Resilience 패턴**:
- **Circuit Breaker**: 외부 채널 API 조회 시
- **Fallback**: 캐시된 이전 데이터 반환
- **Cache-Aside**: Redis 캐싱 (TTL 5분)
**데이터 통합**:
- Event Query Service: 이벤트 정보
- Participation Query Service: 참여자/당첨자 데이터
- Distribution Service: 배포 통계
- 외부 APIs: 우리동네TV, 지니TV, SNS 통계
**데이터 저장**:
- Analytics DB: event_stats, channel_stats
- Redis: 대시보드 데이터 (TTL 5분)
### 2.4 Async Services (비동기 처리)
#### AI Service
**핵심 책임**:
- 업종/지역/시즌 트렌드 분석
- 3가지 이벤트 기획안 자동 생성
- 예상 성과 계산
**관련 유저스토리**: UFR-AI-010
**Resilience 패턴**:
- **Circuit Breaker**: AI API 호출 시 (실패율 10% 초과 시 Open)
- **Timeout**: 30초
- **Fallback**: 캐시된 이전 추천 결과 + 안내 메시지
- **Cache-Aside**: Redis 캐싱 (TTL 24시간)
**처리 시간**:
- 캐시 HIT: 0.1초
- 캐시 MISS: 10초 이내 (비동기 Job 처리)
**데이터 저장**:
- Redis: AI 추천 결과 (TTL 24시간)
- Redis: Job 상태 정보 (TTL 1시간)
#### Content Service
**핵심 책임**:
- 3가지 스타일 SNS 이미지 자동 생성
- 플랫폼별 이미지 최적화
- 이미지 편집 기능
**관련 유저스토리**: UFR-CONT-010, 020
**Resilience 패턴**:
- **Circuit Breaker**: 이미지 생성 API 호출 시
- **Timeout**: 20초
- **Fallback**: 기본 템플릿 이미지 제공
- **Cache-Aside**: Redis 캐싱 (TTL 7일)
**처리 시간**:
- 캐시 HIT: 0.1초
- 캐시 MISS: 5초 이내 (비동기 Job 처리)
**데이터 저장**:
- Redis: 이미지 생성 결과 (CDN URL, TTL 7일)
- CDN: 생성된 이미지 파일
#### Distribution Service
**핵심 책임**:
- 다중 채널 동시 배포
- 배포 상태 모니터링
- 도메인 이벤트 발행 (DistributionCompleted)
**관련 유저스토리**: UFR-DIST-010, 020
**이벤트 구독**:
- **EventPublished**: 배포 작업 시작 트리거
**도메인 이벤트**:
- **DistributionCompleted**: 배포 완료 시
- Payload: eventId, distributedChannels, completedAt
- 구독자: Analytics Query Service
**Resilience 패턴**:
- **Circuit Breaker**: 각 외부 채널 API별 독립 적용
- **Retry**: 최대 3회 재시도 (지수 백오프)
- **Bulkhead**: 채널별 스레드 풀 격리 (장애 전파 방지)
- **Fallback**: 실패 채널 스킵 + 알림
**처리 시간**: 1분 이내 (모든 채널 배포 완료)
**데이터 저장**:
- Event Read DB: distribution_logs 테이블
### 2.5 Event-Driven 통신 전략
#### Event Bus 아키텍처
**기술 스택**: Kafka 또는 AWS SQS
**보장 수준**: At-Least-Once Delivery
**메시지 포맷**: JSON
#### 도메인 이벤트 정의
| 이벤트명 | 발행자 | 구독자 | Payload | 용도 |
|---------|--------|--------|---------|------|
| **EventCreated** | Event Command | Event Query<br/>Analytics Query | eventId, storeId, title, objective, createdAt | 이벤트 생성 동기화 |
| **EventPublished** | Event Command | Distribution Service | eventId, distributionChannels, publishedAt | 배포 작업 트리거 |
| **ParticipantRegistered** | Participation Command | Participation Query<br/>Analytics Query | participantId, eventId, phoneNumber, registeredAt | 참여자 등록 동기화 |
| **WinnerSelected** | Participation Command | Participation Query | winnerId, eventId, selectedAt | 당첨자 선정 동기화 |
| **DistributionCompleted** | Distribution Service | Analytics Query | eventId, distributedChannels, completedAt | 배포 완료 통계 업데이트 |
#### 통신 패턴별 설계
**1. Event-Driven 통신 (비동기 메시징)**
- **사용 시나리오**: 서비스 간 상태 동기화, 느슨한 결합 필요 시
- **장점**:
- 서비스 독립성 보장
- 장애 격리
- 확장 용이
- **단점**:
- 최종 일관성 (Eventual Consistency)
- 디버깅 복잡도 증가
**2. Job Queue 통신 (비동기 작업)**
- **사용 시나리오**: 장시간 작업 (AI 추천, 이미지 생성)
- **기술 스택**: RabbitMQ
- **패턴**: Asynchronous Request-Reply
- **처리 플로우**:
1. Command Service → Job Queue: Job 발행
2. Async Service → Job Queue: Job 수신 및 처리
3. Client → Command Service: Job 상태 폴링 (5초 간격)
4. Async Service → Redis: 결과 캐싱
5. Command Service → Client: 완료 응답
**3. Query Service 간 통신**
- **사용 시나리오**: Analytics Query가 다른 Query Service 데이터 필요 시
- **패턴**: Cache-Aside
- **통신 방식**: REST API (HTTP/JSON)
- **특징**: 읽기 전용이므로 직접 호출 허용
#### Cache-Aside 전략
| 서비스 | 캐시 키 패턴 | TTL | 히트율 목표 | 효과 |
|--------|-------------|-----|-----------|------|
| AI Service | `ai:recommendation:{업종}:{지역}:{목적}` | 24시간 | 80% | 10초 → 0.1초 (99% 개선) |
| Content Service | `content:image:{이벤트ID}:{스타일}` | 7일 | 80% | 5초 → 0.1초 (98% 개선) |
| User Service | `user:business:{사업자번호}` | 7일 | 90% | - |
| Analytics Query | `analytics:dashboard:{이벤트ID}` | 5분 | 95% | 3초 → 0.5초 (83% 개선) |
#### Resilience 패턴 적용
**1. Circuit Breaker 패턴**
- **적용 대상**: 모든 외부 API 호출
- **라이브러리**: Resilience4j 또는 Hystrix
- **설정**:
```yaml
circuit-breaker:
failure-rate-threshold: 50% # 실패율 50% 초과 시 Open
slow-call-rate-threshold: 50% # 느린 호출 50% 초과 시 Open
slow-call-duration-threshold: 5s # 5초 초과 시 느린 호출로 간주
wait-duration-in-open-state: 30s # Open 상태 30초 유지 후 Half-Open
permitted-calls-in-half-open: 3 # Half-Open 상태에서 3개 요청 테스트
```
**2. Retry 패턴**
- **적용 대상**: 일시적 장애가 예상되는 외부 API
- **재시도 전략**: 지수 백오프 (Exponential Backoff)
- **설정**:
```yaml
retry:
max-attempts: 3 # 최대 3회 재시도
wait-duration: 1s # 초기 대기 시간 1초
exponential-backoff-multiplier: 2 # 2배씩 증가 (1초, 2초, 4초)
retry-exceptions:
- java.net.SocketTimeoutException
- java.net.ConnectException
```
**3. Timeout 패턴**
- **적용 대상**: 모든 외부 API 호출
- **설정**:
| 서비스 | Timeout | 이유 |
|--------|---------|------|
| User Service (국세청 API) | 5초 | 빠른 검증 필요 |
| AI Service (AI API) | 30초 | 복잡한 분석 작업 |
| Content Service (이미지 API) | 20초 | 이미지 생성 시간 고려 |
| Distribution Service (채널 APIs) | 10초 | 빠른 배포 필요 |
**4. Bulkhead 패턴**
- **적용 대상**: Distribution Service (다중 채널 배포)
- **목적**: 채널별 리소스 격리로 장애 전파 차단
- **설정**:
```yaml
bulkhead:
max-concurrent-calls: 10 # 채널당 최대 10개 동시 호출
max-wait-duration: 0s # 대기 없이 즉시 실패
```
**5. Fallback 패턴**
- **적용 대상**: 모든 외부 API 호출
- **전략**:
| 서비스 | Fallback 전략 |
|--------|---------------|
| User Service | 사업자번호 검증 스킵 (수동 확인 안내) |
| AI Service | 캐시된 이전 추천 결과 + 안내 메시지 |
| Content Service | 기본 템플릿 이미지 제공 |
| Distribution Service | 실패 채널 스킵 + 알림 |
| Analytics Query | 캐시된 이전 데이터 반환 |
#### 이벤트 순서 보장
- **Kafka Partition Key**: eventId 기준으로 파티션 할당
- **동일 이벤트의 모든 이벤트**: 동일 파티션 → 순서 보장
- **다른 이벤트**: 독립적 처리 → 병렬 처리 가능
#### 이벤트 재처리 (At-Least-Once)
- **멱등성 보장**: 구독자는 동일 이벤트 중복 처리 시 멱등성 유지
- **방법**: 이벤트 ID 기반 중복 체크 (Redis Set 사용)
---
## 3. 주요 사용자 플로우
### 3.1 이벤트 생성 플로우 (CQRS + Event-Driven)
```
1. [이벤트 목적 선택]
┌─────────────────────────────────────────────────────────────┐
│ Client → Event Command Service │
│ - POST /api/events (목적, 매장 정보) │
│ - Event Write DB에 저장 │
│ - EventCreated 이벤트 발행 → Event Bus │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Event Query Service │
│ - EventCreated 이벤트 구독 │
│ - Event Read DB에 동기화 (비정규화) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Analytics Query Service │
│ - EventCreated 이벤트 구독 │
│ - Analytics DB에 기본 통계 초기화 │
└─────────────────────────────────────────────────────────────┘
2. [AI 이벤트 추천]
┌─────────────────────────────────────────────────────────────┐
│ Client → Event Command Service │
│ - POST /api/events/{id}/ai-recommendations │
│ - Job Queue 발행 (AI 작업 요청) │
│ - Job ID 즉시 반환 (0.1초) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ AI Service (Background) │
│ - Job Queue 구독 │
│ - Redis 캐시 확인 (Cache-Aside) │
│ - 캐시 MISS: Claude API 호출 (10초) [Circuit Breaker] │
│ - 결과 캐싱 (TTL 24시간) │
│ - Job 상태 완료로 업데이트 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Client (Polling) │
│ - GET /api/jobs/{id} (5초 간격) │
│ - 완료 시: AI 추천 결과 반환 (3가지 옵션) │
└─────────────────────────────────────────────────────────────┘
3. [SNS 이미지 생성]
┌─────────────────────────────────────────────────────────────┐
│ Client → Event Command Service │
│ - POST /api/events/{id}/content-generation │
│ - Job Queue 발행 (이미지 생성 요청) │
│ - Job ID 즉시 반환 (0.1초) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Content Service (Background) │
│ - Job Queue 구독 │
│ - Redis 캐시 확인 │
│ - 캐시 MISS: Stable Diffusion API (5초) [Circuit Breaker] │
│ - 이미지 CDN 업로드 │
│ - CDN URL 캐싱 (TTL 7일) │
│ - Job 상태 완료로 업데이트 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Client (Polling) │
│ - GET /api/jobs/{id} (3초 간격) │
│ - 완료 시: 3가지 스타일 이미지 URL 반환 │
└─────────────────────────────────────────────────────────────┘
4. [최종 승인 및 배포]
┌─────────────────────────────────────────────────────────────┐
│ Client → Event Command Service │
│ - POST /api/events/{id}/publish │
│ - Event 상태 변경 (DRAFT → PUBLISHED) │
│ - EventPublished 이벤트 발행 → Event Bus │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Distribution Service │
│ - EventPublished 이벤트 구독 │
│ - 다중 채널 병렬 배포 시작 [Circuit Breaker + Bulkhead] │
│ * 우리동네TV API (영상 업로드) [Retry: 3회] │
│ * 링고비즈 API (연결음 업데이트) [Retry: 3회] │
│ * 지니TV API (광고 등록) [Retry: 3회] │
│ * SNS APIs (Instagram, Naver, Kakao) [Retry: 3회] │
│ - 배포 완료: DistributionCompleted 이벤트 발행 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Analytics Query Service │
│ - DistributionCompleted 이벤트 구독 │
│ - Analytics DB 배포 통계 업데이트 │
│ - 대시보드 캐시 무효화 (다음 조회 시 갱신) │
└─────────────────────────────────────────────────────────────┘
```
### 3.2 고객 참여 플로우 (Event-Driven)
```
1. [이벤트 참여]
┌─────────────────────────────────────────────────────────────┐
│ Client → Participation Command Service │
│ - POST /api/events/{id}/participate │
│ - 중복 참여 체크 (전화번호 기반) │
│ - Participation Write DB에 저장 │
│ - ParticipantRegistered 이벤트 발행 → Event Bus │
│ - 응모 번호 즉시 반환 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Participation Query Service │
│ - ParticipantRegistered 이벤트 구독 │
│ - Participation Read DB에 동기화 │
│ - 이벤트별 참여자 수 집계 테이블 업데이트 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Analytics Query Service │
│ - ParticipantRegistered 이벤트 구독 │
│ - 실시간 참여자 수 증가 │
│ - 대시보드 캐시 무효화 │
└─────────────────────────────────────────────────────────────┘
2. [당첨자 추첨]
┌─────────────────────────────────────────────────────────────┐
│ Client → Participation Command Service │
│ - POST /api/events/{id}/draw-winners │
│ - 난수 기반 무작위 추첨 │
│ - Winners Write DB에 저장 │
│ - WinnerSelected 이벤트 발행 → Event Bus │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Participation Query Service │
│ - WinnerSelected 이벤트 구독 │
│ - Winners Read DB에 동기화 │
└─────────────────────────────────────────────────────────────┘
```
### 3.3 성과 분석 플로우 (Query Service + Event 구독)
```
1. [실시간 대시보드 조회]
┌─────────────────────────────────────────────────────────────┐
│ Client → Analytics Query Service │
│ - GET /api/events/{id}/analytics │
│ - Redis 캐시 확인 (TTL 5분) │
│ * 캐시 HIT: 즉시 반환 (0.5초) │
│ * 캐시 MISS: 아래 데이터 통합 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Analytics Query Service (데이터 통합) │
│ - Analytics DB: 이벤트 통계 조회 │
│ - Event Query Service: 이벤트 정보 조회 (REST) │
│ - Participation Query Service: 참여자/당첨자 조회 (REST) │
│ - 외부 APIs: 채널별 노출/클릭 수 [Circuit Breaker + Fallback] │
│ * 우리동네TV API (조회수) │
│ * 지니TV API (광고 노출 수) │
│ * SNS APIs (좋아요, 댓글, 공유 수) │
│ - Redis 캐싱 (TTL 5분) │
│ - 대시보드 데이터 반환 │
└─────────────────────────────────────────────────────────────┘
2. [실시간 업데이트 (Event 구독)]
┌─────────────────────────────────────────────────────────────┐
│ Analytics Query Service (Background) │
│ - EventCreated 구독: 이벤트 기본 정보 초기화 │
│ - ParticipantRegistered 구독: 참여자 수 실시간 증가 │
│ - DistributionCompleted 구독: 배포 채널 통계 업데이트 │
│ - 캐시 무효화: 다음 조회 시 최신 데이터 갱신 │
└─────────────────────────────────────────────────────────────┘
```
### 3.4 플로우 특징
#### CQRS 이점
- **Command Service**: 쓰기 작업에 집중, 트랜잭션 보장
- **Query Service**: 읽기 최적화 데이터 모델, 빠른 조회
- **독립적 확장**: 읽기/쓰기 부하에 따라 독립 스케일링
#### Event-Driven 이점
- **느슨한 결합**: 서비스 간 직접 의존성 제거
- **장애 격리**: 한 서비스 장애가 다른 서비스에 영향 없음
- **확장 용이**: 새로운 구독자 추가로 기능 확장
- **비동기 처리**: 사용자 응답 시간 단축
#### Resilience 이점
- **Circuit Breaker**: 외부 API 장애 시 빠른 실패 및 복구
- **Retry**: 일시적 장애 자동 복구
- **Fallback**: 장애 시에도 서비스 지속 (Graceful Degradation)
- **Bulkhead**: 리소스 격리로 장애 전파 차단
---
## 4. 데이터 흐름 및 캐싱 전략
### 4.1 데이터 흐름
#### 읽기 플로우 (Cache-Aside 패턴)
```
1. Application → Cache 확인
- Cache HIT: 캐시된 데이터 즉시 반환
- Cache MISS:
2. Application → Database/External API 조회
3. Database/External API → Application 데이터 반환
4. Application → Cache 데이터 저장 (TTL 설정)
5. Application → Client 데이터 반환
```
#### 쓰기 플로우 (Write-Through 패턴)
```
1. Application → Database 쓰기
2. Database → Application 성공 응답
3. Application → Cache 무효화 또는 업데이트
4. Application → Client 성공 응답
```
### 4.2 캐싱 전략
#### Redis 캐시 구조
| 서비스 | 캐시 키 패턴 | 데이터 타입 | TTL | 예상 크기 | 히트율 목표 |
|--------|-------------|-----------|-----|----------|-----------|
| User | `user:session:{token}` | String | 7일 | 1KB | - |
| User | `user:business:{사업자번호}` | String | 7일 | 0.5KB | 90% |
| AI | `ai:recommendation:{업종}:{지역}:{목적}` | Hash | 24시간 | 10KB | 80% |
| Content | `content:image:{이벤트ID}:{스타일}` | String | 7일 | 0.2KB (URL) | 80% |
| Analytics | `analytics:dashboard:{이벤트ID}` | Hash | 5분 | 5KB | 95% |
| AI | `job:{jobId}` | Hash | 1시간 | 1KB | - |
| Content | `job:{jobId}` | Hash | 1시간 | 1KB | - |
#### Redis 메모리 산정
- **예상 동시 사용자**: 100명
- **예상 이벤트 수**: 50개
- **예상 캐시 항목 수**: 10,000개
- **예상 총 메모리**: 약 50MB (운영 환경 2GB 할당)
#### 캐시 무효화 전략
- **TTL 기반 자동 만료**: 대부분의 캐시
- **수동 무효화**: 이벤트 수정/삭제 시 관련 캐시 삭제
- **Lazy 무효화**: 데이터 변경 시 다음 조회 시점에 갱신
### 4.3 데이터베이스 전략
#### 서비스별 독립 데이터베이스
- **User DB**: users, stores
- **Event DB**: events, event_objectives, event_prizes, distribution_logs
- **Participation DB**: participants, winners
- **Analytics DB**: event_stats, channel_stats
#### 데이터 일관성 전략
- **Eventual Consistency**: 서비스 간 데이터는 최종 일관성 보장
- **Strong Consistency**: 서비스 내부 트랜잭션은 강한 일관성 보장
- **Saga 패턴**: 이벤트 생성 플로우 (보상 트랜잭션)
---
## 5. 확장성 및 성능 고려사항
### 5.1 수평 확장 전략
#### 서비스별 확장 전략
| 서비스 | 초기 인스턴스 | 확장 조건 | 최대 인스턴스 | Auto-scaling 메트릭 |
|--------|-------------|----------|-------------|-------------------|
| User | 2 | CPU > 70% | 5 | CPU, 메모리 |
| Event | 2 | CPU > 70% | 10 | CPU, 메모리 |
| AI | 1 | Job Queue > 10 | 3 | Queue 길이 |
| Content | 1 | Job Queue > 10 | 3 | Queue 길이 |
| Distribution | 2 | CPU > 70% | 5 | CPU, 메모리 |
| Participation | 1 | CPU > 70% | 3 | CPU, 메모리 |
| Analytics | 1 | CPU > 70% | 3 | CPU, 메모리 |
#### Redis Cluster
- **초기 구성**: 3 노드 (Master 3, Replica 3)
- **확장**: 노드 추가를 통한 수평 확장
- **HA**: Redis Sentinel을 통한 자동 Failover
#### Database Replication
- **Primary-Replica 구조**: 읽기 부하 분산
- **읽기 확장**: Read Replica 추가 (필요 시)
- **쓰기 확장**: Sharding (Phase 2 이후)
### 5.2 성능 목표
#### 응답 시간 목표
| 기능 | 목표 시간 | 캐시 HIT | 캐시 MISS |
|------|----------|---------|----------|
| 로그인 | 0.5초 | - | - |
| 이벤트 목록 조회 | 0.3초 | - | - |
| AI 트렌드 분석 + 추천 | 0.1초 | ✅ | 10초 (비동기) |
| SNS 이미지 생성 | 0.1초 | ✅ | 5초 (비동기) |
| 다중 채널 배포 | 1분 | - | - |
| 대시보드 로딩 | 0.5초 | ✅ | 3초 |
#### 처리량 목표
- **동시 사용자**: 100명 (MVP 목표)
- **API 요청**: 1,000 req/min
- **AI 작업**: 10 jobs/min
- **이미지 생성**: 10 jobs/min
### 5.3 성능 최적화 기법
#### Frontend 최적화
- **Code Splitting**: 페이지별 번들 분할
- **Lazy Loading**: 차트 라이브러리 지연 로딩
- **CDN**: 정적 자산 CDN 배포
- **Compression**: Gzip/Brotli 압축
#### Backend 최적화
- **Connection Pooling**: 데이터베이스 연결 풀 관리
- **Query Optimization**: 인덱스 최적화, N+1 쿼리 방지
- **Batch Processing**: 대량 데이터 일괄 처리
- **Pagination**: 목록 조회 페이지네이션
#### Cache 최적화
- **Multi-Level Caching**: Browser Cache → CDN → Redis → Database
- **Cache Warming**: 자주 사용되는 데이터 사전 로딩
- **Cache Preloading**: 피크 시간 전 캐시 준비
---
## 6. 보안 고려사항
### 6.1 인증 및 인가
#### JWT 기반 인증
- **토큰 발급**: User Service에서 로그인 시 JWT 토큰 발급
- **토큰 검증**: API Gateway에서 모든 요청의 JWT 토큰 검증
- **토큰 저장**: Redis에 세션 정보 저장 (TTL 7일)
- **토큰 갱신**: Refresh Token 패턴 (선택)
#### 역할 기반 접근 제어 (RBAC)
- **역할**: OWNER (매장 사장님), CUSTOMER (이벤트 참여자)
- **권한 관리**: API별 필요 역할 정의
- **API Gateway 검증**: 요청자의 역할 확인
### 6.2 데이터 보안
#### 민감 정보 암호화
- **비밀번호**: bcrypt 해싱 (Cost Factor: 10)
- **사업자번호**: AES-256 암호화 저장
- **개인정보**: 전화번호 마스킹 (010-****-1234)
#### 전송 보안
- **HTTPS**: 모든 통신 TLS 1.3 암호화
- **API Key**: 외부 API 호출 시 안전한 Key 관리 (AWS Secrets Manager)
#### 데이터 접근 통제
- **Database**: 서비스별 독립 계정, 최소 권한 원칙
- **Redis**: 비밀번호 설정, ACL 적용
- **백업**: 암호화된 백업 저장
### 6.3 보안 모니터링
#### 위협 탐지
- **Rate Limiting**: API Gateway에서 사용자당 100 req/min
- **Brute Force 방지**: 로그인 5회 실패 시 계정 잠금 (삭제됨, 향후 추가 가능)
- **SQL Injection 방지**: Prepared Statement 사용
- **XSS 방지**: 입력 데이터 Sanitization
#### 로깅 및 감사
- **Access Log**: 모든 API 요청 로깅
- **Audit Log**: 민감 작업 (로그인, 이벤트 생성, 당첨자 추첨) 감사 로그
- **중앙집중식 로깅**: ELK Stack 또는 CloudWatch Logs
---
## 7. 논리 아키텍처 다이어그램
논리 아키텍처 다이어그램은 별도 Mermaid 파일로 작성되었습니다.
**파일 위치**: `logical-architecture.mmd`
**다이어그램 확인 방법**:
1. https://mermaid.live/edit 접속
2. `logical-architecture.mmd` 파일 내용 붙여넣기
3. 다이어그램 시각적 확인
**다이어그램 구성**:
- Client Layer: Web/Mobile Client
- Gateway Layer: API Gateway
- Service Layer: 7개 마이크로서비스
- Data Layer: Redis Cache, Message Queue, Databases
- External APIs: 7개 외부 API
**의존성 표현**:
- 실선 화살표 (→): 동기적 의존성
- 점선 화살표 (-.->): 비동기 의존성 또는 캐시 접근
- 화살표 레이블: 의존성 목적 명시
---
## 부록
### A. 참고 문서
- [유저스토리](../../userstory.md)
- [아키텍처 패턴](../../pattern/architecture-pattern.md)
- [UI/UX 설계서](../../uiux/uiux.md)
- [클라우드 디자인 패턴](../../../claude/cloud-design-patterns.md)
### B. 주요 결정사항
1. **CQRS 패턴 채택**: 읽기/쓰기 책임 분리로 독립적 확장 및 성능 최적화
2. **Event-Driven 아키텍처 채택**: Event Bus(Kafka/SQS)를 통한 서비스 간 느슨한 결합
3. **도메인 이벤트 정의**: 5개 핵심 이벤트로 서비스 간 상태 동기화
4. **Resilience 패턴 전면 적용**: Circuit Breaker, Retry, Timeout, Bulkhead, Fallback
5. **At-Least-Once Delivery**: 이벤트 보장 수준 및 멱등성 설계
6. **Cache-Aside 패턴**: AI/이미지 생성 결과 캐싱으로 응답 시간 90% 개선
7. **Job Queue 분리**: RabbitMQ로 장시간 비동기 작업 처리
8. **서비스별 독립 Database**: Command/Query별 독립 DB로 CQRS 지원
### C. 향후 개선 방안 (Phase 2 이후)
1. **Event Sourcing 완전 적용**: 모든 상태 변경을 이벤트로 저장하여 시간 여행 및 감사 추적 강화
2. **Saga 패턴 적용**: 복잡한 분산 트랜잭션 보상 로직 체계화
3. **Service Mesh 도입**: Istio를 통한 서비스 간 통신 관찰성 및 보안 강화
4. **Database Sharding**: Event/Participation Write DB 샤딩으로 쓰기 확장성 개선
5. **WebSocket 기반 실시간 푸시**: 대시보드 실시간 업데이트 (폴링 대체)
6. **GraphQL API Gateway**: 클라이언트 맞춤형 데이터 조회 최적화
7. **Dead Letter Queue 고도화**: 실패 이벤트 재처리 및 알림 자동화
---
**문서 버전**: 2.0
**최종 수정일**: 2025-10-22
**작성자**: System Architect
**변경 사항**: CQRS 패턴 및 Event-Driven 아키텍처 전환, Resilience 패턴 전면 적용

View File

@ -0,0 +1,80 @@
graph TB
%% KT AI 기반 소상공인 이벤트 자동 생성 서비스 - 논리 아키텍처 (CQRS + Event-Driven)
%% Command Services (Write)
subgraph "Command Services"
UserCmd["User Service<br/>• 회원가입/로그인<br/>• 프로필 관리<br/>• 사업자번호 검증"]
EventCmd["Event Command<br/>Service<br/>• 이벤트 생성/수정/삭제<br/>• 플로우 오케스트레이션"]
PartCmd["Participation<br/>Command Service<br/>• 참여 접수<br/>• 당첨자 추첨"]
end
%% Query Services (Read)
subgraph "Query Services"
EventQuery["Event Query<br/>Service<br/>• 이벤트 목록/상세<br/>• 이벤트 검색"]
PartQuery["Participation<br/>Query Service<br/>• 참여자 목록<br/>• 당첨자 조회"]
AnalQuery["Analytics Query<br/>Service<br/>• 실시간 대시보드<br/>• 성과 분석"]
end
%% Async Services
subgraph "Async Services"
AISvc["AI Service<br/>• 트렌드 분석<br/>• 이벤트 추천<br/>[Circuit Breaker]"]
ContentSvc["Content Service<br/>• SNS 이미지 생성<br/>• 3가지 스타일<br/>[Circuit Breaker]"]
DistSvc["Distribution<br/>Service<br/>• 다중 채널 배포<br/>[Circuit Breaker]<br/>[Retry Pattern]"]
end
%% Event Bus
EventBus["Event Bus<br/>(Kafka/SQS)<br/>━━━━━━━━━━<br/>• EventCreated<br/>• EventPublished<br/>• ParticipantRegistered<br/>• WinnerSelected<br/>• DistributionCompleted"]
%% Job Queue
JobQueue["Job Queue<br/>(RabbitMQ)<br/>━━━━━━━━━━<br/>• AI 작업 큐<br/>• 이미지 생성 큐"]
%% External System
External["외부시스템<br/>[Circuit Breaker]<br/>━━━━━━━━━━<br/>• 국세청 API<br/>• AI API<br/>• 이미지 생성 API<br/>• 배포 채널 APIs"]
%% Command to Event Bus (이벤트 발행)
EventCmd ==>|"1. EventCreated<br/>발행"| EventBus
EventCmd ==>|"2. EventPublished<br/>발행"| EventBus
PartCmd ==>|"3. ParticipantRegistered<br/>발행"| EventBus
PartCmd ==>|"4. WinnerSelected<br/>발행"| EventBus
DistSvc ==>|"5. DistributionCompleted<br/>발행"| EventBus
%% Event Bus to Services (이벤트 구독)
EventBus -.->|"EventCreated<br/>구독"| EventQuery
EventBus -.->|"EventCreated<br/>구독"| AnalQuery
EventBus -.->|"EventPublished<br/>구독"| DistSvc
EventBus -.->|"ParticipantRegistered<br/>구독"| PartQuery
EventBus -.->|"ParticipantRegistered<br/>구독"| AnalQuery
EventBus -.->|"WinnerSelected<br/>구독"| PartQuery
EventBus -.->|"DistributionCompleted<br/>구독"| AnalQuery
%% Command to Job Queue (비동기 작업)
EventCmd -->|"AI 추천 요청"| JobQueue
EventCmd -->|"이미지 생성 요청"| JobQueue
JobQueue -->|작업 처리| AISvc
JobQueue -->|작업 처리| ContentSvc
%% Query to Query (읽기 최적화)
AnalQuery -.->|캐시 조회| EventQuery
AnalQuery -.->|캐시 조회| PartQuery
%% Services to External (Resilience 패턴)
UserCmd -->|"사업자번호 검증<br/>[Circuit Breaker]<br/>[Retry: 3회]"| External
AISvc -->|"트렌드 분석/추천<br/>[Circuit Breaker]<br/>[Timeout: 30s]"| External
ContentSvc -->|"이미지 생성<br/>[Circuit Breaker]<br/>[Timeout: 20s]"| External
DistSvc -->|"다중 채널 배포<br/>[Circuit Breaker]<br/>[Retry: 3회]<br/>[Bulkhead]"| External
AnalQuery -->|"채널별 통계<br/>[Circuit Breaker]<br/>[Fallback: Cache]"| External
%% Styling
classDef command fill:#4ECDC4,stroke:#14B8A6,stroke-width:3px
classDef query fill:#10B981,stroke:#059669,stroke-width:3px
classDef async fill:#8B5CF6,stroke:#7C3AED,stroke-width:3px,color:#fff
classDef eventbus fill:#F59E0B,stroke:#D97706,stroke-width:3px
classDef jobqueue fill:#FB923C,stroke:#EA580C,stroke-width:3px
classDef external fill:#E5E7EB,stroke:#9CA3AF,stroke-width:2px
class UserCmd,EventCmd,PartCmd command
class EventQuery,PartQuery,AnalQuery query
class AISvc,ContentSvc,DistSvc async
class EventBus eventbus
class JobQueue jobqueue
class External external

View File

@ -13,6 +13,7 @@
## 버전 이력 ## 버전 이력
- **v1.0** (2025-10-21): 초기 마이크로서비스 아키텍처 설계 - **v1.0** (2025-10-21): 초기 마이크로서비스 아키텍처 설계
- **v2.0** (2025-10-22): CQRS 패턴 및 Event-Driven 아키텍처 전환, Resilience 패턴 전면 적용 - **v2.0** (2025-10-22): CQRS 패턴 및 Event-Driven 아키텍처 전환, Resilience 패턴 전면 적용
- **v2.1** (2025-10-22): 서비스 구조 간소화, Kafka 통합 (Event Bus + Job Queue), Distribution 비동기 처리
--- ---
@ -38,18 +39,18 @@
- 불필요한 추가 기능 배제 - 불필요한 추가 기능 배제
- 비즈니스 요구사항 우선 반영 - 비즈니스 요구사항 우선 반영
#### CQRS (Command Query Responsibility Segregation)
- **읽기/쓰기 분리**: Command Service와 Query Service로 책임 분리
- **독립적 확장**: 읽기와 쓰기 부하에 따라 독립적으로 확장
- **성능 최적화**: Query Service는 읽기 최적화 데이터 모델 사용
- **이벤트 소싱 준비**: 도메인 이벤트 기반 상태 동기화
#### Event-Driven 아키텍처 #### Event-Driven 아키텍처
- **비동기 메시징**: Event Bus(Kafka/SQS)를 통한 서비스 간 통신 - **Kafka 기반 통합**: Event Bus와 Job Queue를 Kafka로 통합
- **비동기 메시징**: Kafka Topics를 통한 서비스 간 통신
- **느슨한 결합**: 서비스 간 직접 의존성 제거 - **느슨한 결합**: 서비스 간 직접 의존성 제거
- **확장성**: 이벤트 구독자 추가로 기능 확장 용이 - **확장성**: 이벤트 구독자 추가로 기능 확장 용이
- **장애 격리**: 이벤트 발행/구독 실패 시 서비스 독립성 유지 - **장애 격리**: 이벤트 발행/구독 실패 시 서비스 독립성 유지
#### Kafka 통합 전략
- **Event Topics**: 도메인 이벤트 발행/구독 (EventCreated, ParticipantRegistered 등)
- **Job Topics**: 비동기 작업 요청/처리 (ai-job, image-job, distribution-job)
- **단일 메시징 플랫폼**: 운영 복잡도 감소 및 일관된 메시지 처리
#### Resilience 패턴 적용 #### Resilience 패턴 적용
- **Circuit Breaker**: 외부 API 장애 시 빠른 실패 및 복구 (Hystrix/Resilience4j) - **Circuit Breaker**: 외부 API 장애 시 빠른 실패 및 복구 (Hystrix/Resilience4j)
- **Retry Pattern**: 일시적 장애 시 자동 재시도 (지수 백오프) - **Retry Pattern**: 일시적 장애 시 자동 재시도 (지수 백오프)
@ -59,106 +60,91 @@
### 1.2 핵심 컴포넌트 정의 ### 1.2 핵심 컴포넌트 정의
#### Command Services (쓰기 전용) #### Core Services
1. **User Service**: 사용자 인증 및 매장정보 관리 1. **User Service**: 사용자 인증 및 매장정보 관리
- 회원가입/로그인 (JWT 발급) - 회원가입/로그인 (JWT 발급)
- 프로필 수정 - 프로필 CRUD
- 사업자번호 검증 (외부 API 연동) - 사업자번호 검증 (국세청 API, Circuit Breaker)
2. **Event Command Service**: 이벤트 생성/수정/삭제 2. **Event Service**: 이벤트 전체 생명주기 관리
- 이벤트 생성/수정/삭제/조회
- 이벤트 생성 플로우 오케스트레이션 - 이벤트 생성 플로우 오케스트레이션
- 도메인 이벤트 발행 (EventCreated, EventPublished) - Kafka Job 발행 (AI, 이미지, 배포)
- 이벤트 상태 변경 - Kafka Event 발행 (EventCreated)
3. **Participation Command Service**: 참여 및 당첨자 관리 3. **Participation Service**: 참여 및 당첨자 관리
- 참여 접수 및 검증 - 참여 접수 및 중복 체크
- 당첨자 추첨 실행
- 도메인 이벤트 발행 (ParticipantRegistered, WinnerSelected)
#### Query Services (읽기 전용)
1. **Event Query Service**: 이벤트 조회 최적화
- 이벤트 목록/상세 조회
- 이벤트 검색 및 필터링
- 읽기 최적화 데이터 모델 (비정규화)
2. **Participation Query Service**: 참여자/당첨자 조회
- 참여자 목록 조회 - 참여자 목록 조회
- 당첨자 조회 - 당첨자 추첨 및 조회
- 읽기 최적화 집계 데이터 - Kafka Event 발행 (ParticipantRegistered, WinnerSelected)
3. **Analytics Query Service**: 실시간 성과 분석 4. **Analytics Service**: 실시간 성과 분석 및 대시보드
- 대시보드 데이터 조회 - 대시보드 데이터 조회 (Redis 캐싱)
- 채널별 성과 집계 - Kafka Event 구독 (EventCreated, ParticipantRegistered, DistributionCompleted)
- ROI 계산 및 분석 - 외부 채널 통계 수집 (Circuit Breaker + Fallback)
- ROI 계산 및 성과 분석
#### Async Services (비동기 처리) #### Async Services (비동기 처리)
1. **AI Service**: AI 기반 이벤트 추천 1. **AI Service**: AI 기반 이벤트 추천
- Job Queue를 통한 비동기 처리 - Kafka Job 구독 (ai-job)
- Circuit Breaker 적용 (외부 AI API) - 외부 AI API 호출 (Circuit Breaker, Timeout 30초)
- 결과 캐싱 (Redis) - 결과 캐싱 (Redis, TTL 24시간)
2. **Content Service**: SNS 이미지 생성 2. **Content Service**: SNS 이미지 생성
- Job Queue를 통한 비동기 처리 - Kafka Job 구독 (image-job)
- Circuit Breaker 적용 (외부 이미지 API) - 외부 이미지 생성 API 호출 (Circuit Breaker, Timeout 20초)
- CDN 업로드 및 캐싱 - CDN 업로드 및 캐싱 (Redis, TTL 7일)
3. **Distribution Service**: 다중 채널 배포 3. **Distribution Service**: 다중 채널 배포 (비동기)
- Event Bus를 통한 EventPublished 구독 - Kafka Job 구독 (distribution-job)
- 병렬 배포 및 Circuit Breaker 적용 - 병렬 배포 (Circuit Breaker, Retry, Bulkhead)
- 배포 완료 이벤트 발행 (DistributionCompleted) - Kafka Event 발행 (DistributionCompleted)
#### Event Bus (Kafka/SQS) #### Kafka (통합 메시징 플랫폼)
- **도메인 이벤트 발행/구독**: 서비스 간 비동기 통신 **Event Topics** (도메인 이벤트):
- **이벤트 종류**: - **EventCreated**: 이벤트 생성 시
- EventCreated: 이벤트 생성 시 - **ParticipantRegistered**: 참여자 등록 시
- EventPublished: 이벤트 배포 승인 시 - **WinnerSelected**: 당첨자 선정 시
- ParticipantRegistered: 참여자 등록 시 - **DistributionCompleted**: 배포 완료 시
- WinnerSelected: 당첨자 선정 시
- DistributionCompleted: 배포 완료 시
- **보장 수준**: At-Least-Once Delivery
#### Job Queue (RabbitMQ) **Job Topics** (비동기 작업):
- **장시간 비동기 작업**: AI 추천, 이미지 생성 - **ai-job**: AI 추천 작업
- **Priority Queue**: 작업 우선순위 관리 - **image-job**: 이미지 생성 작업
- **Dead Letter Queue**: 실패 작업 처리 - **distribution-job**: 배포 작업 (비동기)
**특징**:
- At-Least-Once Delivery 보장
- Partition Key 기반 순서 보장
- Dead Letter Queue 지원
#### Data Layer #### Data Layer
- **Redis Cache**: 세션, AI 결과, 이미지 URL, 대시보드 캐싱 - **Redis Cache**: 세션, AI 결과, 이미지 URL, 대시보드 캐싱
- **PostgreSQL**: 서비스별 독립 데이터베이스 - **PostgreSQL**: 서비스별 독립 데이터베이스
- User DB, Event Write DB, Event Read DB, Participation Write DB, Participation Read DB, Analytics DB - User DB, Event DB, Participation DB, Analytics DB
- **읽기 전용 복제본**: Query Service 성능 최적화
#### External Systems #### External Systems
- **국세청 API**: 사업자번호 검증 - **국세청 API**: 사업자번호 검증
- **AI APIs**: Claude/GPT-4 (트렌드 분석) - **AI APIs**: Claude/GPT-4 (트렌드 분석)
- **이미지 생성 APIs**: Stable Diffusion/DALL-E - **이미지 생성 APIs**: Stable Diffusion/DALL-E
- **배포 채널 APIs**: 우리동네TV, 링고비즈, 지니TV, SNS APIs - **배포 채널 APIs**: 우리동네TV, 링고비즈, 지니TV, SNS APIs (비동기 배포)
--- ---
## 2. 서비스 아키텍처 ## 2. 서비스 아키텍처
### 2.1 CQRS 패턴 적용 ### 2.1 서비스별 책임
#### 설계 원칙
- **Command와 Query 분리**: 쓰기와 읽기 책임을 독립된 서비스로 분리
- **독립적 확장**: 읽기/쓰기 부하에 따라 독립적으로 스케일링
- **최적화된 데이터 모델**: Query Service는 비정규화된 읽기 최적화 모델 사용
- **이벤트 동기화**: Command Service가 발행한 도메인 이벤트로 Query Service 동기화
### 2.2 Command Services (쓰기 전용)
#### User Service #### User Service
**핵심 책임**: **핵심 책임**:
- 회원가입/로그인 (JWT 토큰 발급) - 회원가입/로그인 (JWT 토큰 발급)
- 프로필 수정 (매장 정보 포함) - 프로필 CRUD (매장 정보 포함)
- 사업자번호 검증 (국세청 API 연동) - 사업자번호 검증 (국세청 API 연동)
- 세션 관리 - 세션 관리
**관련 유저스토리**: UFR-USER-010, 020, 030, 040 **관련 유저스토리**: UFR-USER-010, 020, 030, 040
**Resilience 패턴**: **Resilience 패턴**:
- **Circuit Breaker**: 국세청 API 호출 시 (실패율 5% 초과 시 Open) - **Circuit Breaker**: 국세청 API 호출 시 (실패율 50% 초과 시 Open)
- **Retry**: 최대 3회 재시도 (지수 백오프: 1초, 2초, 4초) - **Retry**: 최대 3회 재시도 (지수 백오프: 1초, 2초, 4초)
- **Timeout**: 5초 - **Timeout**: 5초
- **Fallback**: 사업자번호 검증 스킵 (수동 확인 안내) - **Fallback**: 사업자번호 검증 스킵 (수동 확인 안내)
@ -167,48 +153,51 @@
- User DB: users, stores 테이블 - User DB: users, stores 테이블
- Redis: 세션 정보 (TTL 7일), 사업자번호 검증 결과 (TTL 7일) - Redis: 세션 정보 (TTL 7일), 사업자번호 검증 결과 (TTL 7일)
#### Event Command Service #### Event Service
**핵심 책임**: **핵심 책임**:
- 이벤트 생성/수정/삭제 - 이벤트 생성/수정/삭제/조회
- 이벤트 생성 플로우 오케스트레이션 - 이벤트 생성 플로우 오케스트레이션
- 도메인 이벤트 발행 (EventCreated, EventPublished) - Kafka Job 발행 (AI, 이미지, 배포)
- Kafka Event 발행 (EventCreated)
**관련 유저스토리**: UFR-EVENT-010, 020, 030, 040, 050, 060, 070 **관련 유저스토리**: UFR-EVENT-010, 020, 030, 040, 050, 060, 070
**도메인 이벤트**: **Kafka 이벤트 발행**:
1. **EventCreated**: 이벤트 생성 완료 시 1. **EventCreated**: 이벤트 생성 완료 시
- Payload: eventId, storeId, title, objective, createdAt - Payload: eventId, storeId, title, objective, createdAt
- 구독자: Event Query Service, Analytics Query Service - 구독자: Analytics Service
2. **EventPublished**: 이벤트 배포 승인 시 **Kafka Job 발행**:
- Payload: eventId, distributionChannels, publishedAt 1. **ai-job**: AI 추천 요청
- 구독자: Distribution Service 2. **image-job**: 이미지 생성 요청
3. **distribution-job**: 배포 요청 (비동기)
**주요 플로우**: **주요 플로우**:
1. 이벤트 목적 선택 → Event DB 저장 1. 이벤트 목적 선택 → Event DB 저장 → EventCreated 발행
2. AI 추천 요청 → Job Queue 발행 2. AI 추천 요청 → ai-job 발행
3. 이미지 생성 요청 → Job Queue 발행 3. 이미지 생성 요청 → image-job 발행
4. 배포 승인 → EventPublished 이벤트 발행 4. 배포 승인 → distribution-job 발행 (비동기)
**데이터 저장**: **데이터 저장**:
- Event Write DB: events, event_objectives, event_prizes 테이블 - Event DB: events, event_objectives, event_prizes 테이블
#### Participation Command Service #### Participation Service
**핵심 책임**: **핵심 책임**:
- 이벤트 참여 접수 및 검증 - 이벤트 참여 접수 및 검증
- 당첨자 추첨 실행 - 참여자 목록 조회
- 도메인 이벤트 발행 (ParticipantRegistered, WinnerSelected) - 당첨자 추첨 및 조회
- Kafka Event 발행 (ParticipantRegistered, WinnerSelected)
**관련 유저스토리**: UFR-PART-010, 020, 030 **관련 유저스토리**: UFR-PART-010, 020, 030
**도메인 이벤트**: **Kafka 이벤트 발행**:
1. **ParticipantRegistered**: 참여자 등록 시 1. **ParticipantRegistered**: 참여자 등록 시
- Payload: participantId, eventId, phoneNumber, registeredAt - Payload: participantId, eventId, phoneNumber, registeredAt
- 구독자: Participation Query Service, Analytics Query Service - 구독자: Analytics Service
2. **WinnerSelected**: 당첨자 선정 시 2. **WinnerSelected**: 당첨자 선정 시
- Payload: winnerId, eventId, selectedAt - Payload: winnerId, eventId, selectedAt
- 구독자: Participation Query Service - 구독자: (추후 확장 가능)
**주요 기능**: **주요 기능**:
- 중복 참여 체크 (전화번호 기반) - 중복 참여 체크 (전화번호 기반)
@ -216,71 +205,36 @@
- 매장 방문 고객 가산점 적용 - 매장 방문 고객 가산점 적용
**데이터 저장**: **데이터 저장**:
- Participation Write DB: participants, winners 테이블 - Participation DB: participants, winners 테이블
### 2.3 Query Services (읽기 전용) #### Analytics Service
#### Event Query Service
**핵심 책임**: **핵심 책임**:
- 이벤트 목록/상세 조회 - 실시간 성과 대시보드 조회
- 이벤트 검색 및 필터링 - 채널별 성과 분석 및 통계
- 읽기 최적화 데이터 제공 - ROI 계산 및 성과 집계
**이벤트 구독**:
- **EventCreated**: 읽기 DB에 이벤트 데이터 동기화
**데이터 모델**:
- **비정규화**: 조인 없이 단일 쿼리로 조회 가능
- **인덱스 최적화**: storeId, status, createdAt
**데이터 저장**:
- Event Read DB: events_view (비정규화 테이블)
#### Participation Query Service
**핵심 책임**:
- 참여자 목록 조회
- 당첨자 조회
- 집계 데이터 제공
**이벤트 구독**:
- **ParticipantRegistered**: 참여자 데이터 동기화
- **WinnerSelected**: 당첨자 데이터 동기화
**데이터 모델**:
- **집계 테이블**: 이벤트별 참여자 수, 당첨자 수 사전 계산
**데이터 저장**:
- Participation Read DB: participants_view, winners_view, event_participant_stats
#### Analytics Query Service
**핵심 책임**:
- 실시간 성과 대시보드
- 채널별 성과 분석
- ROI 계산
**관련 유저스토리**: UFR-ANAL-010 **관련 유저스토리**: UFR-ANAL-010
**이벤트 구독**: **Kafka 이벤트 구독**:
- **EventCreated**: 이벤트 기본 정보 기화 - **EventCreated**: 이벤트 기본 정보 초기화
- **ParticipantRegistered**: 참여자 수 실시간 업데이트 - **ParticipantRegistered**: 참여자 수 실시간 증가
- **DistributionCompleted**: 배포 통계 업데이트 - **DistributionCompleted**: 배포 완료 통계 업데이트
**Resilience 패턴**: **Resilience 패턴**:
- **Circuit Breaker**: 외부 채널 API 조회 시 - **Circuit Breaker**: 외부 채널 API 조회 시 (실패율 50% 초과 시 Open)
- **Fallback**: 캐시된 이전 데이터 반환 - **Fallback**: 캐시된 이전 데이터 반환
- **Cache-Aside**: Redis 캐싱 (TTL 5분) - **Cache-Aside**: Redis 캐싱 (TTL 5분)
**데이터 통합**: **데이터 통합**:
- Event Query Service: 이벤트 정보 - Event Service: 이벤트 정보 조회 (DB 직접 또는 REST)
- Participation Query Service: 참여자/당첨자 데이터 - Participation Service: 참여자/당첨자 데이터 조회
- Distribution Service: 배포 통계 - 외부 APIs: 우리동네TV, 지니TV, SNS 통계 수집
- 외부 APIs: 우리동네TV, 지니TV, SNS 통계
**데이터 저장**: **데이터 저장**:
- Analytics DB: event_stats, channel_stats - Analytics DB: event_stats, channel_stats
- Redis: 대시보드 데이터 (TTL 5분) - Redis: 대시보드 데이터 (TTL 5분)
### 2.4 Async Services (비동기 처리) ### 2.2 Async Services (비동기 처리)
#### AI Service #### AI Service
**핵심 책임**: **핵심 책임**:
@ -290,15 +244,18 @@
**관련 유저스토리**: UFR-AI-010 **관련 유저스토리**: UFR-AI-010
**Kafka Job 구독**:
- **ai-job**: AI 추천 작업 요청
**Resilience 패턴**: **Resilience 패턴**:
- **Circuit Breaker**: AI API 호출 시 (실패율 10% 초과 시 Open) - **Circuit Breaker**: AI API 호출 시 (실패율 50% 초과 시 Open)
- **Timeout**: 30초 - **Timeout**: 30초
- **Fallback**: 캐시된 이전 추천 결과 + 안내 메시지 - **Fallback**: 캐시된 이전 추천 결과 + 안내 메시지
- **Cache-Aside**: Redis 캐싱 (TTL 24시간) - **Cache-Aside**: Redis 캐싱 (TTL 24시간)
**처리 시간**: **처리 시간**:
- 캐시 HIT: 0.1초 - 캐시 HIT: 0.1초
- 캐시 MISS: 10초 이내 (비동기 Job 처리) - 캐시 MISS: 10초 이내 (비동기 처리)
**데이터 저장**: **데이터 저장**:
- Redis: AI 추천 결과 (TTL 24시간) - Redis: AI 추천 결과 (TTL 24시간)
@ -312,15 +269,18 @@
**관련 유저스토리**: UFR-CONT-010, 020 **관련 유저스토리**: UFR-CONT-010, 020
**Kafka Job 구독**:
- **image-job**: 이미지 생성 작업 요청
**Resilience 패턴**: **Resilience 패턴**:
- **Circuit Breaker**: 이미지 생성 API 호출 시 - **Circuit Breaker**: 이미지 생성 API 호출 시 (실패율 50% 초과 시 Open)
- **Timeout**: 20초 - **Timeout**: 20초
- **Fallback**: 기본 템플릿 이미지 제공 - **Fallback**: 기본 템플릿 이미지 제공
- **Cache-Aside**: Redis 캐싱 (TTL 7일) - **Cache-Aside**: Redis 캐싱 (TTL 7일)
**처리 시간**: **처리 시간**:
- 캐시 HIT: 0.1초 - 캐시 HIT: 0.1초
- 캐시 MISS: 5초 이내 (비동기 Job 처리) - 캐시 MISS: 5초 이내 (비동기 처리)
**데이터 저장**: **데이터 저장**:
- Redis: 이미지 생성 결과 (CDN URL, TTL 7일) - Redis: 이미지 생성 결과 (CDN URL, TTL 7일)
@ -328,52 +288,66 @@
#### Distribution Service #### Distribution Service
**핵심 책임**: **핵심 책임**:
- 다중 채널 동시 배포 - 다중 채널 병렬 배포 (비동기)
- 배포 상태 모니터링 - 배포 상태 모니터링
- 도메인 이벤트 발행 (DistributionCompleted) - Kafka Event 발행 (DistributionCompleted)
**관련 유저스토리**: UFR-DIST-010, 020 **관련 유저스토리**: UFR-DIST-010, 020
**이벤트 구독**: **Kafka Job 구독**:
- **EventPublished**: 배포 작업 시작 트리거 - **distribution-job**: 배포 작업 요청 (비동기)
**도메인 이벤트**: **Kafka 이벤트 발행**:
- **DistributionCompleted**: 배포 완료 시 - **DistributionCompleted**: 배포 완료 시
- Payload: eventId, distributedChannels, completedAt - Payload: eventId, distributedChannels, completedAt
- 구독자: Analytics Query Service - 구독자: Analytics Service
**Resilience 패턴**: **Resilience 패턴**:
- **Circuit Breaker**: 각 외부 채널 API별 독립 적용 - **Circuit Breaker**: 각 외부 채널 API별 독립 적용 (실패율 50% 초과 시 Open)
- **Retry**: 최대 3회 재시도 (지수 백오프) - **Retry**: 최대 3회 재시도 (지수 백오프: 1초, 2초, 4초)
- **Bulkhead**: 채널별 스레드 풀 격리 (장애 전파 방지) - **Bulkhead**: 채널별 스레드 풀 격리 (장애 전파 방지)
- **Fallback**: 실패 채널 스킵 + 알림 - **Fallback**: 실패 채널 스킵 + 알림
**처리 시간**: 1분 이내 (모든 채널 배포 완료) **처리 시간**: 1분 이내 (모든 채널 배포 완료)
**배포 채널 (비동기)**:
- 우리동네TV API (영상 업로드)
- 링고비즈 API (연결음 업데이트)
- 지니TV API (TV 광고 등록)
- SNS APIs (Instagram, Naver, Kakao 자동 포스팅)
**데이터 저장**: **데이터 저장**:
- Event Read DB: distribution_logs 테이블 - Event DB: distribution_logs 테이블
### 2.5 Event-Driven 통신 전략 ### 2.3 Kafka 통신 전략
#### Event Bus 아키텍처 #### Kafka 아키텍처
**기술 스택**: Kafka 또는 AWS SQS **기술 스택**: Apache Kafka (Event Topics + Job Topics 통합)
**보장 수준**: At-Least-Once Delivery **보장 수준**: At-Least-Once Delivery
**메시지 포맷**: JSON **메시지 포맷**: JSON
#### 도메인 이벤트 정의 #### Event Topics (도메인 이벤트)
| 이벤트명 | 발행자 | 구독자 | Payload | 용도 | | 토픽명 | 발행자 | 구독자 | Payload | 용도 |
|---------|--------|--------|---------|------| |---------|--------|--------|---------|------|
| **EventCreated** | Event Command | Event Query<br/>Analytics Query | eventId, storeId, title, objective, createdAt | 이벤트 생성 동기화 | | **EventCreated** | Event Service | Analytics Service | eventId, storeId, title, objective, createdAt | 이벤트 생성 시 통계 초기화 |
| **EventPublished** | Event Command | Distribution Service | eventId, distributionChannels, publishedAt | 배포 작업 트리거 | | **ParticipantRegistered** | Participation Service | Analytics Service | participantId, eventId, phoneNumber, registeredAt | 참여자 등록 시 실시간 통계 업데이트 |
| **ParticipantRegistered** | Participation Command | Participation Query<br/>Analytics Query | participantId, eventId, phoneNumber, registeredAt | 참여자 등록 동기화 | | **WinnerSelected** | Participation Service | - | winnerId, eventId, selectedAt | 당첨자 선정 기록 |
| **WinnerSelected** | Participation Command | Participation Query | winnerId, eventId, selectedAt | 당첨자 선정 동기화 | | **DistributionCompleted** | Distribution Service | Analytics Service | eventId, distributedChannels, completedAt | 배포 완료 시 통계 업데이트 |
| **DistributionCompleted** | Distribution Service | Analytics Query | eventId, distributedChannels, completedAt | 배포 완료 통계 업데이트 |
#### Job Topics (비동기 작업)
| 토픽명 | 발행자 | 구독자 | Payload | 용도 |
|---------|--------|--------|---------|------|
| **ai-job** | Event Service | AI Service | eventId, objective, industry, region | AI 트렌드 분석 및 이벤트 추천 요청 |
| **image-job** | Event Service | Content Service | eventId, content, style | SNS 이미지 생성 요청 (3가지 스타일) |
| **distribution-job** | Event Service | Distribution Service | eventId, distributionChannels | 다중 채널 배포 요청 (비동기) |
#### 통신 패턴별 설계 #### 통신 패턴별 설계
**1. Event-Driven 통신 (비동기 메시징)** **1. Event Topics (도메인 이벤트)**
- **사용 시나리오**: 서비스 간 상태 동기화, 느슨한 결합 필요 시 - **사용 시나리오**: 서비스 간 상태 변경 알림 및 동기화
- **통신 방식**: Kafka Pub/Sub
- **장점**: - **장점**:
- 서비스 독립성 보장 - 서비스 독립성 보장
- 장애 격리 - 장애 격리
@ -382,22 +356,22 @@
- 최종 일관성 (Eventual Consistency) - 최종 일관성 (Eventual Consistency)
- 디버깅 복잡도 증가 - 디버깅 복잡도 증가
**2. Job Queue 통신 (비동기 작업)** **2. Job Topics (비동기 작업)**
- **사용 시나리오**: 장시간 작업 (AI 추천, 이미지 생성) - **사용 시나리오**: 장시간 작업 (AI 추천, 이미지 생성, 다중 채널 배포)
- **기술 스택**: RabbitMQ - **통신 방식**: Kafka 메시지 큐
- **패턴**: Asynchronous Request-Reply - **패턴**: Asynchronous Request-Reply
- **처리 플로우**: - **처리 플로우**:
1. Command Service → Job Queue: Job 발행 1. Event Service → Kafka Job Topic: Job 발행
2. Async Service → Job Queue: Job 수신 및 처리 2. Async Service → Kafka: Job 수신 및 처리
3. Client → Command Service: Job 상태 폴링 (5초 간격) 3. Client → Event Service: Job 상태 폴링 (5초 간격)
4. Async Service → Redis: 결과 캐싱 4. Async Service → Redis: 결과 캐싱
5. Command Service → Client: 완료 응답 5. Event Service → Client: 완료 응답
**3. Query Service 간 통신** **3. 데이터베이스 직접 조회**
- **사용 시나리오**: Analytics Query가 다른 Query Service 데이터 필요 시 - **사용 시나리오**: Analytics Service가 이벤트/참여 데이터 필요 시
- **패턴**: Cache-Aside - **패턴**: Database-per-Service 원칙 유지, 필요 시 이벤트로 데이터 동기화
- **통신 방식**: REST API (HTTP/JSON) - **통신 방식**: Kafka 이벤트 구독 → Analytics DB 저장 → 로컬 조회
- **특징**: 읽기 전용이므로 직접 호출 허용 - **특징**: 서비스 간 직접 API 호출 최소화
#### Cache-Aside 전략 #### Cache-Aside 전략
@ -466,7 +440,7 @@
| AI Service | 캐시된 이전 추천 결과 + 안내 메시지 | | AI Service | 캐시된 이전 추천 결과 + 안내 메시지 |
| Content Service | 기본 템플릿 이미지 제공 | | Content Service | 기본 템플릿 이미지 제공 |
| Distribution Service | 실패 채널 스킵 + 알림 | | Distribution Service | 실패 채널 스킵 + 알림 |
| Analytics Query | 캐시된 이전 데이터 반환 | | Analytics Service | 캐시된 이전 데이터 반환 |
#### 이벤트 순서 보장 #### 이벤트 순서 보장
- **Kafka Partition Key**: eventId 기준으로 파티션 할당 - **Kafka Partition Key**: eventId 기준으로 파티션 할당
@ -481,40 +455,34 @@
## 3. 주요 사용자 플로우 ## 3. 주요 사용자 플로우
### 3.1 이벤트 생성 플로우 (CQRS + Event-Driven) ### 3.1 이벤트 생성 플로우 (Event-Driven + Kafka)
``` ```
1. [이벤트 목적 선택] 1. [이벤트 목적 선택]
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Client → Event Command Service │ │ Client → Event Service
│ - POST /api/events (목적, 매장 정보) │ │ - POST /api/events (목적, 매장 정보) │
│ - Event Write DB에 저장 │ │ - Event DB에 저장
│ - EventCreated 이벤트 발행 → Event Bus │ - EventCreated 이벤트 발행 → Kafka
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Event Query Service │ │ Kafka → Analytics Service │
│ - EventCreated 이벤트 구독 │
│ - Event Read DB에 동기화 (비정규화) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Analytics Query Service │
│ - EventCreated 이벤트 구독 │ │ - EventCreated 이벤트 구독 │
│ - Analytics DB에 기본 통계 초기화 │ │ - Analytics DB에 기본 통계 초기화 │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
2. [AI 이벤트 추천] 2. [AI 이벤트 추천]
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Client → Event Command Service │ │ Client → Event Service
│ - POST /api/events/{id}/ai-recommendations │ │ - POST /api/events/{id}/ai-recommendations │
│ - Job Queue 발행 (AI 작업 요청) │ - Kafka ai-job 토픽 발행 (AI 작업 요청)
│ - Job ID 즉시 반환 (0.1초) │ │ - Job ID 즉시 반환 (0.1초) │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ AI Service (Background) │ │ AI Service (Background) │
│ - Job Queue 구독 │ - Kafka ai-job 토픽 구독
│ - Redis 캐시 확인 (Cache-Aside) │ │ - Redis 캐시 확인 (Cache-Aside) │
│ - 캐시 MISS: Claude API 호출 (10초) [Circuit Breaker] │ │ - 캐시 MISS: Claude API 호출 (10초) [Circuit Breaker] │
│ - 결과 캐싱 (TTL 24시간) │ │ - 결과 캐싱 (TTL 24시간) │
@ -529,15 +497,15 @@
3. [SNS 이미지 생성] 3. [SNS 이미지 생성]
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Client → Event Command Service │ │ Client → Event Service
│ - POST /api/events/{id}/content-generation │ │ - POST /api/events/{id}/content-generation │
│ - Job Queue 발행 (이미지 생성 요청) │ - Kafka image-job 토픽 발행 (이미지 생성 요청)
│ - Job ID 즉시 반환 (0.1초) │ │ - Job ID 즉시 반환 (0.1초) │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Content Service (Background) │ │ Content Service (Background) │
│ - Job Queue 구독 │ - Kafka image-job 토픽 구독
│ - Redis 캐시 확인 │ │ - Redis 캐시 확인 │
│ - 캐시 MISS: Stable Diffusion API (5초) [Circuit Breaker] │ │ - 캐시 MISS: Stable Diffusion API (5초) [Circuit Breaker] │
│ - 이미지 CDN 업로드 │ │ - 이미지 CDN 업로드 │
@ -553,25 +521,26 @@
4. [최종 승인 및 배포] 4. [최종 승인 및 배포]
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Client → Event Command Service │ │ Client → Event Service
│ - POST /api/events/{id}/publish │ │ - POST /api/events/{id}/publish │
│ - Event 상태 변경 (DRAFT → PUBLISHED) │ │ - Event 상태 변경 (DRAFT → PUBLISHED) │
│ - EventPublished 이벤트 발행 → Event Bus │ │ - Kafka distribution-job 토픽 발행 (비동기 배포 요청) │
│ - Job ID 즉시 반환 (0.1초) │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
Event Bus → Distribution Service │ │ Distribution Service (Background)
│ - EventPublished 이벤트 구독 │ - Kafka distribution-job 토픽 구독
│ - 다중 채널 병렬 배포 시작 [Circuit Breaker + Bulkhead] │ │ - 다중 채널 병렬 배포 시작 [Circuit Breaker + Bulkhead] │
│ * 우리동네TV API (영상 업로드) [Retry: 3회] │ │ * 우리동네TV API (영상 업로드) [Retry: 3회] │
│ * 링고비즈 API (연결음 업데이트) [Retry: 3회] │ │ * 링고비즈 API (연결음 업데이트) [Retry: 3회] │
│ * 지니TV API (광고 등록) [Retry: 3회] │ │ * 지니TV API (광고 등록) [Retry: 3회] │
│ * SNS APIs (Instagram, Naver, Kakao) [Retry: 3회] │ │ * SNS APIs (Instagram, Naver, Kakao) [Retry: 3회] │
│ - 배포 완료: DistributionCompleted 이벤트 발행 │ - 배포 완료: DistributionCompleted 이벤트 발행 → Kafka
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
Event Bus → Analytics Query Service Kafka → Analytics Service
│ - DistributionCompleted 이벤트 구독 │ │ - DistributionCompleted 이벤트 구독 │
│ - Analytics DB 배포 통계 업데이트 │ │ - Analytics DB 배포 통계 업데이트 │
│ - 대시보드 캐시 무효화 (다음 조회 시 갱신) │ │ - 대시보드 캐시 무효화 (다음 조회 시 갱신) │
@ -583,50 +552,38 @@
``` ```
1. [이벤트 참여] 1. [이벤트 참여]
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Client → Participation Command Service │ │ Client → Participation Service
│ - POST /api/events/{id}/participate │ │ - POST /api/events/{id}/participate │
│ - 중복 참여 체크 (전화번호 기반) │ │ - 중복 참여 체크 (전화번호 기반) │
│ - Participation Write DB에 저장 │ │ - Participation DB에 저장
│ - ParticipantRegistered 이벤트 발행 → Event Bus │ - ParticipantRegistered 이벤트 발행 → Kafka
│ - 응모 번호 즉시 반환 │ │ - 응모 번호 즉시 반환 │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Participation Query Service │ │ Kafka → Analytics Service │
│ - ParticipantRegistered 이벤트 구독 │
│ - Participation Read DB에 동기화 │
│ - 이벤트별 참여자 수 집계 테이블 업데이트 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Analytics Query Service │
│ - ParticipantRegistered 이벤트 구독 │ │ - ParticipantRegistered 이벤트 구독 │
│ - 실시간 참여자 수 증가 │ │ - 실시간 참여자 수 증가 │
│ - Analytics DB에 참여 통계 업데이트 │
│ - 대시보드 캐시 무효화 │ │ - 대시보드 캐시 무효화 │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
2. [당첨자 추첨] 2. [당첨자 추첨]
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Client → Participation Command Service │ │ Client → Participation Service
│ - POST /api/events/{id}/draw-winners │ │ - POST /api/events/{id}/draw-winners │
│ - 난수 기반 무작위 추첨 │ │ - 난수 기반 무작위 추첨 │
│ - Winners Write DB에 저장 │ │ - Winners DB에 저장 │
│ - WinnerSelected 이벤트 발행 → Event Bus │ │ - WinnerSelected 이벤트 발행 → Kafka │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Bus → Participation Query Service │
│ - WinnerSelected 이벤트 구독 │
│ - Winners Read DB에 동기화 │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
``` ```
### 3.3 성과 분석 플로우 (Query Service + Event 구독) ### 3.3 성과 분석 플로우 (Event-Driven)
``` ```
1. [실시간 대시보드 조회] 1. [실시간 대시보드 조회]
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Client → Analytics Query Service │ │ Client → Analytics Service
│ - GET /api/events/{id}/analytics │ │ - GET /api/events/{id}/analytics │
│ - Redis 캐시 확인 (TTL 5분) │ │ - Redis 캐시 확인 (TTL 5분) │
│ * 캐시 HIT: 즉시 반환 (0.5초) │ │ * 캐시 HIT: 즉시 반환 (0.5초) │
@ -634,10 +591,8 @@
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Analytics Query Service (데이터 통합) │ │ Analytics Service (데이터 통합) │
│ - Analytics DB: 이벤트 통계 조회 │ │ - Analytics DB: 이벤트/참여 통계 조회 (로컬 DB) │
│ - Event Query Service: 이벤트 정보 조회 (REST) │
│ - Participation Query Service: 참여자/당첨자 조회 (REST) │
│ - 외부 APIs: 채널별 노출/클릭 수 [Circuit Breaker + Fallback] │ │ - 외부 APIs: 채널별 노출/클릭 수 [Circuit Breaker + Fallback] │
│ * 우리동네TV API (조회수) │ │ * 우리동네TV API (조회수) │
│ * 지니TV API (광고 노출 수) │ │ * 지니TV API (광고 노출 수) │
@ -648,7 +603,7 @@
2. [실시간 업데이트 (Event 구독)] 2. [실시간 업데이트 (Event 구독)]
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Analytics Query Service (Background) │ │ Analytics Service (Background)
│ - EventCreated 구독: 이벤트 기본 정보 초기화 │ │ - EventCreated 구독: 이벤트 기본 정보 초기화 │
│ - ParticipantRegistered 구독: 참여자 수 실시간 증가 │ │ - ParticipantRegistered 구독: 참여자 수 실시간 증가 │
│ - DistributionCompleted 구독: 배포 채널 통계 업데이트 │ │ - DistributionCompleted 구독: 배포 채널 통계 업데이트 │
@ -658,10 +613,10 @@
### 3.4 플로우 특징 ### 3.4 플로우 특징
#### CQRS 이점 #### Kafka 통합 이점
- **Command Service**: 쓰기 작업에 집중, 트랜잭션 보장 - **단일 메시징 플랫폼**: Event Bus와 Job Queue를 Kafka로 통합, 운영 복잡도 감소
- **Query Service**: 읽기 최적화 데이터 모델, 빠른 조회 - **일관된 메시지 처리**: 모든 비동기 통신이 Kafka를 통해 이루어져 모니터링 및 디버깅 용이
- **독립적 확장**: 읽기/쓰기 부하에 따라 독립 스케일링 - **확장성**: Kafka의 높은 처리량으로 대규모 이벤트 처리 지원
#### Event-Driven 이점 #### Event-Driven 이점
- **느슨한 결합**: 서비스 간 직접 의존성 제거 - **느슨한 결합**: 서비스 간 직접 의존성 제거
@ -862,16 +817,15 @@
3. 다이어그램 시각적 확인 3. 다이어그램 시각적 확인
**다이어그램 구성**: **다이어그램 구성**:
- Client Layer: Web/Mobile Client - Services: 4개 핵심 서비스 (User, Event, Participation, Analytics)
- Gateway Layer: API Gateway - Async Services: 3개 비동기 서비스 (AI, Content, Distribution)
- Service Layer: 7개 마이크로서비스 - Kafka: Event Topics + Job Topics 통합 메시징 플랫폼
- Data Layer: Redis Cache, Message Queue, Databases - External System: 통합된 외부 시스템 (국세청 API, AI API, 이미지 생성 API, 배포 채널 APIs)
- External APIs: 7개 외부 API
**의존성 표현**: **의존성 표현**:
- 실선 화살표 (→): 동기적 의존성 - 굵은 화살표 (==>): Kafka Event Topics 발행
- 점선 화살표 (-.->): 비동기 의존성 또는 캐시 접근 - 실선 화살표 (-->): Kafka Job Topics 발행 또는 외부 시스템 호출
- 화살표 레이블: 의존성 목적 명시 - 점선 화살표 (-.->): Kafka 구독
--- ---
@ -884,14 +838,15 @@
- [클라우드 디자인 패턴](../../../claude/cloud-design-patterns.md) - [클라우드 디자인 패턴](../../../claude/cloud-design-patterns.md)
### B. 주요 결정사항 ### B. 주요 결정사항
1. **CQRS 패턴 채택**: 읽기/쓰기 책임 분리로 독립적 확장 및 성능 최적화 1. **Kafka 통합 메시징 플랫폼 채택**: Event Bus와 Job Queue를 Kafka로 통합하여 운영 복잡도 감소
2. **Event-Driven 아키텍처 채택**: Event Bus(Kafka/SQS)를 통한 서비스 간 느슨한 결합 2. **Event-Driven 아키텍처 채택**: Kafka를 통한 서비스 간 느슨한 결합 및 비동기 통신
3. **도메인 이벤트 정의**: 5개 핵심 이벤트로 서비스 간 상태 동기화 3. **도메인 이벤트 정의**: 4개 Event Topics (EventCreated, ParticipantRegistered, WinnerSelected, DistributionCompleted)
4. **Resilience 패턴 전면 적용**: Circuit Breaker, Retry, Timeout, Bulkhead, Fallback 4. **Job Topics 정의**: 3개 Job Topics (ai-job, image-job, distribution-job)로 비동기 작업 처리
5. **At-Least-Once Delivery**: 이벤트 보장 수준 및 멱등성 설계 5. **Resilience 패턴 전면 적용**: Circuit Breaker, Retry, Timeout, Bulkhead, Fallback
6. **Cache-Aside 패턴**: AI/이미지 생성 결과 캐싱으로 응답 시간 90% 개선 6. **At-Least-Once Delivery**: Kafka 메시지 보장 및 멱등성 설계
7. **Job Queue 분리**: RabbitMQ로 장시간 비동기 작업 처리 7. **Cache-Aside 패턴**: AI/이미지 생성 결과 캐싱으로 응답 시간 90% 개선
8. **서비스별 독립 Database**: Command/Query별 독립 DB로 CQRS 지원 8. **비동기 배포**: Distribution Service가 distribution-job을 통해 다중 채널 배포 비동기 처리
9. **서비스별 독립 Database**: Database-per-Service 패턴으로 서비스 독립성 보장
### C. 향후 개선 방안 (Phase 2 이후) ### C. 향후 개선 방안 (Phase 2 이후)
1. **Event Sourcing 완전 적용**: 모든 상태 변경을 이벤트로 저장하여 시간 여행 및 감사 추적 강화 1. **Event Sourcing 완전 적용**: 모든 상태 변경을 이벤트로 저장하여 시간 여행 및 감사 추적 강화
@ -904,7 +859,7 @@
--- ---
**문서 버전**: 2.0 **문서 버전**: 2.1
**최종 수정일**: 2025-10-22 **최종 수정일**: 2025-10-22
**작성자**: System Architect **작성자**: System Architect
**변경 사항**: CQRS 패턴 및 Event-Driven 아키텍처 전환, Resilience 패턴 전면 적용 **변경 사항**: 서비스 구조 간소화, Kafka 통합 (Event Bus + Job Queue), Distribution 비동기 처리

View File

@ -1,80 +1,62 @@
graph TB graph TB
%% KT AI 기반 소상공인 이벤트 자동 생성 서비스 - 논리 아키텍처 (CQRS + Event-Driven) %% KT AI 기반 소상공인 이벤트 자동 생성 서비스 - 논리 아키텍처 (Event-Driven + Kafka)
%% Command Services (Write) %% Services
subgraph "Command Services" subgraph "Services"
UserCmd["User Service<br/>• 회원가입/로그인<br/>• 프로필 관리<br/>• 사업자번호 검증"] UserSvc["User Service<br/>• 회원가입/로그인<br/>• 프로필 관리<br/>• 사업자번호 검증<br/>[Circuit Breaker]"]
EventCmd["Event Command<br/>Service<br/>• 이벤트 생성/수정/삭제<br/>• 플로우 오케스트레이션"] EventSvc["Event Service<br/>• 이벤트 생성/수정/삭제<br/>• 플로우 오케스트레이션<br/>• AI/이미지 작업 요청<br/>• 배포 작업 요청"]
PartCmd["Participation<br/>Command Service<br/>• 참여 접수<br/>• 당첨자 추첨"] PartSvc["Participation<br/>Service<br/>• 참여 접수<br/>• 당첨자 추첨"]
end AnalSvc["Analytics Service<br/>• 실시간 대시보드<br/>• 성과 분석<br/>• 채널별 통계<br/>[Circuit Breaker]"]
%% Query Services (Read)
subgraph "Query Services"
EventQuery["Event Query<br/>Service<br/>• 이벤트 목록/상세<br/>• 이벤트 검색"]
PartQuery["Participation<br/>Query Service<br/>• 참여자 목록<br/>• 당첨자 조회"]
AnalQuery["Analytics Query<br/>Service<br/>• 실시간 대시보드<br/>• 성과 분석"]
end end
%% Async Services %% Async Services
subgraph "Async Services" subgraph "Async Services"
AISvc["AI Service<br/>• 트렌드 분석<br/>• 이벤트 추천<br/>[Circuit Breaker]"] AISvc["AI Service<br/>• 트렌드 분석<br/>• 이벤트 추천<br/>[Circuit Breaker]<br/>[Timeout: 30s]"]
ContentSvc["Content Service<br/>• SNS 이미지 생성<br/>• 3가지 스타일<br/>[Circuit Breaker]"] ContentSvc["Content Service<br/>• SNS 이미지 생성<br/>• 3가지 스타일<br/>[Circuit Breaker]<br/>[Timeout: 20s]"]
DistSvc["Distribution<br/>Service<br/>• 다중 채널 배포<br/>[Circuit Breaker]<br/>[Retry Pattern]"] DistSvc["Distribution<br/>Service<br/>• 다중 채널 배포<br/>[Circuit Breaker]<br/>[Retry: 3회]<br/>[Bulkhead]"]
end end
%% Event Bus %% Kafka (Event Bus + Job Queue)
EventBus["Event Bus<br/>(Kafka/SQS)<br/>━━━━━━━━━━<br/>• EventCreated<br/>• EventPublished<br/>• ParticipantRegistered<br/>• WinnerSelected<br/>• DistributionCompleted"] Kafka["Kafka<br/>━━━━━━━━━━<br/><Event Topics><br/>• EventCreated<br/>• ParticipantRegistered<br/>• WinnerSelected<br/>• DistributionCompleted<br/>━━━━━━━━━━<br/><Job Topics><br/>• ai-job<br/>• image-job<br/>• distribution-job"]
%% Job Queue
JobQueue["Job Queue<br/>(RabbitMQ)<br/>━━━━━━━━━━<br/>• AI 작업 큐<br/>• 이미지 생성 큐"]
%% External System %% External System
External["외부시스템<br/>[Circuit Breaker]<br/>━━━━━━━━━━<br/>• 국세청 API<br/>• AI API<br/>• 이미지 생성 API<br/>• 배포 채널 APIs"] External["외부시스템<br/>[Circuit Breaker]<br/>━━━━━━━━━━<br/>• 국세청 API<br/>• AI API<br/>• 이미지 생성 API<br/>• 배포 채널 APIs<br/>(비동기)"]
%% Command to Event Bus (이벤트 발행) %% Event Publishing
EventCmd ==>|"1. EventCreated<br/>발행"| EventBus EventSvc ==>|"EventCreated<br/>발행"| Kafka
EventCmd ==>|"2. EventPublished<br/>발행"| EventBus PartSvc ==>|"ParticipantRegistered<br/>발행"| Kafka
PartCmd ==>|"3. ParticipantRegistered<br/>발행"| EventBus PartSvc ==>|"WinnerSelected<br/>발행"| Kafka
PartCmd ==>|"4. WinnerSelected<br/>발행"| EventBus DistSvc ==>|"DistributionCompleted<br/>발행"| Kafka
DistSvc ==>|"5. DistributionCompleted<br/>발행"| EventBus
%% Event Bus to Services (이벤트 구독) %% Job Publishing (비동기 작업 요청)
EventBus -.->|"EventCreated<br/>구독"| EventQuery EventSvc -->|"ai-job 발행"| Kafka
EventBus -.->|"EventCreated<br/>구독"| AnalQuery EventSvc -->|"image-job 발행"| Kafka
EventBus -.->|"EventPublished<br/>구독"| DistSvc EventSvc -->|"distribution-job<br/>발행 (비동기)"| Kafka
EventBus -.->|"ParticipantRegistered<br/>구독"| PartQuery
EventBus -.->|"ParticipantRegistered<br/>구독"| AnalQuery
EventBus -.->|"WinnerSelected<br/>구독"| PartQuery
EventBus -.->|"DistributionCompleted<br/>구독"| AnalQuery
%% Command to Job Queue (비동기 작업) %% Event Subscription
EventCmd -->|"AI 추천 요청"| JobQueue Kafka -.->|"EventCreated<br/>구독"| AnalSvc
EventCmd -->|"이미지 생성 요청"| JobQueue Kafka -.->|"ParticipantRegistered<br/>구독"| AnalSvc
JobQueue -->|작업 처리| AISvc Kafka -.->|"DistributionCompleted<br/>구독"| AnalSvc
JobQueue -->|작업 처리| ContentSvc
%% Query to Query (읽기 최적화) %% Job Subscription
AnalQuery -.->|캐시 조회| EventQuery Kafka -.->|"ai-job 구독"| AISvc
AnalQuery -.->|캐시 조회| PartQuery Kafka -.->|"image-job 구독"| ContentSvc
Kafka -.->|"distribution-job<br/>구독 (비동기)"| DistSvc
%% Services to External (Resilience 패턴) %% Services to External (Resilience 패턴)
UserCmd -->|"사업자번호 검증<br/>[Circuit Breaker]<br/>[Retry: 3회]"| External UserSvc -->|"사업자번호 검증<br/>[Retry: 3회]"| External
AISvc -->|"트렌드 분석/추천<br/>[Circuit Breaker]<br/>[Timeout: 30s]"| External AISvc -->|"트렌드 분석/추천"| External
ContentSvc -->|"이미지 생성<br/>[Circuit Breaker]<br/>[Timeout: 20s]"| External ContentSvc -->|"이미지 생성"| External
DistSvc -->|"다중 채널 배포<br/>[Circuit Breaker]<br/>[Retry: 3회]<br/>[Bulkhead]"| External DistSvc -->|"다중 채널 배포<br/>(비동기)"| External
AnalQuery -->|"채널별 통계<br/>[Circuit Breaker]<br/>[Fallback: Cache]"| External AnalSvc -->|"채널별 통계<br/>[Fallback: Cache]"| External
%% Styling %% Styling
classDef command fill:#4ECDC4,stroke:#14B8A6,stroke-width:3px classDef service fill:#4ECDC4,stroke:#14B8A6,stroke-width:3px
classDef query fill:#10B981,stroke:#059669,stroke-width:3px
classDef async fill:#8B5CF6,stroke:#7C3AED,stroke-width:3px,color:#fff classDef async fill:#8B5CF6,stroke:#7C3AED,stroke-width:3px,color:#fff
classDef eventbus fill:#F59E0B,stroke:#D97706,stroke-width:3px classDef kafka fill:#F59E0B,stroke:#D97706,stroke-width:3px
classDef jobqueue fill:#FB923C,stroke:#EA580C,stroke-width:3px
classDef external fill:#E5E7EB,stroke:#9CA3AF,stroke-width:2px classDef external fill:#E5E7EB,stroke:#9CA3AF,stroke-width:2px
class UserCmd,EventCmd,PartCmd command class UserSvc,EventSvc,PartSvc,AnalSvc service
class EventQuery,PartQuery,AnalQuery query
class AISvc,ContentSvc,DistSvc async class AISvc,ContentSvc,DistSvc async
class EventBus eventbus class Kafka kafka
class JobQueue jobqueue
class External external class External external