✨ 주요 기능 - 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>
15 KiB
Content Service 데이터베이스 설계서
데이터설계 요약
설계 개요
- 서비스: Content Service (이미지 생성 및 콘텐츠 관리)
- 아키텍처 패턴: Clean Architecture
- 데이터베이스: PostgreSQL (주 저장소), Redis (캐시 및 Job 상태 관리)
- 설계 원칙: 데이터독립성원칙 준수, Entity 클래스와 1:1 매핑
주요 엔티티
- Content: 이벤트별 콘텐츠 집합 정보
- GeneratedImage: AI 생성 이미지 메타데이터 및 CDN URL
- Job: 비동기 이미지 생성 작업 상태 추적
캐시 설계 (Redis)
- RedisJobData: Job 상태 추적 (TTL: 1시간)
- RedisImageData: 이미지 캐싱 (TTL: 7일)
- RedisAIEventData: AI 추천 데이터 캐싱 (TTL: 1시간)
주요 특징
- 이미지 메타데이터 최적화: CDN URL만 저장, 실제 이미지는 Azure Blob Storage
- 비동기 처리: Kafka 기반 Job 처리, Redis로 상태 관리
- 캐싱 전략: eventId 기반 캐시 키, TTL 설정으로 자동 만료
- 외부 연동: Stable Diffusion/DALL-E API, Azure Blob Storage CDN
1. PostgreSQL 데이터베이스 설계
1.1 테이블: content (콘텐츠 집합)
목적: 이벤트별 생성된 콘텐츠 집합 정보 관리
Entity 매핑: com.kt.event.content.biz.domain.Content
| 컬럼명 | 데이터 타입 | NULL | 기본값 | 설명 |
|---|---|---|---|---|
| id | BIGSERIAL | NOT NULL | AUTO | 콘텐츠 ID (PK) |
| event_id | VARCHAR(100) | NOT NULL | - | 이벤트 초안 ID |
| event_title | VARCHAR(200) | NOT NULL | - | 이벤트 제목 |
| event_description | TEXT | NULL | - | 이벤트 설명 |
| created_at | TIMESTAMP | NOT NULL | NOW() | 생성 시각 |
| updated_at | TIMESTAMP | NOT NULL | NOW() | 수정 시각 |
제약 조건:
- PRIMARY KEY: id
- UNIQUE INDEX: event_id (이벤트당 하나의 콘텐츠 집합)
- INDEX: created_at (시간 기반 조회)
비즈니스 규칙:
- 하나의 이벤트는 하나의 콘텐츠 집합만 보유
- 이미지는 별도 테이블에서 1:N 관계로 관리
1.2 테이블: generated_image (생성된 이미지)
목적: AI 생성 이미지 메타데이터 및 CDN URL 관리
Entity 매핑: com.kt.event.content.biz.domain.GeneratedImage
| 컬럼명 | 데이터 타입 | NULL | 기본값 | 설명 |
|---|---|---|---|---|
| id | BIGSERIAL | NOT NULL | AUTO | 이미지 ID (PK) |
| event_id | VARCHAR(100) | NOT NULL | - | 이벤트 초안 ID |
| style | VARCHAR(20) | NOT NULL | - | 이미지 스타일 (FANCY, SIMPLE, TRENDY) |
| platform | VARCHAR(30) | NOT NULL | - | 플랫폼 (INSTAGRAM, FACEBOOK, KAKAO, BLOG) |
| cdn_url | VARCHAR(500) | NOT NULL | - | CDN 이미지 URL (Azure Blob) |
| prompt | TEXT | NOT NULL | - | 생성에 사용된 프롬프트 |
| selected | BOOLEAN | NOT NULL | false | 사용자 선택 여부 |
| width | INT | NOT NULL | - | 이미지 너비 (픽셀) |
| height | INT | NOT NULL | - | 이미지 높이 (픽셀) |
| file_size | BIGINT | NULL | - | 파일 크기 (bytes) |
| content_type | VARCHAR(50) | NOT NULL | 'image/png' | MIME 타입 |
| created_at | TIMESTAMP | NOT NULL | NOW() | 생성 시각 |
| updated_at | TIMESTAMP | NOT NULL | NOW() | 수정 시각 |
제약 조건:
- PRIMARY KEY: id
- INDEX: (event_id, style, platform) - 필터링 조회 최적화
- INDEX: event_id - 이벤트별 이미지 조회
- INDEX: created_at - 시간 기반 조회
- CHECK: style IN ('FANCY', 'SIMPLE', 'TRENDY')
- CHECK: platform IN ('INSTAGRAM', 'FACEBOOK', 'KAKAO', 'BLOG')
- CHECK: width > 0 AND height > 0
비즈니스 규칙:
- 이미지 실체는 Azure Blob Storage에 저장, DB는 메타데이터만 보유
- 동일한 (event_id, style, platform) 조합으로 여러 이미지 생성 가능 (재생성)
- selected = true인 이미지가 최종 선택 이미지
플랫폼별 기본 해상도:
- INSTAGRAM: 1080x1080
- FACEBOOK: 1200x628
- KAKAO: 800x800
- BLOG: 800x600
1.3 테이블: job (비동기 작업 추적)
목적: 이미지 생성 비동기 작업 상태 추적
Entity 매핑: com.kt.event.content.biz.domain.Job
| 컬럼명 | 데이터 타입 | NULL | 기본값 | 설명 |
|---|---|---|---|---|
| id | VARCHAR(100) | NOT NULL | - | Job ID (PK) |
| event_id | VARCHAR(100) | NOT NULL | - | 이벤트 초안 ID |
| job_type | VARCHAR(50) | NOT NULL | - | 작업 타입 (IMAGE_GENERATION, IMAGE_REGENERATION) |
| status | VARCHAR(20) | NOT NULL | 'PENDING' | 작업 상태 (PENDING, PROCESSING, COMPLETED, FAILED) |
| progress | INT | NOT NULL | 0 | 진행률 (0-100) |
| result_message | TEXT | NULL | - | 완료 메시지 |
| error_message | TEXT | NULL | - | 에러 메시지 |
| created_at | TIMESTAMP | NOT NULL | NOW() | 생성 시각 |
| updated_at | TIMESTAMP | NOT NULL | NOW() | 수정 시각 |
| completed_at | TIMESTAMP | NULL | - | 완료 시각 |
제약 조건:
- PRIMARY KEY: id
- INDEX: event_id - 이벤트별 작업 조회
- INDEX: (status, created_at) - 상태별 작업 조회
- CHECK: status IN ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED')
- CHECK: job_type IN ('IMAGE_GENERATION', 'IMAGE_REGENERATION')
- CHECK: progress >= 0 AND progress <= 100
비즈니스 규칙:
- Job ID는 "job-img-{uuid}" 형식 (외부에서 생성)
- 상태 전이: PENDING → PROCESSING → COMPLETED/FAILED
- COMPLETED/FAILED 상태에서 completed_at 자동 설정
- Redis에도 동일한 Job 정보 저장 (TTL 1시간, 폴링 조회 최적화)
2. Redis 캐시 설계
2.1 RedisJobData (Job 상태 캐싱)
목적: 비동기 작업 상태 폴링 조회 성능 최적화
DTO 매핑: com.kt.event.content.biz.dto.RedisJobData
Redis 키 패턴: job:{jobId}
TTL: 1시간 (3600초)
데이터 구조 (Hash):
job:job-img-abc123 = {
"id": "job-img-abc123",
"eventId": "evt-draft-12345",
"jobType": "IMAGE_GENERATION",
"status": "PROCESSING",
"progress": 50,
"resultMessage": null,
"errorMessage": null,
"createdAt": "2025-10-29T10:00:00Z",
"updatedAt": "2025-10-29T10:00:05Z"
}
사용 시나리오:
- 이미지 생성 요청 시 Job 생성 → Redis 저장
- 클라이언트 폴링 조회 → Redis에서 빠르게 조회
- Job 완료 후 1시간 뒤 자동 삭제
- PostgreSQL의 job 테이블과 동기화 (영구 이력)
2.2 RedisImageData (이미지 캐싱)
목적: 동일 이벤트 재요청 시 즉시 반환
DTO 매핑: com.kt.event.content.biz.dto.RedisImageData
Redis 키 패턴: image:{eventId}:{style}:{platform}
TTL: 7일 (604800초)
데이터 구조 (Hash):
image:evt-draft-12345:SIMPLE:INSTAGRAM = {
"eventId": "evt-draft-12345",
"style": "SIMPLE",
"platform": "INSTAGRAM",
"imageUrl": "https://cdn.kt-event.com/images/evt-draft-12345-simple.png",
"prompt": "Clean and simple event poster with coffee theme",
"createdAt": "2025-10-29T10:00:10Z"
}
사용 시나리오:
- 이미지 생성 완료 → Redis 저장
- 동일 이벤트 재요청 → Redis 캐시 확인 → 즉시 반환
- 7일 후 자동 삭제 (오래된 캐시 정리)
2.3 RedisAIEventData (AI 추천 데이터 캐싱)
목적: AI Service 이벤트 데이터 캐싱
DTO 매핑: com.kt.event.content.biz.dto.RedisAIEventData
Redis 키 패턴: ai:event:{eventId}
TTL: 1시간 (3600초)
데이터 구조 (Hash):
ai:event:evt-draft-12345 = {
"eventId": "evt-draft-12345",
"recommendedStyles": ["SIMPLE", "TRENDY"],
"recommendedKeywords": ["coffee", "spring", "discount"],
"cachedAt": "2025-10-29T10:00:00Z"
}
사용 시나리오:
- AI Service에서 이벤트 분석 완료 → Redis 저장
- Content Service에서 이미지 생성 시 AI 추천 데이터 참조
- 1시간 후 자동 삭제
3. 인덱스 전략
3.1 성능 최적화 인덱스
generated_image 테이블:
-- 이벤트별 이미지 조회 (가장 빈번)
CREATE INDEX idx_generated_image_event_id ON generated_image(event_id);
-- 필터링 조회 (스타일, 플랫폼)
CREATE INDEX idx_generated_image_filter ON generated_image(event_id, style, platform);
-- 선택된 이미지 조회
CREATE INDEX idx_generated_image_selected ON generated_image(event_id, selected) WHERE selected = true;
-- 시간 기반 조회 (최근 생성 이미지)
CREATE INDEX idx_generated_image_created ON generated_image(created_at DESC);
job 테이블:
-- 이벤트별 작업 조회
CREATE INDEX idx_job_event_id ON job(event_id);
-- 상태별 작업 조회 (모니터링)
CREATE INDEX idx_job_status ON job(status, created_at DESC);
content 테이블:
-- 이벤트 ID 기반 조회 (UNIQUE)
CREATE UNIQUE INDEX idx_content_event_id ON content(event_id);
4. 데이터 정합성 규칙
4.1 데이터 일관성 보장
PostgreSQL ↔ Redis 동기화:
- Write-Through: Job 생성 시 PostgreSQL + Redis 동시 저장
- Cache-Aside: 이미지 조회 시 Redis 먼저 확인 → 없으면 PostgreSQL
- TTL 기반 자동 만료: Redis 데이터는 TTL로 자동 정리
4.2 트랜잭션 범위
이미지 생성 트랜잭션:
BEGIN TRANSACTION
1. Job 상태 업데이트 (PROCESSING)
2. 외부 API 호출 (Stable Diffusion)
3. CDN 업로드 (Azure Blob)
4. generated_image INSERT
5. Job 상태 업데이트 (COMPLETED)
6. Redis 캐시 저장
COMMIT
실패 시 롤백:
- 외부 API 실패 → Job 상태 FAILED, error_message 저장
- CDN 업로드 실패 → 재시도 (3회), 최종 실패 시 FAILED
- Circuit Breaker OPEN → Fallback 템플릿 이미지 사용
5. 백업 및 보존 정책
5.1 백업 전략
PostgreSQL:
- Full Backup: 매일 오전 2시 (Cron Job)
- Incremental Backup: 6시간마다
- 보관 기간: 30일
Redis:
- RDB Snapshot: 1시간마다
- AOF (Append-Only File): 실시간 로깅
- 보관 기간: 7일
5.2 데이터 보존 정책
generated_image:
- 보존 기간: 90일
- 정리 방식: created_at 기준 90일 초과 데이터 자동 삭제 (Batch Job)
job:
- 보존 기간: 30일
- 정리 방식: created_at 기준 30일 초과 데이터 자동 삭제
content:
- 보존 기간: 영구 (이미지 삭제 시에만 CASCADE 삭제)
6. 확장성 고려사항
6.1 수평 확장
Read Replica:
- PostgreSQL Read Replica 구성 (조회 성능 향상)
- 쓰기: Master, 읽기: Replica
Sharding 전략 (미래 대비):
- Shard Key: event_id (이벤트 ID 기반 분산)
- 예상 임계점: 1억 건 이미지 이상
6.2 캐시 전략
Redis Cluster:
- 3 Master + 3 Replica 구성
- 데이터 파티셔닝: event_id 기반 Hash Slot
Cache Warming:
- 자주 조회되는 이미지는 Redis에 영구 보관 (별도 TTL 없음)
7. 모니터링 지표
7.1 성능 지표
PostgreSQL:
- QPS (Queries Per Second): 이미지 조회 빈도
- Slow Query: 100ms 이상 쿼리 모니터링
- Connection Pool: 사용률 70% 이하 유지
Redis:
- Cache Hit Ratio: 90% 이상 목표
- Memory Usage: 80% 이하 유지
- Eviction Rate: 최소화
7.2 비즈니스 지표
이미지 생성:
- 성공률: 95% 이상
- 평균 생성 시간: 10초 이내
- Circuit Breaker OPEN 빈도: 월 5회 이하
캐시 효율:
- 재사용률: 동일 이벤트 재요청 비율
- TTL 만료율: 7일 이내 재조회 비율
8. 데이터독립성 검증
8.1 서비스 경계
Content Service 소유 데이터:
- content, generated_image, job 테이블 완전 소유
- 다른 서비스는 API를 통해서만 접근
외부 의존성 최소화:
- Event Service 데이터: event_id만 참조 (FK 없음)
- User Service 데이터: 참조하지 않음
- AI Service 데이터: Redis 캐시로만 참조
8.2 크로스 서비스 조인 금지
허용되지 않는 패턴:
-- ❌ 금지: Event Service DB와 조인
SELECT * FROM event_service.event e
JOIN content_service.generated_image i ON e.id = i.event_id;
올바른 패턴:
// ✅ 허용: API 호출 또는 캐시 참조
String eventTitle = eventServiceClient.getEvent(eventId).getTitle();
9. 보안 고려사항
9.1 접근 제어
PostgreSQL:
- 계정: content_service_user (최소 권한)
- 권한: content, generated_image, job 테이블만 SELECT, INSERT, UPDATE, DELETE
- 스키마 변경: DBA 계정만 가능
Redis:
- 계정: content_service_redis (별도 패스워드)
- ACL: 특정 키 패턴만 접근 (
job:*,image:*,ai:event:*)
9.2 데이터 암호화
전송 암호화:
- PostgreSQL: SSL/TLS 연결 강제
- Redis: TLS 연결 강제
저장 암호화:
- PostgreSQL: AES-256 암호화 (pgcrypto 확장)
- CDN URL은 공개 데이터 (암호화 불필요)
10. 클래스 설계와의 매핑 검증
10.1 Entity 클래스 매핑
| Entity 클래스 | PostgreSQL 테이블 | 필드 매핑 일치 |
|---|---|---|
| Content | content | ✅ 완전 일치 |
| GeneratedImage | generated_image | ✅ 완전 일치 (width, height 추가) |
| Job | job | ✅ 완전 일치 |
10.2 DTO 매핑
| DTO 클래스 | Redis 키 패턴 | 필드 매핑 일치 |
|---|---|---|
| RedisJobData | job:{jobId} | ✅ 완전 일치 |
| RedisImageData | image:{eventId}:{style}:{platform} | ✅ 완전 일치 |
| RedisAIEventData | ai:event:{eventId} | ✅ 완전 일치 |
10.3 Enum 매핑
| Enum 클래스 | 데이터베이스 | 값 일치 |
|---|---|---|
| ImageStyle | VARCHAR CHECK | ✅ FANCY, SIMPLE, TRENDY |
| Platform | VARCHAR CHECK | ✅ INSTAGRAM, FACEBOOK, KAKAO, BLOG |
| JobStatus | VARCHAR CHECK | ✅ PENDING, PROCESSING, COMPLETED, FAILED |
11. 마이그레이션 전략
11.1 초기 배포
데이터베이스 생성:
CREATE DATABASE content_service_db;
CREATE SCHEMA content;
테이블 생성 순서:
- content (부모 테이블)
- generated_image (자식 테이블)
- job (독립 테이블)
11.2 버전 관리
도구: Flyway (Spring Boot 통합)
마이그레이션 파일 위치: src/main/resources/db/migration/
명명 규칙: V{version}__{description}.sql
예시:
- V1__create_content_tables.sql
- V2__add_image_size_columns.sql
- V3__create_job_status_index.sql
12. 테스트 데이터
12.1 샘플 데이터
Content:
INSERT INTO content (event_id, event_title, event_description)
VALUES ('evt-draft-12345', '봄맞이 커피 할인 이벤트', '신메뉴 아메리카노 1+1 이벤트');
GeneratedImage:
INSERT INTO generated_image (event_id, style, platform, cdn_url, prompt, width, height)
VALUES
('evt-draft-12345', 'SIMPLE', 'INSTAGRAM',
'https://cdn.kt-event.com/images/evt-draft-12345-simple.png',
'Clean and simple coffee event poster', 1080, 1080),
('evt-draft-12345', 'FANCY', 'INSTAGRAM',
'https://cdn.kt-event.com/images/evt-draft-12345-fancy.png',
'Vibrant and colorful coffee event poster', 1080, 1080);
Job:
INSERT INTO job (id, event_id, job_type, status, progress)
VALUES ('job-img-abc123', 'evt-draft-12345', 'IMAGE_GENERATION', 'COMPLETED', 100);
13. 참조 문서
- 클래스 설계서: design/backend/class/content-service.puml
- API 명세서: design/backend/api/content-service-api.yaml
- 통합 검증: design/backend/class/integration-verification.md
- 데이터설계 가이드: claude/data-design.md
작성자: Backend Developer (아키텍트) 작성일: 2025-10-29 버전: v1.0