42 KiB
KT AI 기반 소상공인 이벤트 자동 생성 서비스 - 논리 아키텍처
문서 정보
- 작성일: 2025-10-21
- 최종 수정일: 2025-10-22
- 버전: 2.0 (CQRS + Event-Driven 전환)
- 작성자: System Architect
- 관련 문서:
버전 이력
- v1.0 (2025-10-21): 초기 마이크로서비스 아키텍처 설계
- v2.0 (2025-10-22): CQRS 패턴 및 Event-Driven 아키텍처 전환, Resilience 패턴 전면 적용
목차
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 (쓰기 전용)
-
User Service: 사용자 인증 및 매장정보 관리
- 회원가입/로그인 (JWT 발급)
- 프로필 수정
- 사업자번호 검증 (외부 API 연동)
-
Event Command Service: 이벤트 생성/수정/삭제
- 이벤트 생성 플로우 오케스트레이션
- 도메인 이벤트 발행 (EventCreated, EventPublished)
- 이벤트 상태 변경
-
Participation Command Service: 참여 및 당첨자 관리
- 참여 접수 및 검증
- 당첨자 추첨 실행
- 도메인 이벤트 발행 (ParticipantRegistered, WinnerSelected)
Query Services (읽기 전용)
-
Event Query Service: 이벤트 조회 최적화
- 이벤트 목록/상세 조회
- 이벤트 검색 및 필터링
- 읽기 최적화 데이터 모델 (비정규화)
-
Participation Query Service: 참여자/당첨자 조회
- 참여자 목록 조회
- 당첨자 조회
- 읽기 최적화 집계 데이터
-
Analytics Query Service: 실시간 성과 분석
- 대시보드 데이터 조회
- 채널별 성과 집계
- ROI 계산 및 분석
Async Services (비동기 처리)
-
AI Service: AI 기반 이벤트 추천
- Job Queue를 통한 비동기 처리
- Circuit Breaker 적용 (외부 AI API)
- 결과 캐싱 (Redis)
-
Content Service: SNS 이미지 생성
- Job Queue를 통한 비동기 처리
- Circuit Breaker 적용 (외부 이미지 API)
- CDN 업로드 및 캐싱
-
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
도메인 이벤트:
-
EventCreated: 이벤트 생성 완료 시
- Payload: eventId, storeId, title, objective, createdAt
- 구독자: Event Query Service, Analytics Query Service
-
EventPublished: 이벤트 배포 승인 시
- Payload: eventId, distributionChannels, publishedAt
- 구독자: Distribution Service
주요 플로우:
- 이벤트 목적 선택 → Event DB 저장
- AI 추천 요청 → Job Queue 발행
- 이미지 생성 요청 → Job Queue 발행
- 배포 승인 → EventPublished 이벤트 발행
데이터 저장:
- Event Write DB: events, event_objectives, event_prizes 테이블
Participation Command Service
핵심 책임:
- 이벤트 참여 접수 및 검증
- 당첨자 추첨 실행
- 도메인 이벤트 발행 (ParticipantRegistered, WinnerSelected)
관련 유저스토리: UFR-PART-010, 020, 030
도메인 이벤트:
-
ParticipantRegistered: 참여자 등록 시
- Payload: participantId, eventId, phoneNumber, registeredAt
- 구독자: Participation Query Service, Analytics Query Service
-
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 Analytics Query |
eventId, storeId, title, objective, createdAt | 이벤트 생성 동기화 |
| EventPublished | Event Command | Distribution Service | eventId, distributionChannels, publishedAt | 배포 작업 트리거 |
| ParticipantRegistered | Participation Command | Participation Query 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
- 처리 플로우:
- Command Service → Job Queue: Job 발행
- Async Service → Job Queue: Job 수신 및 처리
- Client → Command Service: Job 상태 폴링 (5초 간격)
- Async Service → Redis: 결과 캐싱
- 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
- 설정:
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)
- 설정:
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 (다중 채널 배포)
- 목적: 채널별 리소스 격리로 장애 전파 차단
- 설정:
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
다이어그램 확인 방법:
- https://mermaid.live/edit 접속
logical-architecture.mmd파일 내용 붙여넣기- 다이어그램 시각적 확인
다이어그램 구성:
- Client Layer: Web/Mobile Client
- Gateway Layer: API Gateway
- Service Layer: 7개 마이크로서비스
- Data Layer: Redis Cache, Message Queue, Databases
- External APIs: 7개 외부 API
의존성 표현:
- 실선 화살표 (→): 동기적 의존성
- 점선 화살표 (-.->): 비동기 의존성 또는 캐시 접근
- 화살표 레이블: 의존성 목적 명시
부록
A. 참고 문서
B. 주요 결정사항
- CQRS 패턴 채택: 읽기/쓰기 책임 분리로 독립적 확장 및 성능 최적화
- Event-Driven 아키텍처 채택: Event Bus(Kafka/SQS)를 통한 서비스 간 느슨한 결합
- 도메인 이벤트 정의: 5개 핵심 이벤트로 서비스 간 상태 동기화
- Resilience 패턴 전면 적용: Circuit Breaker, Retry, Timeout, Bulkhead, Fallback
- At-Least-Once Delivery: 이벤트 보장 수준 및 멱등성 설계
- Cache-Aside 패턴: AI/이미지 생성 결과 캐싱으로 응답 시간 90% 개선
- Job Queue 분리: RabbitMQ로 장시간 비동기 작업 처리
- 서비스별 독립 Database: Command/Query별 독립 DB로 CQRS 지원
C. 향후 개선 방안 (Phase 2 이후)
- Event Sourcing 완전 적용: 모든 상태 변경을 이벤트로 저장하여 시간 여행 및 감사 추적 강화
- Saga 패턴 적용: 복잡한 분산 트랜잭션 보상 로직 체계화
- Service Mesh 도입: Istio를 통한 서비스 간 통신 관찰성 및 보안 강화
- Database Sharding: Event/Participation Write DB 샤딩으로 쓰기 확장성 개선
- WebSocket 기반 실시간 푸시: 대시보드 실시간 업데이트 (폴링 대체)
- GraphQL API Gateway: 클라이언트 맞춤형 데이터 조회 최적화
- Dead Letter Queue 고도화: 실패 이벤트 재처리 및 알림 자동화
문서 버전: 2.0 최종 수정일: 2025-10-22 작성자: System Architect 변경 사항: CQRS 패턴 및 Event-Driven 아키텍처 전환, Resilience 패턴 전면 적용