mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2025-12-06 17:26:23 +00:00
✨ 주요 기능 - Azure 기반 물리아키텍처 설계 (개발환경/운영환경) - 7개 마이크로서비스 물리 구조 설계 - 네트워크 아키텍처 다이어그램 작성 (Mermaid) - 환경별 비교 분석 및 마스터 인덱스 문서 📁 생성 파일 - design/backend/physical/physical-architecture.md (마스터) - design/backend/physical/physical-architecture-dev.md (개발환경) - design/backend/physical/physical-architecture-prod.md (운영환경) - design/backend/physical/*.mmd (4개 Mermaid 다이어그램) 🎯 핵심 성과 - 비용 최적화: 개발환경 월 $143, 운영환경 월 $2,860 - 확장성: 개발환경 100명 → 운영환경 10,000명 (100배) - 가용성: 개발환경 95% → 운영환경 99.9% - 보안: 다층 보안 아키텍처 (L1~L4) 🛠️ 기술 스택 - Azure Kubernetes Service (AKS) - Azure Database for PostgreSQL Flexible - Azure Cache for Redis Premium - Azure Service Bus Premium - Application Gateway + WAF 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
559 lines
17 KiB
Markdown
559 lines
17 KiB
Markdown
# Event Service 데이터베이스 설계서
|
|
|
|
## 📋 데이터설계 요약
|
|
|
|
### 개요
|
|
- **서비스명**: Event Service
|
|
- **데이터베이스**: PostgreSQL 15.x
|
|
- **캐시 시스템**: Redis 7.x
|
|
- **아키텍처 패턴**: Clean Architecture
|
|
- **설계 일자**: 2025-10-29
|
|
|
|
### 데이터베이스 역할
|
|
- **핵심 도메인**: 이벤트 생명주기 관리 (DRAFT → PUBLISHED → ENDED)
|
|
- **상태 머신**: EventStatus enum 기반 상태 전환
|
|
- **비동기 작업**: Job 엔티티로 장시간 작업 추적
|
|
- **AI 추천**: AiRecommendation 엔티티로 AI 생성 결과 저장
|
|
- **이미지 관리**: GeneratedImage 엔티티로 생성 이미지 저장
|
|
|
|
### 테이블 구성
|
|
| 테이블명 | 설명 | 주요 컬럼 | 비고 |
|
|
|---------|------|----------|------|
|
|
| events | 이벤트 기본 정보 | event_id, user_id, store_id, status | 핵심 도메인 |
|
|
| ai_recommendations | AI 추천 결과 | recommendation_id, event_id | Event 1:N |
|
|
| generated_images | 생성 이미지 정보 | image_id, event_id | Event 1:N |
|
|
| jobs | 비동기 작업 추적 | job_id, event_id, job_type, status | 작업 모니터링 |
|
|
|
|
### Redis 캐시 설계
|
|
| 키 패턴 | 설명 | TTL | 비고 |
|
|
|---------|------|-----|------|
|
|
| `event:session:{userId}` | 이벤트 생성 세션 정보 | 3600s | 임시 데이터 |
|
|
| `event:draft:{eventId}` | DRAFT 상태 이벤트 캐시 | 1800s | 빈번한 수정 |
|
|
| `job:status:{jobId}` | 작업 상태 실시간 조회 | 600s | 진행률 캐싱 |
|
|
|
|
---
|
|
|
|
## 1. PostgreSQL 테이블 설계
|
|
|
|
### 1.1 events (이벤트 기본 정보)
|
|
|
|
**설명**: 이벤트 핵심 도메인 엔티티. 상태 머신 패턴으로 생명주기 관리.
|
|
|
|
**컬럼 정의**:
|
|
|
|
| 컬럼명 | 데이터 타입 | 제약조건 | 설명 |
|
|
|--------|------------|---------|------|
|
|
| event_id | UUID | PK | 이벤트 고유 ID |
|
|
| user_id | UUID | NOT NULL, INDEX | 사용자 ID (소상공인) |
|
|
| store_id | UUID | NOT NULL, INDEX | 매장 ID |
|
|
| event_name | VARCHAR(200) | NULL | 이벤트 명칭 |
|
|
| description | TEXT | NULL | 이벤트 설명 |
|
|
| objective | VARCHAR(100) | NOT NULL | 이벤트 목적 |
|
|
| start_date | DATE | NULL | 이벤트 시작일 |
|
|
| end_date | DATE | NULL | 이벤트 종료일 |
|
|
| status | VARCHAR(20) | NOT NULL, DEFAULT 'DRAFT' | 이벤트 상태 (DRAFT, PUBLISHED, ENDED) |
|
|
| selected_image_id | UUID | NULL | 선택된 이미지 ID |
|
|
| selected_image_url | VARCHAR(500) | NULL | 선택된 이미지 URL |
|
|
| channels | TEXT | NULL | 배포 채널 목록 (JSON Array) |
|
|
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | 생성 일시 |
|
|
| updated_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | 수정 일시 |
|
|
|
|
**인덱스**:
|
|
- `PK_events`: event_id (Primary Key)
|
|
- `IDX_events_user_id`: user_id (사용자별 이벤트 조회 최적화)
|
|
- `IDX_events_store_id`: store_id (매장별 이벤트 조회)
|
|
- `IDX_events_status`: status (상태별 필터링)
|
|
- `IDX_events_user_status`: (user_id, status) (복합 인덱스 - 사용자별 상태 조회)
|
|
|
|
**비즈니스 규칙**:
|
|
- DRAFT 상태에서만 수정 가능
|
|
- PUBLISHED 상태에서 수정 불가, END만 가능
|
|
- ENDED 상태는 최종 상태 (수정/삭제 불가)
|
|
- selected_image_id는 generated_images 테이블 참조
|
|
- channels는 JSON 배열 형태로 저장 (예: ["SMS", "EMAIL"])
|
|
|
|
**데이터 예시**:
|
|
```json
|
|
{
|
|
"event_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"user_id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"store_id": "789e0123-e45b-67c8-d901-234567890abc",
|
|
"event_name": "여름 시즌 특별 할인",
|
|
"description": "7월 한 달간 전 품목 20% 할인",
|
|
"objective": "고객 유치",
|
|
"start_date": "2025-07-01",
|
|
"end_date": "2025-07-31",
|
|
"status": "PUBLISHED",
|
|
"selected_image_id": "abc12345-e67d-89ef-0123-456789abcdef",
|
|
"selected_image_url": "https://cdn.example.com/images/abc12345.jpg",
|
|
"channels": "[\"SMS\", \"EMAIL\", \"KAKAO\"]",
|
|
"created_at": "2025-06-15T10:00:00",
|
|
"updated_at": "2025-06-20T14:30:00"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 1.2 ai_recommendations (AI 추천 결과)
|
|
|
|
**설명**: AI 서비스로부터 받은 이벤트 추천 결과 저장.
|
|
|
|
**컬럼 정의**:
|
|
|
|
| 컬럼명 | 데이터 타입 | 제약조건 | 설명 |
|
|
|--------|------------|---------|------|
|
|
| recommendation_id | UUID | PK | 추천 고유 ID |
|
|
| event_id | UUID | NOT NULL, FK(events) | 이벤트 ID |
|
|
| event_name | VARCHAR(200) | NOT NULL | AI 추천 이벤트명 |
|
|
| description | TEXT | NOT NULL | AI 추천 설명 |
|
|
| promotion_type | VARCHAR(50) | NOT NULL | 프로모션 유형 |
|
|
| target_audience | VARCHAR(100) | NOT NULL | 타겟 고객층 |
|
|
| is_selected | BOOLEAN | NOT NULL, DEFAULT FALSE | 선택 여부 |
|
|
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | 생성 일시 |
|
|
| updated_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | 수정 일시 |
|
|
|
|
**인덱스**:
|
|
- `PK_ai_recommendations`: recommendation_id (Primary Key)
|
|
- `FK_recommendations_event`: event_id (Foreign Key)
|
|
- `IDX_recommendations_event_id`: event_id (이벤트별 추천 조회)
|
|
- `IDX_recommendations_selected`: (event_id, is_selected) (선택된 추천 조회)
|
|
|
|
**비즈니스 규칙**:
|
|
- 하나의 이벤트당 최대 3개의 AI 추천 생성
|
|
- is_selected=true는 이벤트당 최대 1개만 가능
|
|
- 선택 시 해당 이벤트의 다른 추천들은 is_selected=false 처리
|
|
|
|
**데이터 예시**:
|
|
```json
|
|
{
|
|
"recommendation_id": "111e2222-e33b-44d4-a555-666677778888",
|
|
"event_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"event_name": "여름 시즌 특별 할인",
|
|
"description": "7월 한 달간 전 품목 20% 할인 이벤트",
|
|
"promotion_type": "DISCOUNT",
|
|
"target_audience": "기존 고객",
|
|
"is_selected": true,
|
|
"created_at": "2025-06-15T10:05:00",
|
|
"updated_at": "2025-06-15T10:10:00"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 1.3 generated_images (생성 이미지 정보)
|
|
|
|
**설명**: Content Service로부터 생성된 이미지 정보 저장.
|
|
|
|
**컬럼 정의**:
|
|
|
|
| 컬럼명 | 데이터 타입 | 제약조건 | 설명 |
|
|
|--------|------------|---------|------|
|
|
| image_id | UUID | PK | 이미지 고유 ID |
|
|
| event_id | UUID | NOT NULL, FK(events) | 이벤트 ID |
|
|
| image_url | VARCHAR(500) | NOT NULL | 이미지 URL (CDN) |
|
|
| style | VARCHAR(50) | NOT NULL | 이미지 스타일 (MODERN, VINTAGE 등) |
|
|
| platform | VARCHAR(50) | NOT NULL | 플랫폼 (INSTAGRAM, FACEBOOK 등) |
|
|
| is_selected | BOOLEAN | NOT NULL, DEFAULT FALSE | 선택 여부 |
|
|
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | 생성 일시 |
|
|
| updated_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | 수정 일시 |
|
|
|
|
**인덱스**:
|
|
- `PK_generated_images`: image_id (Primary Key)
|
|
- `FK_images_event`: event_id (Foreign Key)
|
|
- `IDX_images_event_id`: event_id (이벤트별 이미지 조회)
|
|
- `IDX_images_selected`: (event_id, is_selected) (선택된 이미지 조회)
|
|
|
|
**비즈니스 규칙**:
|
|
- 하나의 이벤트당 여러 스타일/플랫폼 조합 이미지 생성 가능
|
|
- is_selected=true는 이벤트당 최대 1개만 가능
|
|
- 선택 시 해당 이벤트의 다른 이미지들은 is_selected=false 처리
|
|
- 선택된 이미지의 image_id와 image_url은 events 테이블에도 저장
|
|
|
|
**데이터 예시**:
|
|
```json
|
|
{
|
|
"image_id": "abc12345-e67d-89ef-0123-456789abcdef",
|
|
"event_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"image_url": "https://cdn.example.com/images/abc12345.jpg",
|
|
"style": "MODERN",
|
|
"platform": "INSTAGRAM",
|
|
"is_selected": true,
|
|
"created_at": "2025-06-15T11:00:00",
|
|
"updated_at": "2025-06-15T11:05:00"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 1.4 jobs (비동기 작업 추적)
|
|
|
|
**설명**: AI 추천 생성, 이미지 생성 등 장시간 작업 추적.
|
|
|
|
**컬럼 정의**:
|
|
|
|
| 컬럼명 | 데이터 타입 | 제약조건 | 설명 |
|
|
|--------|------------|---------|------|
|
|
| job_id | UUID | PK | 작업 고유 ID |
|
|
| event_id | UUID | NOT NULL | 이벤트 ID |
|
|
| job_type | VARCHAR(50) | NOT NULL | 작업 유형 (AI_RECOMMENDATION, IMAGE_GENERATION) |
|
|
| status | VARCHAR(20) | NOT NULL, DEFAULT 'PENDING' | 작업 상태 (PENDING, PROCESSING, COMPLETED, FAILED) |
|
|
| progress | INT | NOT NULL, DEFAULT 0 | 진행률 (0-100) |
|
|
| result_key | VARCHAR(200) | NULL | 결과 저장 키 (Redis 또는 S3) |
|
|
| error_message | TEXT | NULL | 오류 메시지 |
|
|
| completed_at | TIMESTAMP | NULL | 완료 일시 |
|
|
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | 생성 일시 |
|
|
| updated_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | 수정 일시 |
|
|
|
|
**인덱스**:
|
|
- `PK_jobs`: job_id (Primary Key)
|
|
- `IDX_jobs_event_id`: event_id (이벤트별 작업 조회)
|
|
- `IDX_jobs_type_status`: (job_type, status) (작업 유형별 상태 조회)
|
|
- `IDX_jobs_status`: status (상태별 작업 모니터링)
|
|
|
|
**비즈니스 규칙**:
|
|
- PENDING → PROCESSING → COMPLETED/FAILED 순차 진행
|
|
- progress는 0에서 100 사이 값 (PROCESSING 상태에서만 업데이트)
|
|
- COMPLETED 시 completed_at 자동 설정
|
|
- FAILED 시 error_message 필수
|
|
|
|
**데이터 예시**:
|
|
```json
|
|
{
|
|
"job_id": "999e8888-e77b-66d6-a555-444433332222",
|
|
"event_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"job_type": "AI_RECOMMENDATION",
|
|
"status": "COMPLETED",
|
|
"progress": 100,
|
|
"result_key": "ai-recommendation:550e8400-e29b-41d4-a716-446655440000",
|
|
"error_message": null,
|
|
"completed_at": "2025-06-15T10:10:00",
|
|
"created_at": "2025-06-15T10:00:00",
|
|
"updated_at": "2025-06-15T10:10:00"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Redis 캐시 설계
|
|
|
|
### 2.1 이벤트 세션 정보
|
|
|
|
**키 패턴**: `event:session:{userId}`
|
|
|
|
**데이터 구조**: Hash
|
|
|
|
**필드**:
|
|
- `eventId`: UUID - 임시 이벤트 ID
|
|
- `objective`: String - 선택한 목적
|
|
- `storeId`: UUID - 매장 ID
|
|
- `createdAt`: Timestamp - 세션 생성 시각
|
|
|
|
**TTL**: 3600초 (1시간)
|
|
|
|
**사용 목적**:
|
|
- 이벤트 생성 프로세스의 임시 데이터 저장
|
|
- 사용자가 이벤트 생성 중 페이지 이동 시 데이터 유지
|
|
- 1시간 후 자동 삭제로 메모리 최적화
|
|
|
|
**예시**:
|
|
```
|
|
HSET event:session:123e4567-e89b-12d3-a456-426614174000
|
|
eventId "550e8400-e29b-41d4-a716-446655440000"
|
|
objective "고객 유치"
|
|
storeId "789e0123-e45b-67c8-d901-234567890abc"
|
|
createdAt "2025-06-15T10:00:00"
|
|
EXPIRE event:session:123e4567-e89b-12d3-a456-426614174000 3600
|
|
```
|
|
|
|
---
|
|
|
|
### 2.2 DRAFT 이벤트 캐시
|
|
|
|
**키 패턴**: `event:draft:{eventId}`
|
|
|
|
**데이터 구조**: Hash
|
|
|
|
**필드**:
|
|
- `eventName`: String - 이벤트명
|
|
- `description`: String - 설명
|
|
- `objective`: String - 목적
|
|
- `status`: String - 상태
|
|
- `userId`: UUID - 사용자 ID
|
|
- `storeId`: UUID - 매장 ID
|
|
|
|
**TTL**: 1800초 (30분)
|
|
|
|
**사용 목적**:
|
|
- DRAFT 상태 이벤트의 빈번한 조회/수정 성능 최적화
|
|
- 사용자가 이벤트 편집 중 빠른 응답 제공
|
|
- DB 부하 감소
|
|
|
|
**예시**:
|
|
```
|
|
HSET event:draft:550e8400-e29b-41d4-a716-446655440000
|
|
eventName "여름 시즌 특별 할인"
|
|
description "7월 한 달간 전 품목 20% 할인"
|
|
objective "고객 유치"
|
|
status "DRAFT"
|
|
userId "123e4567-e89b-12d3-a456-426614174000"
|
|
storeId "789e0123-e45b-67c8-d901-234567890abc"
|
|
EXPIRE event:draft:550e8400-e29b-41d4-a716-446655440000 1800
|
|
```
|
|
|
|
---
|
|
|
|
### 2.3 작업 상태 캐시
|
|
|
|
**키 패턴**: `job:status:{jobId}`
|
|
|
|
**데이터 구조**: Hash
|
|
|
|
**필드**:
|
|
- `jobType`: String - 작업 유형
|
|
- `status`: String - 작업 상태
|
|
- `progress`: Integer - 진행률 (0-100)
|
|
- `eventId`: UUID - 이벤트 ID
|
|
|
|
**TTL**: 600초 (10분)
|
|
|
|
**사용 목적**:
|
|
- 비동기 작업 진행 상태 실시간 조회
|
|
- 폴링 방식의 진행률 체크 시 DB 부하 방지
|
|
- AI 추천/이미지 생성 작업의 빠른 상태 확인
|
|
|
|
**예시**:
|
|
```
|
|
HSET job:status:999e8888-e77b-66d6-a555-444433332222
|
|
jobType "AI_RECOMMENDATION"
|
|
status "PROCESSING"
|
|
progress "45"
|
|
eventId "550e8400-e29b-41d4-a716-446655440000"
|
|
EXPIRE job:status:999e8888-e77b-66d6-a555-444433332222 600
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 데이터베이스 제약조건
|
|
|
|
### 3.1 외래 키 (Foreign Key)
|
|
|
|
```sql
|
|
-- ai_recommendations 테이블
|
|
ALTER TABLE ai_recommendations
|
|
ADD CONSTRAINT FK_recommendations_event
|
|
FOREIGN KEY (event_id) REFERENCES events(event_id)
|
|
ON DELETE CASCADE;
|
|
|
|
-- generated_images 테이블
|
|
ALTER TABLE generated_images
|
|
ADD CONSTRAINT FK_images_event
|
|
FOREIGN KEY (event_id) REFERENCES events(event_id)
|
|
ON DELETE CASCADE;
|
|
```
|
|
|
|
**설명**:
|
|
- `ON DELETE CASCADE`: 이벤트 삭제 시 관련 추천/이미지 자동 삭제
|
|
- jobs 테이블은 FK 제약조건 없음 (이벤트 삭제 후에도 작업 이력 보존)
|
|
|
|
---
|
|
|
|
### 3.2 체크 제약조건 (Check Constraints)
|
|
|
|
```sql
|
|
-- events 테이블
|
|
ALTER TABLE events
|
|
ADD CONSTRAINT CHK_events_status
|
|
CHECK (status IN ('DRAFT', 'PUBLISHED', 'ENDED'));
|
|
|
|
ALTER TABLE events
|
|
ADD CONSTRAINT CHK_events_dates
|
|
CHECK (start_date IS NULL OR end_date IS NULL OR start_date <= end_date);
|
|
|
|
-- jobs 테이블
|
|
ALTER TABLE jobs
|
|
ADD CONSTRAINT CHK_jobs_status
|
|
CHECK (status IN ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED'));
|
|
|
|
ALTER TABLE jobs
|
|
ADD CONSTRAINT CHK_jobs_type
|
|
CHECK (job_type IN ('AI_RECOMMENDATION', 'IMAGE_GENERATION'));
|
|
|
|
ALTER TABLE jobs
|
|
ADD CONSTRAINT CHK_jobs_progress
|
|
CHECK (progress >= 0 AND progress <= 100);
|
|
```
|
|
|
|
---
|
|
|
|
### 3.3 유니크 제약조건 (Unique Constraints)
|
|
|
|
```sql
|
|
-- 이벤트당 하나의 선택된 추천만 허용 (애플리케이션 레벨에서 관리)
|
|
-- 이벤트당 하나의 선택된 이미지만 허용 (애플리케이션 레벨에서 관리)
|
|
```
|
|
|
|
**설명**:
|
|
- is_selected=true 조건의 UNIQUE 제약은 DB 레벨에서 구현 어려움
|
|
- 애플리케이션 레벨에서 트랜잭션으로 보장
|
|
|
|
---
|
|
|
|
## 4. 성능 최적화 전략
|
|
|
|
### 4.1 인덱스 전략
|
|
|
|
**단일 컬럼 인덱스**:
|
|
- `events.user_id`: 사용자별 이벤트 조회 (가장 빈번한 쿼리)
|
|
- `events.status`: 상태별 필터링
|
|
- `jobs.status`: 작업 모니터링
|
|
|
|
**복합 인덱스**:
|
|
- `(user_id, status)`: 사용자별 상태 필터 조회 (API: GET /events?status=DRAFT)
|
|
- `(job_type, status)`: 작업 유형별 상태 조회 (배치 처리)
|
|
- `(event_id, is_selected)`: 선택된 추천/이미지 조회
|
|
|
|
---
|
|
|
|
### 4.2 파티셔닝 전략
|
|
|
|
**events 테이블 파티셔닝 (향후 고려)**:
|
|
- **파티션 키**: created_at (월별)
|
|
- **적용 시점**: 이벤트 데이터 100만 건 이상
|
|
- **이점**: 과거 데이터 조회 성능 향상, 백업/삭제 효율화
|
|
|
|
```sql
|
|
-- 예시 (PostgreSQL 12+)
|
|
CREATE TABLE events (
|
|
...
|
|
) PARTITION BY RANGE (created_at);
|
|
|
|
CREATE TABLE events_2025_06 PARTITION OF events
|
|
FOR VALUES FROM ('2025-06-01') TO ('2025-07-01');
|
|
```
|
|
|
|
---
|
|
|
|
### 4.3 캐시 전략
|
|
|
|
**캐시 우선 조회**:
|
|
1. Redis에서 캐시 조회
|
|
2. 캐시 미스 시 DB 조회 후 캐시 저장
|
|
3. TTL 만료 시 자동 삭제
|
|
|
|
**캐시 무효화**:
|
|
- 이벤트 수정 시: `event:draft:{eventId}` 삭제
|
|
- 작업 완료 시: `job:status:{jobId}` 삭제
|
|
- 이벤트 발행 시: `event:draft:{eventId}` 삭제
|
|
|
|
---
|
|
|
|
## 5. 데이터 일관성 보장
|
|
|
|
### 5.1 트랜잭션 전략
|
|
|
|
**이벤트 생성**:
|
|
```sql
|
|
BEGIN;
|
|
INSERT INTO events (...) VALUES (...);
|
|
INSERT INTO jobs (event_id, job_type, status) VALUES (?, 'AI_RECOMMENDATION', 'PENDING');
|
|
COMMIT;
|
|
```
|
|
|
|
**추천 선택**:
|
|
```sql
|
|
BEGIN;
|
|
UPDATE ai_recommendations SET is_selected = FALSE WHERE event_id = ?;
|
|
UPDATE ai_recommendations SET is_selected = TRUE WHERE recommendation_id = ?;
|
|
UPDATE events SET event_name = ?, description = ?, start_date = ?, end_date = ? WHERE event_id = ?;
|
|
COMMIT;
|
|
```
|
|
|
|
---
|
|
|
|
### 5.2 낙관적 락 (Optimistic Locking)
|
|
|
|
**updated_at 기반 버전 관리**:
|
|
```java
|
|
@Version
|
|
private LocalDateTime updatedAt;
|
|
```
|
|
|
|
**충돌 감지**:
|
|
```sql
|
|
UPDATE events
|
|
SET event_name = ?, updated_at = CURRENT_TIMESTAMP
|
|
WHERE event_id = ? AND updated_at = ?;
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 백업 및 복구 전략
|
|
|
|
### 6.1 백업 주기
|
|
|
|
- **전체 백업**: 매일 02:00 (pg_dump)
|
|
- **증분 백업**: 6시간마다 (WAL 아카이빙)
|
|
- **보관 기간**: 30일
|
|
|
|
### 6.2 복구 시나리오
|
|
|
|
**시나리오 1: 데이터 손실 (최근 1시간)**
|
|
- WAL 로그 기반 Point-in-Time Recovery (PITR)
|
|
- 복구 시간: 약 15분
|
|
|
|
**시나리오 2: 전체 데이터베이스 복구**
|
|
- 최근 전체 백업 복원 + WAL 로그 적용
|
|
- 복구 시간: 약 30분
|
|
|
|
---
|
|
|
|
## 7. 모니터링 지표
|
|
|
|
### 7.1 성능 모니터링
|
|
|
|
| 지표 | 임계값 | 알림 |
|
|
|------|--------|------|
|
|
| 평균 쿼리 응답 시간 | > 200ms | Warning |
|
|
| DB Connection Pool 사용률 | > 80% | Critical |
|
|
| Redis Cache Hit Rate | < 70% | Warning |
|
|
| 느린 쿼리 (Slow Query) | > 1초 | Critical |
|
|
|
|
### 7.2 데이터 모니터링
|
|
|
|
| 지표 | 확인 주기 | 비고 |
|
|
|------|----------|------|
|
|
| events 테이블 레코드 수 | 일일 | 증가 추이 분석 |
|
|
| DRAFT 상태 30일 이상 | 주간 | 정리 대상 파악 |
|
|
| FAILED 작업 누적 | 일일 | 재처리 필요 |
|
|
| Redis 메모리 사용률 | 실시간 | > 80% 경고 |
|
|
|
|
---
|
|
|
|
## 8. 데이터 보안
|
|
|
|
### 8.1 암호화
|
|
|
|
- **전송 중 암호화**: SSL/TLS (PostgreSQL + Redis)
|
|
- **저장 암호화**: Transparent Data Encryption (TDE) 고려
|
|
- **민감 정보**: 없음 (이미지 URL만 저장)
|
|
|
|
### 8.2 접근 제어
|
|
|
|
- **DB 사용자**: event_service_user (최소 권한 원칙)
|
|
- **권한**: events, ai_recommendations, generated_images, jobs 테이블에 대한 CRUD
|
|
- **Redis**: Password 인증 + 네트워크 격리
|
|
|
|
---
|
|
|
|
## 9. ERD 및 스키마 파일
|
|
|
|
- **ERD**: `event-service-erd.puml` (PlantUML)
|
|
- **DDL 스크립트**: `event-service-schema.psql` (PostgreSQL)
|
|
|
|
---
|
|
|
|
**작성자**: Backend Architect (최수연 "아키텍처")
|
|
**작성일**: 2025-10-29
|
|
**검토자**: Backend Developer, DevOps Engineer
|
|
**승인일**: 2025-10-29
|