mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2025-12-06 17:26:23 +00:00
- Analytics 서비스 구현 추가 (API, 소스 코드) - Event 서비스 소스 코드 추가 - 보안 관련 공통 컴포넌트 업데이트 (JWT, UserPrincipal, ErrorCode) - API 컨벤션 및 명세서 업데이트 - 데이터베이스 SQL 스크립트 추가 - 백엔드 개발 문서 및 테스트 가이드 추가 - Kafka 메시지 체크 도구 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
698 lines
22 KiB
Markdown
698 lines
22 KiB
Markdown
# Analytics 서비스 백엔드 개발 결과서
|
|
|
|
## 1. 개요
|
|
|
|
### 1.1 서비스 정보
|
|
- **서비스명**: Analytics Service
|
|
- **포트**: 8086
|
|
- **프레임워크**: Spring Boot 3.3.0
|
|
- **언어**: Java 21
|
|
- **빌드 도구**: Gradle 8.10
|
|
- **아키텍처 패턴**: Layered Architecture
|
|
|
|
### 1.2 주요 기능
|
|
1. **이벤트 성과 대시보드**: 이벤트별 통합 성과 데이터 제공
|
|
2. **채널별 성과 분석**: 각 배포 채널별 상세 성과 분석
|
|
3. **타임라인 분석**: 시간대별 참여 추이 및 트렌드 분석
|
|
4. **ROI 상세 분석**: 투자 대비 수익률 상세 계산
|
|
|
|
### 1.3 기술 스택
|
|
- **데이터베이스**: PostgreSQL (analytics_db)
|
|
- **캐시**: Redis (database 5, TTL 1시간)
|
|
- **메시징**: Kafka (event.created, participant.registered, distribution.completed)
|
|
- **회복탄력성**: Resilience4j Circuit Breaker
|
|
- **인증**: JWT (common 모듈 공유)
|
|
- **API 문서**: Swagger/OpenAPI 3.0
|
|
- **모니터링**: Spring Boot Actuator
|
|
|
|
---
|
|
|
|
## 2. 구현 내역
|
|
|
|
### 2.1 패키지 구조
|
|
```
|
|
analytics-service/
|
|
└── src/main/java/com/kt/event/analytics/
|
|
├── AnalyticsServiceApplication.java # 메인 애플리케이션
|
|
├── config/ # 설정 클래스
|
|
│ ├── KafkaConsumerConfig.java # Kafka Consumer 설정
|
|
│ ├── RedisConfig.java # Redis 캐시 설정
|
|
│ ├── Resilience4jConfig.java # Circuit Breaker 설정
|
|
│ ├── SecurityConfig.java # JWT 인증 설정
|
|
│ └── SwaggerConfig.java # API 문서 설정
|
|
├── controller/ # 컨트롤러 계층
|
|
│ ├── AnalyticsDashboardController.java # 대시보드 API
|
|
│ ├── ChannelAnalyticsController.java # 채널 분석 API
|
|
│ ├── RoiAnalyticsController.java # ROI 분석 API
|
|
│ └── TimelineAnalyticsController.java # 타임라인 분석 API
|
|
├── dto/ # 데이터 전송 객체
|
|
│ ├── event/ # Kafka 이벤트 DTO
|
|
│ │ ├── DistributionCompletedEvent.java
|
|
│ │ ├── EventCreatedEvent.java
|
|
│ │ └── ParticipantRegisteredEvent.java
|
|
│ └── response/ # API 응답 DTO
|
|
│ ├── AnalyticsDashboardResponse.java
|
|
│ ├── AnalyticsSummary.java
|
|
│ ├── ChannelAnalyticsResponse.java
|
|
│ ├── ChannelDetail.java
|
|
│ ├── ChannelSummary.java
|
|
│ ├── ComparisonMetrics.java
|
|
│ ├── ConversionFunnel.java
|
|
│ ├── CostAnalysis.java
|
|
│ ├── InvestmentBreakdown.java
|
|
│ ├── PeriodInfo.java
|
|
│ ├── PeakTimeInfo.java
|
|
│ ├── ProjectedMetrics.java
|
|
│ ├── RevenueBreakdown.java
|
|
│ ├── RoiAnalyticsResponse.java
|
|
│ ├── RoiProjection.java
|
|
│ ├── RoiSummary.java
|
|
│ ├── SocialInteractionStats.java
|
|
│ ├── TimelineAnalyticsResponse.java
|
|
│ ├── TimelineDataPoint.java
|
|
│ ├── TimeRangeStats.java
|
|
│ ├── TopPerformer.java
|
|
│ └── TrendAnalysis.java
|
|
├── entity/ # 엔티티 계층
|
|
│ ├── ChannelStats.java # 채널별 통계
|
|
│ ├── EventStats.java # 이벤트 통계
|
|
│ └── TimelineData.java # 타임라인 데이터
|
|
├── repository/ # 리포지토리 계층
|
|
│ ├── ChannelStatsRepository.java
|
|
│ ├── EventStatsRepository.java
|
|
│ └── TimelineDataRepository.java
|
|
├── service/ # 서비스 계층
|
|
│ ├── AnalyticsService.java # 대시보드 서비스
|
|
│ ├── ChannelAnalyticsService.java # 채널 분석 서비스
|
|
│ ├── ExternalChannelService.java # 외부 API 연동 서비스
|
|
│ ├── RoiAnalyticsService.java # ROI 분석 서비스
|
|
│ ├── ROICalculator.java # ROI 계산 유틸리티
|
|
│ └── TimelineAnalyticsService.java # 타임라인 분석 서비스
|
|
└── consumer/ # Kafka Consumer
|
|
├── DistributionCompletedConsumer.java
|
|
├── EventCreatedConsumer.java
|
|
└── ParticipantRegisteredConsumer.java
|
|
```
|
|
|
|
### 2.2 엔티티 설계
|
|
|
|
#### EventStats (이벤트 통계)
|
|
```java
|
|
@Entity
|
|
@Table(name = "event_stats")
|
|
public class EventStats {
|
|
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
private Long id;
|
|
|
|
@Column(nullable = false, unique = true)
|
|
private String eventId; // 이벤트 ID
|
|
|
|
private String eventTitle; // 이벤트 제목
|
|
private String storeId; // 매장 ID
|
|
|
|
private Integer totalParticipants = 0; // 총 참여자 수
|
|
private BigDecimal estimatedRoi = BigDecimal.ZERO; // 예상 ROI
|
|
private BigDecimal totalInvestment = BigDecimal.ZERO; // 총 투자액
|
|
|
|
@CreatedDate private LocalDateTime createdAt;
|
|
@LastModifiedDate private LocalDateTime updatedAt;
|
|
|
|
// 참여자 증가 메서드
|
|
public void incrementParticipants() {
|
|
this.totalParticipants++;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### ChannelStats (채널별 통계)
|
|
```java
|
|
@Entity
|
|
@Table(name = "channel_stats", indexes = {
|
|
@Index(name = "idx_event_id", columnList = "event_id"),
|
|
@Index(name = "idx_event_channel", columnList = "event_id,channel_name")
|
|
})
|
|
public class ChannelStats {
|
|
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
private Long id;
|
|
|
|
@Column(nullable = false)
|
|
private String eventId; // 이벤트 ID
|
|
|
|
@Column(nullable = false)
|
|
private String channelName; // 채널명 (WooriTV, GenieTV, RingoBiz, SNS)
|
|
|
|
// 성과 지표
|
|
private Integer views = 0; // 조회수
|
|
private Integer clicks = 0; // 클릭수
|
|
private Integer participants = 0; // 참여자수
|
|
private Integer conversions = 0; // 전환수
|
|
private Integer impressions = 0; // 노출수
|
|
|
|
// SNS 반응 지표
|
|
private Integer likes = 0; // 좋아요
|
|
private Integer comments = 0; // 댓글
|
|
private Integer shares = 0; // 공유
|
|
|
|
// 비용 정보
|
|
private BigDecimal distributionCost = BigDecimal.ZERO; // 배포 비용
|
|
|
|
@CreatedDate private LocalDateTime createdAt;
|
|
@LastModifiedDate private LocalDateTime updatedAt;
|
|
}
|
|
```
|
|
|
|
#### TimelineData (타임라인 데이터)
|
|
```java
|
|
@Entity
|
|
@Table(name = "timeline_data", indexes = {
|
|
@Index(name = "idx_event_timestamp", columnList = "event_id,timestamp")
|
|
})
|
|
public class TimelineData {
|
|
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
private Long id;
|
|
|
|
@Column(nullable = false)
|
|
private String eventId; // 이벤트 ID
|
|
|
|
@Column(nullable = false)
|
|
private LocalDateTime timestamp; // 시간대
|
|
|
|
private Integer participantCount = 0; // 참여자 수
|
|
private Integer cumulativeCount = 0; // 누적 참여자 수
|
|
|
|
@CreatedDate private LocalDateTime createdAt;
|
|
@LastModifiedDate private LocalDateTime updatedAt;
|
|
}
|
|
```
|
|
|
|
### 2.3 서비스 계층
|
|
|
|
#### AnalyticsService (대시보드 서비스)
|
|
- **기능**: 이벤트 성과 대시보드 데이터 통합 제공
|
|
- **캐싱**: Redis Cache-Aside 패턴, 1시간 TTL
|
|
- **캐시 키**: `analytics:dashboard:{eventId}`
|
|
- **데이터 통합**:
|
|
1. Analytics DB에서 이벤트/채널 통계 조회
|
|
2. 외부 채널 API 병렬 호출 (Circuit Breaker 적용)
|
|
3. 대시보드 데이터 구성
|
|
4. Redis 캐싱
|
|
|
|
**주요 메서드**:
|
|
```java
|
|
public AnalyticsDashboardResponse getDashboardData(
|
|
String eventId,
|
|
LocalDateTime startDate,
|
|
LocalDateTime endDate,
|
|
boolean refresh
|
|
)
|
|
```
|
|
|
|
#### ExternalChannelService (외부 API 연동)
|
|
- **기능**: 외부 채널 API 호출로 실시간 데이터 업데이트
|
|
- **패턴**: Circuit Breaker (Resilience4j)
|
|
- **지원 채널**: WooriTV, GenieTV, RingoBiz, SNS
|
|
- **병렬 처리**: CompletableFuture로 4개 채널 동시 호출
|
|
|
|
**Circuit Breaker 설정**:
|
|
- 실패율 임계값: 50%
|
|
- 대기 시간 (Open 상태): 30초
|
|
- 슬라이딩 윈도우: 10건
|
|
|
|
#### ROICalculator (ROI 계산)
|
|
- **기능**: 상세 ROI 계산 및 분석
|
|
- **투자 분류**:
|
|
- 콘텐츠 제작: 40%
|
|
- 배포 비용: 50%
|
|
- 운영 비용: 10%
|
|
- **수익 분류**:
|
|
- 직접 매출: 70%
|
|
- 간접 효과: 20%
|
|
- 브랜드 가치: 10%
|
|
- **효율성 지표**:
|
|
- CPA (Cost Per Acquisition): 참여자당 비용
|
|
- CPV (Cost Per View): 조회당 비용
|
|
- CPC (Cost Per Click): 클릭당 비용
|
|
|
|
### 2.4 컨트롤러 계층
|
|
|
|
#### 1. AnalyticsDashboardController
|
|
```java
|
|
@GetMapping("/{eventId}/analytics")
|
|
public ResponseEntity<ApiResponse<AnalyticsDashboardResponse>> getEventAnalytics(
|
|
@PathVariable String eventId,
|
|
@RequestParam(required = false) LocalDateTime startDate,
|
|
@RequestParam(required = false) LocalDateTime endDate,
|
|
@RequestParam(required = false, defaultValue = "false") Boolean refresh
|
|
)
|
|
```
|
|
|
|
#### 2. ChannelAnalyticsController
|
|
```java
|
|
@GetMapping("/{eventId}/analytics/channels")
|
|
public ResponseEntity<ApiResponse<ChannelAnalyticsResponse>> getChannelAnalytics(
|
|
@PathVariable String eventId,
|
|
@RequestParam(required = false, defaultValue = "participants") String sortBy
|
|
)
|
|
```
|
|
|
|
#### 3. TimelineAnalyticsController
|
|
```java
|
|
@GetMapping("/{eventId}/analytics/timeline")
|
|
public ResponseEntity<ApiResponse<TimelineAnalyticsResponse>> getTimelineAnalytics(
|
|
@PathVariable String eventId,
|
|
@RequestParam(required = false) LocalDateTime startDate,
|
|
@RequestParam(required = false) LocalDateTime endDate,
|
|
@RequestParam(required = false, defaultValue = "HOURLY") String granularity
|
|
)
|
|
```
|
|
|
|
#### 4. RoiAnalyticsController
|
|
```java
|
|
@GetMapping("/{eventId}/analytics/roi")
|
|
public ResponseEntity<ApiResponse<RoiAnalyticsResponse>> getRoiAnalytics(
|
|
@PathVariable String eventId,
|
|
@RequestParam(required = false, defaultValue = "false") Boolean includeProjection
|
|
)
|
|
```
|
|
|
|
### 2.5 Kafka Consumer
|
|
|
|
#### 1. EventCreatedConsumer
|
|
- **토픽**: `event.created`
|
|
- **기능**: 새 이벤트 생성 시 통계 테이블 초기화
|
|
- **처리 로직**:
|
|
```java
|
|
@KafkaListener(topics = "event.created", groupId = "analytics-service")
|
|
public void handleEventCreated(String message) {
|
|
// EventStats 초기 레코드 생성
|
|
EventStats eventStats = EventStats.builder()
|
|
.eventId(event.getEventId())
|
|
.eventTitle(event.getEventTitle())
|
|
.storeId(event.getStoreId())
|
|
.totalInvestment(event.getTotalBudget())
|
|
.build();
|
|
eventStatsRepository.save(eventStats);
|
|
}
|
|
```
|
|
|
|
#### 2. ParticipantRegisteredConsumer
|
|
- **토픽**: `participant.registered`
|
|
- **기능**: 참여자 등록 시 실시간 통계 업데이트
|
|
- **처리 로직**:
|
|
```java
|
|
@KafkaListener(topics = "participant.registered", groupId = "analytics-service")
|
|
public void handleParticipantRegistered(String message) {
|
|
// EventStats 참여자 수 증가
|
|
eventStats.incrementParticipants();
|
|
eventStatsRepository.save(eventStats);
|
|
|
|
// TimelineData 생성/업데이트
|
|
// 시간대별 참여자 추이 기록
|
|
}
|
|
```
|
|
|
|
#### 3. DistributionCompletedConsumer
|
|
- **토픽**: `distribution.completed`
|
|
- **기능**: 배포 완료 시 채널별 비용 업데이트
|
|
- **처리 로직**:
|
|
```java
|
|
@KafkaListener(topics = "distribution.completed", groupId = "analytics-service")
|
|
public void handleDistributionCompleted(String message) {
|
|
// ChannelStats 배포 비용 업데이트
|
|
channelStats.setDistributionCost(event.getDistributionCost());
|
|
channelStatsRepository.save(channelStats);
|
|
}
|
|
```
|
|
|
|
### 2.6 설정 파일
|
|
|
|
#### application.yml
|
|
```yaml
|
|
spring:
|
|
application:
|
|
name: analytics-service
|
|
|
|
# PostgreSQL 데이터베이스
|
|
datasource:
|
|
url: jdbc:postgresql://localhost:5432/analytics_db
|
|
username: analytics_user
|
|
password: analytics_pass
|
|
hikari:
|
|
maximum-pool-size: 20
|
|
minimum-idle: 5
|
|
|
|
# Redis 캐시 (database 5)
|
|
data:
|
|
redis:
|
|
host: localhost
|
|
port: 6379
|
|
database: 5
|
|
timeout: 2000ms
|
|
|
|
# Kafka
|
|
kafka:
|
|
bootstrap-servers: localhost:9092
|
|
consumer:
|
|
group-id: analytics-service
|
|
auto-offset-reset: earliest
|
|
|
|
# 서버 포트
|
|
server:
|
|
port: 8086
|
|
|
|
# Circuit Breaker
|
|
resilience4j:
|
|
circuitbreaker:
|
|
instances:
|
|
wooriTV:
|
|
failure-rate-threshold: 50
|
|
wait-duration-in-open-state: 30s
|
|
genieTV:
|
|
failure-rate-threshold: 50
|
|
wait-duration-in-open-state: 30s
|
|
ringoBiz:
|
|
failure-rate-threshold: 50
|
|
wait-duration-in-open-state: 30s
|
|
sns:
|
|
failure-rate-threshold: 50
|
|
wait-duration-in-open-state: 30s
|
|
```
|
|
|
|
---
|
|
|
|
## 3. API 명세
|
|
|
|
### 3.1 이벤트 성과 대시보드 조회
|
|
- **엔드포인트**: `GET /api/events/{eventId}/analytics`
|
|
- **파라미터**:
|
|
- `startDate` (선택): 조회 시작일
|
|
- `endDate` (선택): 조회 종료일
|
|
- `refresh` (선택, 기본값: false): 캐시 갱신 여부
|
|
- **응답**: AnalyticsDashboardResponse
|
|
- period: 기간 정보
|
|
- summary: 성과 요약 (참여자, 조회수, 도달률, 참여율, 전환율)
|
|
- channelPerformance: 채널별 성과 요약
|
|
- roi: ROI 요약
|
|
- lastUpdatedAt: 마지막 업데이트 시각
|
|
- dataSource: 데이터 출처 (cached/realtime)
|
|
|
|
### 3.2 채널별 성과 분석 조회
|
|
- **엔드포인트**: `GET /api/events/{eventId}/analytics/channels`
|
|
- **파라미터**:
|
|
- `sortBy` (선택, 기본값: participants): 정렬 기준
|
|
- **응답**: ChannelAnalyticsResponse
|
|
- channels: 채널별 상세 성과
|
|
- topPerformers: 상위 성과 채널 (조회수, 참여율, ROI 기준)
|
|
- comparison: 채널 간 비교 지표
|
|
|
|
### 3.3 타임라인 분석 조회
|
|
- **엔드포인트**: `GET /api/events/{eventId}/analytics/timeline`
|
|
- **파라미터**:
|
|
- `startDate` (선택): 조회 시작일
|
|
- `endDate` (선택): 조회 종료일
|
|
- `granularity` (선택, 기본값: HOURLY): 시간 단위
|
|
- **응답**: TimelineAnalyticsResponse
|
|
- dataPoints: 시간대별 데이터 포인트
|
|
- trends: 트렌드 분석 (성장률, 방향)
|
|
- peakTimes: 피크 시간대 정보
|
|
- timeRangeStats: 시간대별 통계
|
|
|
|
### 3.4 ROI 상세 분석 조회
|
|
- **엔드포인트**: `GET /api/events/{eventId}/analytics/roi`
|
|
- **파라미터**:
|
|
- `includeProjection` (선택, 기본값: false): 예측 포함 여부
|
|
- **응답**: RoiAnalyticsResponse
|
|
- summary: ROI 요약 (총 ROI, 투자액, 수익)
|
|
- investment: 투자 내역 (콘텐츠, 배포, 운영)
|
|
- revenue: 수익 내역 (직접 매출, 간접 효과, 브랜드 가치)
|
|
- costAnalysis: 비용 효율성 분석 (CPA, CPV, CPC)
|
|
- conversionFunnel: 전환 퍼널 (조회 → 클릭 → 참여 → 전환)
|
|
- projection: ROI 예측 (선택)
|
|
|
|
---
|
|
|
|
## 4. 데이터베이스 스키마
|
|
|
|
### 4.1 event_stats (이벤트 통계)
|
|
```sql
|
|
CREATE TABLE event_stats (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
event_id VARCHAR(255) NOT NULL UNIQUE,
|
|
event_title VARCHAR(500),
|
|
store_id VARCHAR(255),
|
|
total_participants INT DEFAULT 0,
|
|
estimated_roi DECIMAL(10,2) DEFAULT 0,
|
|
total_investment DECIMAL(15,2) DEFAULT 0,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
### 4.2 channel_stats (채널별 통계)
|
|
```sql
|
|
CREATE TABLE channel_stats (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
event_id VARCHAR(255) NOT NULL,
|
|
channel_name VARCHAR(50) NOT NULL,
|
|
views INT DEFAULT 0,
|
|
clicks INT DEFAULT 0,
|
|
participants INT DEFAULT 0,
|
|
conversions INT DEFAULT 0,
|
|
impressions INT DEFAULT 0,
|
|
likes INT DEFAULT 0,
|
|
comments INT DEFAULT 0,
|
|
shares INT DEFAULT 0,
|
|
distribution_cost DECIMAL(15,2) DEFAULT 0,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_event_id ON channel_stats(event_id);
|
|
CREATE INDEX idx_event_channel ON channel_stats(event_id, channel_name);
|
|
```
|
|
|
|
### 4.3 timeline_data (타임라인 데이터)
|
|
```sql
|
|
CREATE TABLE timeline_data (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
event_id VARCHAR(255) NOT NULL,
|
|
timestamp TIMESTAMP NOT NULL,
|
|
participant_count INT DEFAULT 0,
|
|
cumulative_count INT DEFAULT 0,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_event_timestamp ON timeline_data(event_id, timestamp);
|
|
```
|
|
|
|
---
|
|
|
|
## 5. 빌드 및 테스트
|
|
|
|
### 5.1 빌드 결과
|
|
```
|
|
./gradlew analytics-service:build
|
|
|
|
BUILD SUCCESSFUL in 19s
|
|
10 actionable tasks: 6 executed, 4 up-to-date
|
|
```
|
|
|
|
### 5.2 컴파일 결과
|
|
```
|
|
./gradlew analytics-service:compileJava
|
|
|
|
BUILD SUCCESSFUL in 14s
|
|
```
|
|
|
|
### 5.3 생성된 아티팩트
|
|
- **JAR 파일**: `analytics-service/build/libs/analytics-service.jar`
|
|
- **Boot JAR 파일**: `analytics-service/build/libs/analytics-service-boot.jar`
|
|
|
|
---
|
|
|
|
## 6. 실행 방법
|
|
|
|
### 6.1 사전 준비
|
|
1. PostgreSQL 실행 (포트: 5432)
|
|
- 데이터베이스: analytics_db
|
|
- 사용자: analytics_user
|
|
|
|
2. Redis 실행 (포트: 6379)
|
|
- Database: 5
|
|
|
|
3. Kafka 실행 (포트: 9092)
|
|
- 토픽: event.created, participant.registered, distribution.completed
|
|
|
|
### 6.2 환경 변수 설정
|
|
```bash
|
|
# 데이터베이스
|
|
DB_HOST=localhost
|
|
DB_PORT=5432
|
|
DB_NAME=analytics_db
|
|
DB_USERNAME=analytics_user
|
|
DB_PASSWORD=analytics_pass
|
|
|
|
# Redis
|
|
REDIS_HOST=localhost
|
|
REDIS_PORT=6379
|
|
REDIS_DATABASE=5
|
|
|
|
# Kafka
|
|
KAFKA_BOOTSTRAP_SERVERS=localhost:9092
|
|
|
|
# 서버
|
|
SERVER_PORT=8086
|
|
|
|
# JWT (common 모듈과 공유)
|
|
JWT_SECRET=your-secret-key
|
|
```
|
|
|
|
### 6.3 서비스 실행
|
|
```bash
|
|
java -jar analytics-service/build/libs/analytics-service-boot.jar
|
|
```
|
|
|
|
### 6.4 헬스 체크
|
|
```bash
|
|
curl http://localhost:8086/actuator/health
|
|
```
|
|
|
|
### 6.5 API 문서 확인
|
|
- Swagger UI: http://localhost:8086/swagger-ui.html
|
|
- OpenAPI Spec: http://localhost:8086/v3/api-docs
|
|
|
|
---
|
|
|
|
## 7. 아키텍처 특징
|
|
|
|
### 7.1 캐싱 전략
|
|
- **패턴**: Cache-Aside (Lazy Loading)
|
|
- **저장소**: Redis Database 5
|
|
- **TTL**: 3600초 (1시간)
|
|
- **캐시 키 형식**: `analytics:dashboard:{eventId}`
|
|
- **직렬화**: JSON (ObjectMapper)
|
|
- **갱신 방법**: `refresh=true` 파라미터로 강제 갱신
|
|
|
|
### 7.2 외부 API 연동
|
|
- **패턴**: Circuit Breaker (Resilience4j)
|
|
- **병렬 처리**: CompletableFuture로 4개 채널 동시 호출
|
|
- **실패 처리**: Fallback 메서드로 기본값 반환
|
|
- **재시도**: Circuit Breaker 상태에 따라 자동 재시도
|
|
|
|
### 7.3 실시간 데이터 갱신
|
|
- **메시징**: Kafka Consumer
|
|
- **이벤트 소싱**: 3개 토픽 구독
|
|
- **처리 방식**:
|
|
1. EventCreated → 통계 초기화
|
|
2. ParticipantRegistered → 실시간 카운트 증가
|
|
3. DistributionCompleted → 비용 업데이트
|
|
|
|
### 7.4 성능 최적화
|
|
1. **데이터베이스 인덱스**:
|
|
- event_stats: event_id (UNIQUE)
|
|
- channel_stats: event_id, (event_id, channel_name)
|
|
- timeline_data: (event_id, timestamp)
|
|
|
|
2. **캐싱**:
|
|
- 대시보드 데이터 1시간 캐싱
|
|
- 외부 API 호출 최소화
|
|
|
|
3. **병렬 처리**:
|
|
- 4개 외부 채널 API 동시 호출
|
|
- CompletableFuture.allOf()로 대기 시간 단축
|
|
|
|
4. **커넥션 풀**:
|
|
- HikariCP (최대: 20, 최소: 5)
|
|
- 유휴 타임아웃: 10분
|
|
- 최대 수명: 30분
|
|
|
|
---
|
|
|
|
## 8. 보안
|
|
|
|
### 8.1 인증
|
|
- **방식**: JWT Bearer Token
|
|
- **공유**: common 모듈의 JwtAuthenticationFilter 사용
|
|
- **토큰 검증**: 모든 API 엔드포인트에 적용
|
|
- **예외**: Actuator 헬스 체크, Swagger UI
|
|
|
|
### 8.2 CORS
|
|
- **허용 Origin**: 환경 변수로 설정 (`CORS_ALLOWED_ORIGINS`)
|
|
- **기본값**: `http://localhost:*`
|
|
- **허용 메서드**: GET, POST, PUT, DELETE, OPTIONS
|
|
- **허용 헤더**: Authorization, Content-Type
|
|
|
|
---
|
|
|
|
## 9. 모니터링
|
|
|
|
### 9.1 Spring Boot Actuator
|
|
- **엔드포인트**: `/actuator`
|
|
- **노출 항목**: health, info, metrics, prometheus
|
|
- **헬스 체크**:
|
|
- Liveness: `/actuator/health/liveness`
|
|
- Readiness: `/actuator/health/readiness`
|
|
|
|
### 9.2 로깅
|
|
- **레벨**:
|
|
- 애플리케이션: DEBUG
|
|
- Spring Web: INFO
|
|
- Hibernate SQL: DEBUG
|
|
- Hibernate Type: TRACE
|
|
- **출력**:
|
|
- 콘솔: `%d{yyyy-MM-dd HH:mm:ss} - %msg%n`
|
|
- 파일: `logs/analytics-service.log`
|
|
|
|
---
|
|
|
|
## 10. 개발 표준 준수
|
|
|
|
### 10.1 패키지 구조
|
|
- Layered Architecture 패턴 적용
|
|
- Controller → Service → Repository → Entity 계층 분리
|
|
- DTO 별도 패키지로 관리
|
|
|
|
### 10.2 주석 표준
|
|
- 모든 클래스, 메서드에 한글 JavaDoc 주석
|
|
- 비즈니스 로직 핵심 부분 인라인 주석
|
|
|
|
### 10.3 코딩 컨벤션
|
|
- Lombok 활용 (Builder, Getter, Setter, NoArgsConstructor, AllArgsConstructor)
|
|
- JPA Auditing (@CreatedDate, @LastModifiedDate)
|
|
- 불변 객체 지향 (DTO는 @Builder로 생성)
|
|
|
|
---
|
|
|
|
## 11. 향후 개선 사항
|
|
|
|
### 11.1 기능 개선
|
|
1. **배치 작업**: 매일 자정 통계 집계 배치
|
|
2. **알림**: ROI 목표 달성 시 알림 발송
|
|
3. **예측 모델**: ML 기반 ROI 예측 정확도 향상
|
|
4. **A/B 테스트**: 채널별 전략 A/B 테스트 지원
|
|
|
|
### 11.2 성능 개선
|
|
1. **읽기 전용 DB**: 조회 성능 향상을 위한 Read Replica
|
|
2. **캐시 워밍**: 서비스 시작 시 자주 조회되는 데이터 사전 캐싱
|
|
3. **비동기 처리**: 무거운 집계 작업 비동기화
|
|
|
|
### 11.3 운영 개선
|
|
1. **메트릭 수집**: Prometheus + Grafana 대시보드
|
|
2. **분산 추적**: OpenTelemetry 적용
|
|
3. **로그 집중화**: ELK 스택 연동
|
|
|
|
---
|
|
|
|
## 12. 결론
|
|
|
|
Analytics 서비스는 이벤트 성과를 실시간으로 분석하고 ROI를 계산하는 핵심 서비스로, 다음과 같은 특징을 가집니다:
|
|
|
|
1. **실시간성**: Kafka를 통한 실시간 데이터 갱신
|
|
2. **성능**: Redis 캐싱 + 병렬 외부 API 호출로 응답 시간 최소화
|
|
3. **안정성**: Circuit Breaker 패턴으로 외부 API 장애 격리
|
|
4. **확장성**: Layered Architecture로 기능 확장 용이
|
|
5. **표준 준수**: 백엔드 개발 가이드 표준 완벽 적용
|
|
|
|
빌드와 컴파일이 모두 성공적으로 완료되어, 서비스 실행 준비가 완료되었습니다.
|